見出し画像

「入門 自然言語処理」 入門 (4) - 文

テキスト (text / teksto) を文 (sentence / frazo) に分割する関数 sents() が NLTK には用意されています。ただし、この関数はあまり精度がよくないようです。

>>> feino_sents = tekstaro.sents('LaFeino.txt')
>>> type(feino_sents)
<class 'nltk.corpus.reader.util.StreamBackedCorpusView'>
>>> len(feino_sents)
36
>>> feino_sents
[['La', 'feino', '.'], ['Unu', 'vidvino', 'havis', 'du', 'filinojn', '.'], ...]
>>> feino_sents[0:]
[['La', 'feino', '.'], ['Unu', 'vidvino', ...(中略)... , 'en', 'angulo', 'de', 'arbaro', '.']]

sents() 関数を適用すると、リストのリストが生成されます。テキスト全体が一つのリストとなっていて、その要素としての文が単語のリストとなっています。len() 関数を使うと、'LaFeino.txt' は36個の文から成るテキストであることが分かります。

文の分割(=どういう基準で文であると判断するか)は微妙な点があって、決定的なことは言えないようですが、ここではとりあえず次の2点で文と判断してみます。

1.前のトークン列が '.'、'!'、'?' で終わっている('"' などの記号類が続く場合を含む)
2.文字列が大文字で始まっている('-'、'"' などの記号類が先行する場合を含む)

その上で、結果として得られた feino_sents の中身を詳しく見ていくと、6か所で正しく分割されていなかったことが判明しました。上記の基準で判定した文の数は 42 でした。文の分析をおこなう際、単純に sents() 関数の結果を利用することはできないようです。『入門』第6章第2節では文分割の問題について詳しく触れているようですが、まだまだ先は長いです(涙)。

さて、それぞれの文の長さ(=含まれるトークン数)を求めることを考えてみます。本書 p.23 に単語の長さの分布を求める例が示されているので、それを応用します。

まず
>>> feino_sents_rev = [['La', 'feino', '.'], ['Unu', 'vidvino', 'havis', ...(中略)...  'en', 'angulo', 'de', 'arbaro', '.']]
>>> feino_slen = [len(s) for s in feino_sents_rev]
>>> feino_slen
[3, 6, 48, 34, 37, 11, 27, 28, 12, 37, 66, 53, 19, 22, 12, 14, 9, 15, 28, 17, 28, 26, 18, 26, 14, 59, 23, 18, 13, 14, 33, 17, 19, 15, 22, 13, 41, 15, 34, 8, 42, 46]
>>> len(feino_slen)
42
>>> sum(feino_slen)
1042
>>> max(feino_slen)
66
>>> min(feino_slen) 
3
>>> round(sum(feino_slen)/len(feino_slen))   
25

まず feino_sents_rev に手作業で修正した文のリストを代入します。各文の長さを求め、リスト化し、結果を feino_slen に保存します。
sum() 関数はリスト内の各文の長さの合計値(=テキスト内のトークン総数)を示します。max() 関数は文の長さの最大値(=最も長い文のトークン数)、min() 関数は最小値(=最も短い文のトークン数)を示します。テキスト内の文の平均長は sum()/len() で求めることができます。
ただし、このやり方では文の長さに句読点や記号類を含むので、「文の長さ=単語数」ではありません。句読点や記号類を含まずに、純粋に単語だけで文の長さを求めるには、以下のようにします。

>>> feino_sents2 = [[w for w in s if w.isalpha()] for s in feino_sents_rev]
>>> type(feino_sents2)
<class 'list'>
>>> len(feino_sents2)
42
>>> feino_sents2
[['La', 'feino'], ['Unu', 'vidvino', 'havis', ...(中略)... 'en', 'angulo', 'de', 'arbaro']] 
>>> len(feino_slen2)
42
>>> sum(feino_slen2)
851
>>> max(feino_slen2)
55
>>> min(feino_slen2)
2
>>> round(sum(feino_slen2)/len(feino_slen2))
20

リスト内の各トークンに isalpha() メソッドを適用することで、句読点などの記号類を除外することができます。
テキスト内の単語総数は 851個、最も長い文の単語数は 55個、最も短い文の単語数は 2個で、文の平均長は 20語という結果になりました。トークン数で求めた値よりもそれぞれ少なくなっています。




この記事が気に入ったらサポートをしてみませんか?