データナード

機械学習と自然言語処理についての備忘録 (旧ナード戦隊データマン)

edict2をjsonに変換

edict2は、JMdict/EDICTプロジェクトで作られている日英辞書です。この辞書を、英語をキー、キーに対応した日本語の単語リストをバリューとしたjsonに変換します。

edict2json.py · GitHub

事前準備

edict2のダウンロード

wget http://ftp.monash.edu/pub/nihongo/edict2.gz
gunzip edict2.gz

edict2をutf-8

 iconv -f euc-jp -t utf-8 edict2 -o edict2_utf8

pythonコード

import re
import json
from tqdm import tqdm

kakko = re.compile(r"[\((\{].+?[\))\}]")


def process_line(line):
    tline = line[:].strip()
    tline = re.sub(kakko, "", tline)
    tline = tline.split("/", 1)
    tline[0] = tline[0].split(" [")[0]
    jws = tline[0].split(";")
    ews = [x.strip() for x in tline[1].split("/") if x.strip()]
    ews, ent = ews[:-1], ews[-1]
    return ent, jws, ews


def simplify(infile="../data/edict2_utf8",
             outfile="../data/edict2_simplified"):
    with open(infile) as f, open(outfile, "w") as fw:
        for line in f:
            ent, jws, ews = process_line(line)
            fw.write("{}\t{}\t{}\n".format(
                ent, ':::'.join(jws), ':::'.join(ews)))


def alignment(infile="../data/edict2_simplified"):
    out = {}
    with open(infile) as f:
        for line in tqdm(f):
            if not line.startswith("Ent"):
                continue
            line = line.strip().split("\t")
            jws = line[1].split(":::")
            ews = line[2].split(":::")
            for ew in ews:
                ew = ew.lower()
                if ew not in out:
                    out[ew] = set()
                for jw in jws:
                    out[ew].add(jw)
    out = {k: list(vset) for k, vset in out.items()}
    return out


if __name__ == "__main__":
    simplify()
    with open("edict2_aligned.json", "w") as f:
        json.dump(alignment(), f, indent=4, ensure_ascii=False)

Note: ファイルの場所と名前は適当に変える必要があります。

出力の一部

{

...


    "3pl": [
        "サードパーティーロジスティクス",
        "サード・パーティー・ロジスティクス ",
        "3PL"
    ],
    "three-card monte": [
        "3カードモンテ"
    ],
    "march": [
        "進軍",
        "3月",
        "行程",
        "三月",
        "行軍",
        "マーチ ",
        "行進",
        "行進曲"
    ],
    "third month of the lunar calendar": [
        "弥生",
        "晩春",
        "3月",
        "三月",
        "暮春",
        "季春"
    ],
    "ternary material": [
        "3元材料"
    ],
    "man who is highly educated, has a high salary and is tall": [
        "3高",
        "三高"
    ],
    "three highs": [
        "3高",
        "三高"
    ],

...


}

追記

2020-02-27 10:00

なにかに使えそうなので、共起する英語定義をエッジとしてグラフを取得する関数を追加しました。

def extract_graph(infile="../data/edict2_simplified"):
    edges = set()
    nodes = set()
    with open(infile) as f:
        for line in tqdm(f):
            if not line.startswith("Ent"):
                continue
            line = line.strip().split("\t")
            ews = line[2].split(":::")
            for ew1 in ews:
                ew1 = ew1.strip().lower()
                nodes.add(ew1)
                for ew2 in ews:
                    ew2 = ew2.strip().lower()
                    if ew1 == ew2:
                        continue
                    edges.add((ew1, ew2))
    return sorted(list(nodes)), sorted(list(edges))

2020-02-27 12:24

グラフを使えば、あるノード(英語定義)から一定以内の距離にある日本語単語を探すことができます。wordnetのような階層性をもっと大規模で見出すことができないか、という課題に対する一つの探索的な方法です。

import json
import pickle
import networkx as nx


def load(indict="../data/edict2_aligned.json", ingraph="./edict2_engraph.pkl"):
    with open(indict) as f:
        dic = json.load(f)
    with open(ingraph, "rb") as f:
        G = pickle.load(f)
    return dic, G


def get_jawords(dic, G, node, dist=0):
    dist = int(dist)
    visited = set()
    out = []

    if node in dic:
        out += dic[node]

    for n, _ in nx.single_source_shortest_path(G, node, dist).items():
        if n in visited:
            pass
        else:
            visited.add(n)
            if n in dic:
                out += dic[n]
    return sorted(list(set(out)))


if __name__ == "__main__":
    import sys
    dic, G = load()
    print(get_jawords(dic, G, *sys.argv[1:]))
$ python3 edict2_graph.py "jail" 0
['ジェイル ', '人屋', '刑獄', '別荘', '囚獄', '拘置所', '', '牢屋', '牢獄', '', '獄舎', '留置場', '留置所', '鉄窓']

$ python3 edict2_graph.py "jail" 1
['お仕置き', 'ジェイル ', 'パニッシュメント ', 'ビラ', 'ブタ箱', 'プリズン ', 'リゾートハウス', 'リゾート・ハウス ', 'ヴィラ ', '下屋敷', '人屋', '仕置', '仕置き', '処分', '処罰', '', '刑務所', '刑戮', '刑獄', '刑罰', '別業', '別荘', '別邸', '制裁', '務所', '受刑', '囚獄', '報い', '', '征伐', '御仕置き', '懲悪', '懲戒', '懲罰', '成敗', '戒め', '拘置所', '', '牢屋', '牢獄', '', '獄屋', '獄所', '獄舎', '留置場', '留置所', '監獄', '科罰', '', '罪科', '', '罰則', '膺懲', '', '誡め', '警め', '豚箱', '酬い', '鉄格子', '鉄窓']

参考

  1. JMdict/EDICT Project