nltk 単語の出現回数頻度

今思えばこれを最初にすべきですよね。笑

単語の出現頻度を調べるのは最も基本的なことだと思います。
Pythonではdictを使えば簡単にできます。
他の言語でもMapだとかがあると思うのでそれ使えばできるでしょう。

nltkにもFreqDistというものがあります。
さらにそこから各単語の出現確率を最尤推定(MLE)できるMLEProbDistまで使い方をメモ。

1.text -> FreqDist

#fileから読む
text = nltk.filestring(open(filename))
word_tok = nltk.word_tokenize(text)
freqdist = nltk.FreqDist(word_tok)

これでFreqDistが作れます。品詞を使うかはお好みで。
2.FreqDist -> MLEProbDist
FreqDistから各単語の出現確率をMLEします。
MLEといっても各単語の出現回数を全単語数で割ればいいです。
(なぜこうなのかはまた後日
なので普通の辞書であれば

N = sum(dict.values())
for key in dict:
    dict[key] /= N

なんてしてやればOKです。
MLEProbDistを使うと

mleprobdist = nltk.MLEProbDist(freqdist)

これでOKです。freqdistを渡してやればあとはNormalizeしてくれます。

まとめ

簡単に出現頻度の辞書が作れる。
MLEProbDistだけでなくUniformProbDistやらいろいろなProbDistがあります。
おもしろいですねー。
さらに便利だと思ったのはnltk.ConditionalFreqDistですね。
これについてはまた書きたいと思います。書かないかもしれませんが。

nltkを使ってみた 単語の出現位置(続き

さて、前回
「文中でのクエリの最初の出現位置」
「文中でのクエリの最後の出現位置」
を探索しました(ムリヤリ
今回は
「文中でのクエリの出現位置」のリストを求めます。
さて、はじめます。使うテキストは前回と同じです。

文中でのクエリの出現位置

クエリが1単語の場合

query = "language"
#非正規化版
pos = [i for sent_tok in tokens for i, tok in enumerate(sent_tok) if query == tok]
#正規化版
pos = [float(i)/len(sent_tok) for sent_tok in tokens for i, tok in enumerate(sent_tok) if tok == query]

クエリがn単語の場合

#非正規化版
pos = [i for ngram_tok in [nltk.ngrams(sent_tok, n) for sent_tok in tokens] for i, tok in enumerate(ngram_tok) if tuple(query_tok) == tok]
#正規化版
pos = [float(i) / (len(ngram_tok)+n-1) for ngram_tok in [nltk.ngrams(sent_tok, n) for sent_tok in tokens] for i, tok in enumerate(ngram_tok) if tuple(query_tok) == tok]

まとめ

以外と簡単に実装できました。
僕の研究に役立つといいんですけどね…

課題

今回は

  • 文中でのクエリの最初の出現位置
  • 文中でのクエリの最後の出現位置
  • 文中でのクエリの出現位置のリスト

をやりました。考えられる問題は

  • 文書内のどのあたりでクエリが出現しているか
  • 文書内のどのあたりでクエリが出現する文が出現するか

くらいですかね。そのうち片付けよう。

nltkを使ってみた 単語の出現位置

単語の出現位置は何かと便利な表層的な量だと思います。

前回、前々回と、nltkを使ってraw_text -> tokensの変換をやりました。

例えばこんなテキストがあるとします。(from wikipedia of NLP)

mytext = "Natural language processing (NLP) is a field of computer science, artificial intelligence, and linguistics concerned with the interactions between computers and human (natural) languages. As such, NLP is related to the area of human–computer interaction. Many challenges in NLP involve natural language understanding -- that is, enabling computers to derive meaning from human or natural language input."

1.text -> sents

#ファイルから読む場合
sents = nltk.sent_tokenize(nltk.filestring(open(filename)))
#plane textの場合
sents = nltk.sent_tokenize(mytext)

2.sents -> tokens

tokens = [nltk.word_tokens(sent) for sent in sents]

ここまでの処理でtokensは[[],…,[]]という形をしています。

print "count of sentences:", len(tokens)
print "count of tokens for each sentence", [len(tks) for tks in tokens]

文数、それぞれの文のトークンの数を表示してみました。
では、本題に入ります。

ある文中でのクエリの最初の位置を調べる

クエリが出現した位置を調べます。
目的はクエリが文の中のどのあたりで出現しているか、を調べることです。
例えば「先頭で出てくる場合が多い」とか「最後に出てくる場合が多い」とかです。
ここで注意したいのは単語での位置ということです。
例えばクエリがlanguageだった時、今ほしいのは[1,…]というリストです。[7,…]ではありません。

クエリが1単語の場合

これは簡単に実装できます。

query = "language"
#非正規化版
pos = [sent_tok.index(query) for sent_tok in tokens if query in sent_tok]
#正規化版
[float(sent_tok.index(query))/len(sent_tok) for sent_tok in tokens if query in sent_tok]

目的のリストをゲットできます。正規化をしないと比べられないので文中の単語数で正規化をしておきます。

クエリがn単語の場合

少しやっかいです。計算量とわかりやすさどちらをとるかという話にもなりますが…
まずクエリが何単語なのかを調べましょう。クエリは”natural language”にします。

query = "natural language"
query_tok = nltk.word_tokenize(query)
n = len(query_tok)
print n

これでよし!です。ああここで(1)大文字小文字(2)ステミングなどをどうするかという話が出てきますがまたあとで。
では、探索です。わかりやすい方法で行きましょう。
token化された1文をngramにしましょう。このnは先程求めたnです。
そしてその中でのquery_tokの位置を探索しましょう。
nltk.ngramsを使います。しかしこいつはtupleのリストを返してくるのでquery_tokをtupleにして比較します。

[float(ngram_tok.index(tuple(query_tok))) / (len(ngram_tok)+n-1) for ngram_tok in [nltk.ngrams(sent_tok, n) for sent_tok in tokens] if tuple(query_tok) in ngram_tok]

リスト内包表記で書いて見ましたがおそらく意味不明なので、以下のコードと等価です。

query_tok = tuple(query_tok)
pos = []
for sent_tok in tokens:
    ngram_tok = nltk.ngrams(sent_tok):
    if query_tok in ngram_tok:
        pos.append(float(ngram_tok.index(query_tok)) / (len(ngram_tok)+n-1))

重大なことに気づく

書いたあとに思ったのですが、これ、全部とってこれてませんね。
リストのindexは最初にマッチしたindexしか返してくれないのでこれでは不十分です。
文中でのクエリの最初の出現位置 としておきます。(題も変更)
rindexにすれば 文中でのクエリの最後の出現位置 になりますね。

課題

これをiterationできるようにする。
itertoolsになにかないかな。

nltkを使ってみた ngramの作成

前回raw_text -> tokensをやったのですが、このtokensを使ってngramを作るモジュールってないのかなと思い探しました。
ちなみにipythonでそれっぽい名前を見つける→ドキュメントっていう超非効率な探索をしていますいつも。
nltk.ngramsを使えばいいみたいです。

n = 4 #4-gram
nltk.ngrams(tokens, n)

解決ですね。

まとめ

nltkは便利

疑問

queryがnトークンからなるとしたらn+2グラムを考えればqueryの前後、直前2個前、直後2個後を考慮に入れる?
queryの前後の品詞が知りたい時に使えるか?
query -> query_tokens
sent -> sent_tokens
n = len(query_tokens) + 2
sent -> ngram_sent_tokens
として
query_tokensの全ての要素がngram_sent_tokensの要素の要素の中にあればいいということであって、
1回の出現につき3個取れる…はず?
というかそういうモジュールもありそうだな。

nltkを使ってみた

こんにちは。

Pythonの自然言語処理モジュールnltkを使ってみた。

よくやる操作
txt -> sent -> tokens -> [token, postag]

1.txt -> sent

fp = open(filename)
raw_text = nltk.filestring(fp)
sentences = nltk.sent_tokenize(raw_text)

以下と等価

sentences = nltk.sent_tokenize(nltk.filestring(open(filename)))

2.sent -> tokens

tokens = [nltk.word_tokenize(sent) for sent in sentences]

3.tokens -> (token, pos)

tokenpos = nltk.pos_tag(tokens)

まとめ

1-3は以下のコードと等価。

[nltk.pos_tag(tokens) for tokens in [nltk.word_tokenize(sent) for sent in nltk.sent_tokenize(nltk.filestring(open(filename)))]]

今まで自分で書いてたのがあほらしくなった(/_;)。
これを「再発明」というのですね。
今度からやりたいことを実現するツールがあるかチェックしないとな。
「思いついたことのほとんどはもう既に誰かがやっている。」

疑問

ngram作りたいんですけど、nltkにはtrigramまでしかない?