@ポケじゃらし

音楽ゲームとかボーカロイドとか

【ExcelVBA】N-gramを実装してみた【言語処理】

どうも、じゃらしです。

今回はExcelVBAのネタです。

ExcelVBAで「言語処理100本ノック」に挑戦してみようと思い、N-gramというテキスト分割方法について実装してみました。
※「言語処理100本ノック」について気になる人はググってみて下さいませ

①文字単位のN-gramを行うクラスとして「CharNgram.cls」
②単語単位のN-gramを行うクラスとして「WordNgram.cls」
を作成します。

連想配列としてDictionary型を使用するので、事前に参照設定から
Microsoft Scripting Runtime」にチェックを付けておきましょう。

早速本題です。

'CharNgram.cls

Private N_ As Long

Public Property Let N(ByVal hoge As Long)
    N_ = hoge
End Property

Public Property Get N() As Long
    N = N_
End Property

Public Function ToNgram(ByRef hoge As String) As Dictionary
    Dim letters As String
    letters = Replace(hoge, " ", "")
    
    Dim length As Long
    length = Len(letters)
    
    Dim dic As New Dictionary
    Dim char As String
    
    Dim i As Long
    For i = 1 To length - N + 1
        char = Mid(letters, i, N)
        If Not dic.Exists(char) Then
            dic.Add char, char
        End If
    Next i
    
    Set ToNgram = dic
End Function

↑は、文字単位にN-gramを行う「CharNgram.cls」です。
使い方ですが、あらかじめNに数値を設定しておきます。
そして、N-gramしたい文章を引数としてToNgramを実行すると、N文字で分割された連想配列を返します。

'WordNgram.cls

Private N_ As Long

Public Property Let N(ByVal hoge As Long)
    N_ = hoge
End Property

Public Property Get N() As Long
    N = N_
End Property

Public Function ToNgram(ByRef hoge As String) As Dictionary
    Dim arrHoge() As String
    arrHoge = Split(hoge)
    
    Dim dic As New Dictionary
    Dim word As String
    
    Dim i As Long
    For i = LBound(arrHoge) To UBound(arrHoge) - N + 1
        word = ConcatWords(arrHoge, i)
        If Not dic.Exists(word) Then
            dic.Add word, word
        End If
    Next i
    
    Set ToNgram = dic
End Function

Private Function ConcatWords(ByRef arrHoge As Variant, ByVal index As Long) As String
    Dim arr() As String
    Dim i As Long
    For i = 1 To N
        ReDim Preserve arr(1 To i) As String
        arr(i) = arrHoge(index + i - 1)
    Next i
    ConcatWords = Join(arr)
End Function

↑は、単語単位にN-gramを行う「CharNgram.cls」です。
使い方は先ほどと同様、N-gramしたい文章を引数としてToNgramを実行すると、N単語で分割された連想配列を返します。

<実行例1>

'Module1.bas
Sub test()
    Dim sentence As String
    sentence = "I am an NLPer"
    With New CharNgram
        Dim i As Long
        For i = 1 To 3
            .N = i
            Debug.Print Join(.ToNgram(sentence).Items, ",")
        Next i
    End With
    
    With New WordNgram
        For i = 1 To 3
            .N = i
            Debug.Print Join(.ToNgram(sentence).Items, ",")
        Next i
    End With
End Sub

"I am an NLPer"という文章を文字・単語単位で分割して出力します。
Nが1~3の場合でそれぞれ分割しています。

f:id:twitpokej:20200415225933p:plain
Module1.test


<実行例2>

'Module2.bas
Sub test()
    Dim x As String, y As String
    x = "paraparaparadise"
    y = "paragraph"
    With New CharNgram
        .N = 2
        Debug.Print Join(.ToNgram(x).Items, ",")
        Debug.Print Join(.ToNgram(y).Items, ",")
    End With
End Sub

分割した要素は重複しないよ、という例です。

f:id:twitpokej:20200415230207p:plain
Module2.test

ExcelVBAでテキスト分析するかどうかはさておき、実装を考えるのは楽しかったです。(粉みかん)
以上、それではまた。