[[ノート/テキストマイニング]]~
訪問者数 &counter(); 最終更新 &lastmod();~
> [[ノート/テキストマイニング/NLTK]]~
> [[ノート/テキストマイニング/Stanfordパーザー]]~
> 元ページは [[ノート/テキストマイニング/NLTK+StanfordParser]]
***サンプル 第1版 (2010/01/11) [#e0e64e6b]
大きく変更した点として、(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だけ除外したければここを無くしてしまえばよい
処理結果の例は
⇒ &ref(GetDependencies.txt);
そのときの入力は &ref(colorectal_canser1.txt);
また、モジュールとして作ったファイル /usr/local/python/modules/StanfordDependencies.pyは
⇒ &ref(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',
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ファイル):ftp://ftp.cis.upenn.edu/pub/treebank/doc/tagguide.ps.gz]]だし、簡易表はたとえば[[ここ:http://www.comp.leeds.ac.uk/amalgam/tagsets/upenn.html]]を参照。