せっかくのクリスマスなのでマタイによる福音書のイエズス・キリストの系図でも描いてみます。
アブラハムの子はイサク、
イサクの子はヤコブ、
ヤコブの子はユダと、その兄弟たち、
ユダの子はタマルによるペレズと、ゼラ、
ペレズの子はヘズロン、
ヘズロンの子はラム、
ラムの子はアミナダブ、
アミナダブの子はナション、
ナションの子はサルモン、
サルモンの子はラハブによるボアズ、
ボアズの子はルツによるオベデ、
オベデの子はエッサイ、
エッサイの子はダビデである。
ダビデの子はウリヤの妻によるソロモン、
ソロモンの子はレハバアム、
レハバアムの子はアビヤ、
アビヤの子はアサ、
アサの子はヨシャパテ、
ヨシャパテの子はヨラム、
ヨラムの子はウジヤ、
ウジヤの子はヨタム、
ヨタムの子はアハズ、
アハズの子はヒゼキヤ、
ヒゼキヤの子はマナセ、
マナセの子はアモン、
アモンの子はヨシヤ、
ヨシヤの子はバビロン追放当時の、エコニヤと、その兄弟たちである。
バビロン追放の後は次の通り。
エコニヤの子はシャルテル、
シャルテルの子はゼルバベル、
ゼルバベルの子はアビウデ、
アビウデの子はエリヤキム、
エリヤキムの子はアゾル、
アゾルの子はサドク、
サドクの子はアキム、
アキムの子はエリウデ、
エリウデの子はエレアザル、
エレアザルの子はマタン、
マタンの子はヤコブ、
ヤコブの子はマリアの夫のヨセフである。
準備
Cabocha 0.68のインストール
http://qiita.com/mima_ita/items/161cd869648edb30627b
GraphVizとpydotのインストール
http://needtec.exblog.jp/20608517/
ユーザ辞書をつくる
固有名があるので、形態素解析ができるようにユーザ辞書を作成します。
以下のようなCSVをつくります。
bible.csv
アブラハム,0,0,500,名詞,一般,*,*,*,*,アブラハム,アブラハム,アブラハム
イサク,0,0,500,名詞,一般,*,*,*,*,イサク,イサク,イサク
ヤコブ,0,0,500,名詞,一般,*,*,*,*,ヤコブ,ヤコブ,ヤコブ
ユダ,0,0,500,名詞,一般,*,*,*,*,ユダ,ユダ,ユダ
ゼラ,0,0,500,名詞,一般,*,*,*,*,ゼラ,ゼラ,ゼラ
ペレズ,0,0,500,名詞,一般,*,*,*,*,ペレズ,ペレズ,ペレズ
ヘズロン,0,0,500,名詞,一般,*,*,*,*,ヘズロン,ヘズロン,ヘズロン
ラム,0,0,500,名詞,一般,*,*,*,*,ラム,ラム,ラム
アミナダブ,0,0,500,名詞,一般,*,*,*,*,アミナダブ,アミナダブ,アミナダブ
ナション,0,0,500,名詞,一般,*,*,*,*,ナション,ナション,ナション
サルモン,0,0,500,名詞,一般,*,*,*,*,サルモン,サルモン,サルモン
ボアズ,0,0,500,名詞,一般,*,*,*,*,ボアズ,ボアズ,ボアズ
オベデ,0,0,500,名詞,一般,*,*,*,*,オベデ,オベデ,オベデ
エッサイ,0,0,500,名詞,一般,*,*,*,*,エッサイ,エッサイ,エッサイ
ダビデ,0,0,500,名詞,一般,*,*,*,*,ダビデ,ダビデ,ダビデ
ソロモン,0,0,500,名詞,一般,*,*,*,*,ソロモン,ソロモン,ソロモン
レハバアム,0,0,500,名詞,一般,*,*,*,*,レハバアム,レハバアム,レハバアム
アビヤ,0,0,500,名詞,一般,*,*,*,*,アビヤ,アビヤ,アビヤ
アサ,0,0,500,名詞,一般,*,*,*,*,アサ,アサ,アサ
ヨシャパテ,0,0,500,名詞,一般,*,*,*,*,ヨシャパテ,ヨシャパテ,ヨシャパテ
ヨラム,0,0,500,名詞,一般,*,*,*,*,ヨラム,ヨラム,ヨラム
ウジヤ,0,0,500,名詞,一般,*,*,*,*,ウジヤ,ウジヤ,ウジヤ
ヨタム,0,0,500,名詞,一般,*,*,*,*,ヨタム,ヨタム,ヨタム
アハズ,0,0,500,名詞,一般,*,*,*,*,アハズ,アハズ,アハズ
ヒゼキヤ,0,0,500,名詞,一般,*,*,*,*,ヒゼキヤ,ヒゼキヤ,ヒゼキヤ
マナセ,0,0,500,名詞,一般,*,*,*,*,マナセ,マナセ,マナセ
アモン,0,0,500,名詞,一般,*,*,*,*,アモン,アモン,アモン
ヨシヤ,0,0,500,名詞,一般,*,*,*,*,ヨシヤ,ヨシヤ,ヨシヤ
バビロン,0,0,500,名詞,一般,*,*,*,*,バビロン,バビロン,バビロン
エコニヤ,0,0,500,名詞,一般,*,*,*,*,エコニヤ,エコニヤ,エコニヤ
シャルテル,0,0,500,名詞,一般,*,*,*,*,シャルテル,シャルテル,シャルテル
ゼルバベル,0,0,500,名詞,一般,*,*,*,*,ゼルバベル,ゼルバベル,ゼルバベル
アビウデ,0,0,500,名詞,一般,*,*,*,*,アビウデ,アビウデ,アビウデ
エリヤキム,0,0,500,名詞,一般,*,*,*,*,エリヤキム,エリヤキム,エリヤキム
アゾル,0,0,500,名詞,一般,*,*,*,*,アゾル,アゾル,アゾル
サドク,0,0,500,名詞,一般,*,*,*,*,サドク,サドク,サドク
アキム,0,0,500,名詞,一般,*,*,*,*,アキム,アキム,アキム
エリウデ,0,0,500,名詞,一般,*,*,*,*,エリウデ,エリウデ,エリウデ
エレアザル,0,0,500,名詞,一般,*,*,*,*,エレアザル,エレアザル,エレアザル
マタン,0,0,500,名詞,一般,*,*,*,*,マタン,マタン,マタン
ヤコブ,0,0,500,名詞,一般,*,*,*,*,ヤコブ,ヤコブ,ヤコブ
Windowsの場合は次のコマンドをコマンドプロンプトで実行するとbible.dicというユーザ辞書が作成できます。
"C:\Program Files (x86)\MeCab\bin\mecab-dict-index" -d"C:\Program Files (x86)\MeCab\dic\ipadic" -u bible.dic -f shift-jis -t utf-8 bible.csv
Cabochaでは-uオプションでユーザ辞書のパスを指定することで、ユーザ辞書を使用した係り受け解析が行えます。
家系図作成用のコード
bible.py
# !/usr/bin/python
# -*- coding: utf-8 -*-
import CaboCha
import pydot
import uuid
import sys
class node:
"""
家系図のノードを記録
"""
def __init__(self):
self.name = None
self.children_node = {}
self.pairs_node = {}
self.id = str(uuid.uuid4())
font_name = "ms ui gothic"
sentence = """
アブラハムの子はイサク、\n
イサクの子はヤコブ、\n
ヤコブの子はユダと、その兄弟たち、\n
ユダの子はタマルによるペレズと、ゼラ、\n
ペレズの子はヘズロン、\n
ヘズロンの子はラム、\n
ラムの子はアミナダブ、\n
アミナダブの子はナション、\n
ナションの子はサルモン、\n
サルモンの子はラハブによるボアズ、\n
ボアズの子はルツによるオベデ、\n
オベデの子はエッサイ、\n
エッサイの子はダビデである。\n
ダビデの子はウリヤの妻によるソロモン、\n
ソロモンの子はレハバアム、\n
レハバアムの子はアビヤ、\n
アビヤの子はアサ、\n
アサの子はヨシャパテ、\n
ヨシャパテの子はヨラム、\n
ヨラムの子はウジヤ、\n
ウジヤの子はヨタム、\n
ヨタムの子はアハズ、\n
アハズの子はヒゼキヤ、\n
ヒゼキヤの子はマナセ、\n
マナセの子はアモン、\n
アモンの子はヨシヤ、\n
ヨシヤの子はバビロン追放当時の、エコニヤと、その兄弟たちである。\n
バビロン追放の後は次の通り。\n
エコニヤの子はシャルテル、\n
シャルテルの子はゼルバベル、\n
ゼルバベルの子はアビウデ、\n
アビウデの子はエリヤキム、\n
エリヤキムの子はアゾル、\n
アゾルの子はサドク、\n
サドクの子はアキム、\n
アキムの子はエリウデ、\n
エリウデの子はエレアザル、\n
エレアザルの子はマタン、\n
マタンの子はヤコブ、\n
ヤコブの子はマリアの夫のヨセフである。\n
"""
def get_word(tree, ix):
surface = tree.token(ix).surface
f = tree.token(ix).feature.split(",")
return surface, f
def create_node(tree, chunk):
s1, f1 = get_word(tree, chunk.token_pos + chunk.head_pos)
s2 = None
f2 = None
type = 0
if chunk.head_pos != chunk.func_pos:
s2, f2 = get_word(tree, chunk.token_pos + chunk.func_pos)
if f2[0] == '助詞':
if f2[1] == '連体化': # 「の」
type = 1
elif f2[1] == '係助詞': # 「は」
type = 2
elif f2[1] == '接続助詞': # 「と」
type = 3
elif f2[1] == '格助詞': # 「による」
type = 4
if f1[0] == '名詞':
if f1[1] == '接尾':
s1 = tree.token(chunk.token_pos + chunk.head_pos - 1).surface + s1
return {
'text': s1,
'type': type
}
def find_child(t, child_name):
"""
家系図から指定の子を持つノードを取得する
"""
for key, item in t.children_node.items():
if key == child_name and item is None:
return t
elif item is not None:
ret = find_child(item, child_name)
if ret:
return ret
return None
def dump_family(family_tree):
"""
家系図のダンプ
"""
print ('親:', family_tree.name)
for key, item in family_tree.pairs_node.items():
print ('妻:', key)
for key, item in family_tree.children_node.items():
print ('子:', key)
if item:
print (dump_family(item))
def create_graph_children(graph, p_node, child):
n = pydot.Node(child.id,
label=child.name,
style="filled",
fillcolor="green",
shape = "box",
fontname=font_name)
subg = pydot.Subgraph('', rank='same')
subg.add_node(n)
graph.add_subgraph(subg)
if p_node:
graph.add_edge(pydot.Edge(p_node, n))
for key, item in child.pairs_node.items():
pair = pydot.Node(str(uuid.uuid4()),
label=key,
style="filled",
fillcolor="pink",
shape = "box",
fontname=font_name)
subg.add_node(pair)
graph.add_edge(pydot.Edge(n, pair))
for key, item in child.children_node.items():
if item:
create_graph_children(graph, n, item)
else:
nchild = pydot.Node(str(uuid.uuid4()),
label=key,
style="filled",
fillcolor="gray",
shape = "box",
fontname=font_name)
graph.add_node(nchild)
graph.add_edge(pydot.Edge(n, nchild))
def create_graph(family_tree):
graph = pydot.Dot(graph_type='digraph',
fontname=font_name)
create_graph_children(graph, None, family_tree)
graph.write_png('example2_graph.png')
def main(argvs, argc):
c = CaboCha.Parser("-u bible.dic")
lines = sentence.split("\n")
family_tree = None
for line in lines:
tree = c.parse(line)
data = node()
if tree.chunk_size() == 0:
continue
ids = {-1: None}
children = []
pairs = []
for i in range(tree.chunk_size()):
curid = i
if i in ids:
continue
chunk = tree.chunk(i)
attr = None
cnn_type = 0
while True:
d = create_node(tree, chunk)
ids[curid] = d
if data.name is None:
data.name = d['text']
if cnn_type == 1: # 前のチャンクが「の」で終わっている
if d['text'] == '子':
attr = 'children'
elif cnn_type == 2: # 前のチャンクが「は」で終わっている
if attr == 'children':
children.append(curid)
elif cnn_type == 3: # 前のチャンクが「と」で終わっている場合は、同じ操作
if attr == 'children':
children.append(curid)
else:
attr = None
if d['type'] == 4: # チャンクが「よる」で終わっている場合は、配偶者とみなす
pairs.append(i)
else:
cnn_type = d['type']
if not chunk.link in ids:
curid = chunk.link
chunk = tree.chunk(curid)
else:
break
for child in children:
data.children_node[ids[child]['text']] = None
for pair in pairs:
data.pairs_node[ids[pair]['text']] = None
if len(children) > 0:
if family_tree is None:
family_tree = data
else:
node_obj = find_child(family_tree, data.name)
if node_obj:
node_obj.children_node[data.name] = data
if family_tree:
dump_family(family_tree)
create_graph(family_tree)
if __name__ == '__main__':
argvs = sys.argv
argc = len(argvs)
sys.exit(main(argvs, argc))
同じような文体で記述すれば、多分、いろんな家系図が作成できまます。
一応、配偶者を複数設定できるデータ構造なのは、ご先祖様のただれた・・・もとい精力的な交配活動に対応できるようにしてますが、誰と誰の子ってのは意識してません。