はじめに
Pythonを勉強するため、東京工業大学の岡崎教授が出題されている言語処理100本ノック 2015を解いていきます。
より深く理解するため、別解や利用したライブラリの解説もまとめていきます。
環境
Python3.6
OS : mac
問題
05. n-gram
与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.
n-gramとは
検索対象を単語単位ではなく文字単位で分解し、後続の N-1 文字を含めた状態で出現頻度を求める方法。 Nの値が1なら「ユニグラム(英: uni-gram)」、2なら「バイグラム(英: bi-gram)」、3なら「トライグラム(英: tri-gram)」と呼ばれる。
WiKiペディアより
wikiの記述は、問題文でいうところの、文字bi-gram
に当たります。
後続の1文字を含めるため、文字列"Hello World"とあったら、['He', 'el', 'll', 'lo', 'o ', ' W', 'Wo', 'or', 'rl', 'ld']
となります。
単語bi-gram
は、単語単位で分解し、後続のN-1単語を含めた状態です。Nは2となります。
"I am your father"を単語bi-gram
で分解すると、[['I', 'am'], ['am', 'your'], ['your', 'father']]
となります。
疑問なのが、句読点などの処理や先頭と末端の処理です。 こちらは、調査しておきます。
今回はn-gram
を以下のように定義して解答しました。
- 後続のN-1文字(単語)を含める
- 句読点は考慮しない
- 終端はN文字(単語)確保できるところまで。
また、bi-gramnだけでなくnに対応します。
解答:単語n-gram
sentence = "I am an NLPer"
def word_n_gram(sentence, N):
"""
単語のn-gramを返す。
"""
words = sentence.split()
result = []
for it, c in enumerate(words):
if it + N > len(words):
return result
result.append(words[it: it+N])
出力
# bi-gram
print(word_n_gram(sentence, N=2))
# => [['I', 'am'], ['am', 'an'], ['an', 'NLPer']]
# tri-gram
print(word_n_gram(sentence, N=3))
# => [['I', 'am', 'an'], ['am', 'an', 'NLPer']]
解答:文字n-gram
import re
sentence = "I am an NLPer"
def char_n_gram(sentence, N):
"""
文字のn-gramを返す。
"""
result = []
for it in range(len(sentence)):
if it + N > len(sentence):
return result
result.append(sentence[it: it+N])
出力
# bi-gram
print(char_n_gram(sentence, N=2))
# => ['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
# tri-gram
print(char_n_gram(sentence, N=3))
# => ['I a', ' am', 'am ', 'm a', ' an', 'an ', 'n N', ' NL', 'NLP', 'LPe', 'Per']
Ngram
Pythonライブラリにpython-ngramがあるそうです。
使い方はPythonでN-Gram
終わりに
いつも解いた後、@segavvyさんの素人の言語処理100本ノック:まとめを見て答えあわせや別解の確認をしています。
そこで気づいたのですが、char_n_gram()
に渡す形式を変えるだけで、単語n-gramと文字n-gram両方に対応していました。
sentence = "I am an NLPer"
# 文字n-gram
print(char_n_gram(sentence, N=3))
# => ['I a', ' am', 'am ', 'm a', ' an', 'an ', 'n N', ' NL', 'NLP', 'LPe', 'Per']
# 単語n-gram
print(char_n_gram(sentence.split(), N=3))
# => [['I', 'am', 'an'], ['am', 'an', 'NLPer']]
単語のリストを渡せば単語n-gramもいけるんですね。
@segavvyさんありがとうございます。
続いての記事
前の問題:04. 元素記号
次の問題:06. 集合