![]() |
ノート/テキストマイニング/NLTK+StanfordParser-2http://pepper.is.sci.toho-u.ac.jp/pepper/index.php?%A5%CE%A1%BC%A5%C8%2F%A5%C6%A5%AD%A5%B9%A5%C8%A5%DE%A5%A4%A5%CB%A5%F3%A5%B0%2FNLTK%2BStanfordParser-2 |
![]() |
ノート/テキストマイニング
訪問者数 1997 最終更新 2010-01-11 (月) 19:03:52
> ノート/テキストマイニング/NLTK
> ノート/テキストマイニング/Stanfordパーザー
> 元ページは ノート/テキストマイニング/NLTK+StanfordParser
大きく変更した点として、(1)モジュール(というかライブラリというか)を呼び出すようにしたこと、(2)そのために入力と出力をあまり一般性を失わないようにした(特定用途に依存した処理をはずした)こと、である。
呼び出し方は、
#!/usr/bin/env python # encoding: utf-8 # -*- coding: utf-8 -*- # coding: utf-8 import sys import nltk from nltk import * import re from subprocess import * mod_path = "/usr/local/python/modules" # 以下3行が呼び出しの設定のおまじない sys.path.append(mod_path) # このpath情報を理解させる from StanfordDependencies import stanforddependencies # これで関数stanforddependenciesをimport f = open('colorectal_canser1.txt') # このサンプルデータファイル中は複数の文からなる1つのabstract file_content = f.read() (trees, relations, wordlists) = stanforddependencies(file_content) # 3つの出力trees, relations, wordlistsがある ### ここからは処理結果を確認するサンプルコード ### まずは、それぞれの出力をプリントしてみる for u in trees: # treesは各文ごとのtreeのリスト print u print "===" for u in relations: # relationsは各文ごとのdependency relationのリスト print u print "===" for u in wordlists: # wordlistsは各文ごとの単語リスト[('NN','aspirin'), ('VBN','work'), ...]のリスト print u print "===" ### dependencyリストを、適宜プリントしてみる。 # 文ごとに分かれているrelations(dependencyペアのリストのリスト)を、全文通しのリスト(ペアのリスト)にする allrelation = [] for u in relations: for v in u: allrelation.append(v) # allrelationをソートしてみる キーはv[0], v[1], v[2]...の順 allrelation.sort() for v in allrelation: # 今後、allrelationリストの内容を使ってよい if v[0] != 'det': print ' ' + v[0] + ' / ' + v[1] + ' / ' + v[3] + ' *** ' + v[2] + ' ' + v[4] # v[0]は関係の名前、v[1]とv[3]は単語を標準形に直したもの、v[2]とv[4]はもとの語のまま ### print ' ' + v[0] + ' / ' + v[1] + ' / ' + v[3] # こうすればv[2]やv[4]を書かなくなる else: print '>> ' + v[0] + ' / ' + v[1] + ' / ' + v[3] + ' *** ' + v[2] + ' ' + v[4] # detだけ除外したければここを無くしてしまえばよい
処理結果の例は
⇒ GetDependencies.txt
そのときの入力は
colorectal_canser1.txt
また、モジュールとして作ったファイル /usr/local/python/modules/StanfordDependencies.pyは
⇒ StanfordDependencies.py
# coding: utf-8 import sys import nltk import sys from nltk import * import re from subprocess import * def stanforddependencies(content): # 入力contentは複数の文からなる文章、戻り値はStanford Dependencyの表[[関係名, 要素1, 要素2], [関係名, 要素1, 要素2], ... ] のリスト # contentを文に分割するためのNLTK内のPUNKT tokenizerを準備する mytokenizer = nltk.data.load('tokenizers/punkt/english.pickle') # Need to prepare this file. # PUNKT tokenizerによって文に分割し、リストsentencesに入れる sentences = mytokenizer.tokenize(content) # contentに含まれる文の数を数えるカウンターを作っておく num_sentences = 0 # Treeの最終結果を溜めるための空のリストを用意する。リストの要素はトリーである。 trees = [] # Dependenciesの最終結果を溜めるための空のリストを用意する。リストの要素はDependenciesリストである(リストのリスト) dependencies = [] wordlists = [] relations = [] # あとでWordNetベースのStemmer(基本形抽出)であるWordNetLemmatizerを使うのでループ外でオブジェクト生成 wnl = stem.WordNetLemmatizer() # あとでre (正規表現処理)を使うので、ループより外でmref, mref2を設定 mref = re.compile(r"((\w|[-])+)\(((\w|[-])+), ((\w|[-])+)\).*", re.S) # S = DOALL mref2 = re.compile(r"((\S)+)-(\d{1,5})", re.S) # S = DOALL # Java Program "StanfordFromNltk.class" をNLTK内から起動するための初期化 nltk.internals.config_java() # sentencesリストの要素ごとに以下を繰返す for sentence in sentences: p = nltk.internals.java(['StanfordFromNltk', '-mx512m', '-Xms512m', '-cp'], '/home/yamanouc/src/stanford:/usr/local/stanford-parser/stanford-parser.jar', stdin=PIPE, stdout=PIPE, blocking=False) q = p.communicate(input=sentence) s = q[0] # Stanford Parserの出力sを、トリー部分とdependency部分に分割する ## 空白行が2つの部分の切れ目になっているので、それを目当てに2つにsplitする。 t = s.split('\n\n', 2) # 前半部分t[0]がトリーの印刷形になっているので、それを使う tree = nltk.bracket_parse(t[0]) trees.append(tree) wordlist = [] # トリー部分についてすべての単語のリストを作っておき、後で使う for u in tree.subtrees(): # subtreesメソッドはgeneratorなので要注意 if (isinstance(u, list) and not isinstance(u[0], list)): # 自身がlistで[0]要素がlistでないノードを探す wordlist.append((u.node, u[0])) # ('NN', 'aspirin')のようなノードのリストを作る wordlists.append(wordlist) # 文ごとのリストwordlistを、全体のリストwordlistsにアペンドする # 後半部分t[1]がdependencyの表になっているので、それを使う relation = [] for u in t[1].split('\n',2000): # 改行文字でデータを行ごとに分割した上で m = mref.search(u) # 正規表現で、abc_d(efg-h, ijk-l) を3つに分解 if m: # m.group(3)と(5)は"xxxxx-nn"の形なので、語幹だけを抽出し語尾変化等を基本形に戻す w = mref2.search(m.group(3)) # 正規表現で、head語をxxxxx-nnを2つに分解 if w: word = w.group(1) # group(1)が欲しいxxxxの部分 else: word = '' # このwordをWordNetLemmatizerを使って基本形に戻す for p in wordlist: if p[1] == word: if (p[0] == 'VB') or (p[0] == 'VBD') or (p[0] == 'VBN') or (p[0] == 'VBG') or (p[0] == 'VBP') or (p[0] == 'VBZ') or (p[0] == 'JJ'): stem3 = wnl.lemmatize(word, 'v') else: stem3 = wnl.lemmatize(word) w = mref2.search(m.group(5)) # 正規表現で、tail語をxxxxx-nnを2つに分解 if w: word = w.group(1) # group(1)が欲しいxxxxの部分 else: word = '' # このwordをWordNetLemmatizerを使って基本形に戻す for p in wordlist: if p[1] == word: if (p[0] == 'VB') or (p[0] == 'VBD') or (p[0] == 'VBN') or (p[0] == 'VBG') or (p[0] == 'VBP') or (p[0] == 'VBZ') or (p[0] == 'JJ'): stem5 = wnl.lemmatize(word, 'v') else: stem5 = wnl.lemmatize(word) relation.append([m.group(1), stem3, m.group(3), stem5, m.group(5)]) # リストrelationは、[[関係名, 要素1, 要素2], [関係名, 要素1, 要素2], ... ] のリストである relations.append(relation) # 戻り値は、(trees, relations, wordlists) である。それぞれは文ごとの出力をリストにしたもの return(trees, relations, wordlists)
なお、treesのノードやwordlistsで出てくるタグはUniv. PennsylvaniaのPenn TreeBankのもので、オリジナルはここ(PostScriptファイル)だし、簡易表はたとえばここを参照。