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になにかないかな。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中