ノート/テキストマイニング
訪問者数 1240      最終更新 2010-01-11 (月) 19:03:52
> ノート/テキストマイニング/NLTK
> ノート/テキストマイニング/Stanfordパーザー
> 元ページは ノート/テキストマイニング/NLTK+StanfordParser

サンプル 第1版 (2010/01/11)

大きく変更した点として、(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だけ除外したければここを無くしてしまえばよい

処理結果の例は   ⇒ fileGetDependencies.txt    そのときの入力は filecolorectal_canser1.txt

また、モジュールとして作ったファイル /usr/local/python/modules/StanfordDependencies.pyは   ⇒ fileStanfordDependencies.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ファイル)だし、簡易表はたとえばここを参照。


添付ファイル: filecolorectal_canser1.txt 382件 [詳細] fileStanfordDependencies.py 340件 [詳細] fileGetDependencies.txt 384件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2010-01-11 (月) 19:03:52 (2719d)