Source code for pyknp.knp.pas

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import unicode_literals
from __future__ import absolute_import
import collections
import six
import re
import sys


[docs]class Argument(object): """ 項に関する情報を保持するオブジェクト 詳しくは下記ページの「格要素側」の記述方法を参照 http://nlp.ist.i.kyoto-u.ac.jp/index.php?KNP%2F%E6%A0%BC%E8%A7%A3%E6%9E%90%E7%B5%90%E6%9E%9C%E6%9B%B8%E5%BC%8F Attributes: sid (str): 文ID tid (int): 基本句ID eid (int): Entity ID midasi (str): 表記 flag (str): フラグ (C, N, O, D, E, U) sdist (int): 述語の何文前か """
[docs] def __init__(self, sid=None, tid=None, eid=None, midasi='', flag=None, sdist=None): assert isinstance(tid, int) assert isinstance(midasi, six.text_type) self.sid = sid self.tid = tid self.eid = eid self.midasi = midasi self.flag = flag self.sdist = sdist
ArgRepname = collections.namedtuple("ArgRepname", "repname,tid_list") class CaseInfoFormat(object): """ 格/述語項構造のフォーマットを管理する名前空間 """ CASE = 0 # 格解析フォーマット PASv41 = 1 # KNP v4.1 での述語項構造フォーマット PASv42 = 2 # KNP v4.2 での述語項構造フォーマット
[docs]class Pas(object): """ 述語項構造を扱うクラス Usage: result = knp.result(knp_result) pas = result.tag_list()[tid].pas # tid番目の基本句(述語)の項構造 Attributes: cfid (str): 格フレームID (例: "行う/おこなう:動10") arguments (dict of (case, list of Argument)): 格と項を対応付けた辞書 {case: [Argument, ..]} keyは格を表す文字列, valueはArgumentオブジェクトのリスト。 リスト形式なのは、ガ格などは複数の項を取り得るため。 """
[docs] def __init__(self, tid=None, result=None): self.valid = True self.cfid = None self.arguments = collections.defaultdict(list) if tid is None: self.valid = False return self.tid = tid self.tag_list = result.tag_list() self.tid2sdist = {} self.sid = result.sid tag_predicate = self.tag_list[self.tid] if "項構造" in tag_predicate.features: # KNP v4.1 で -anaphora # (eid, tid, sdist) の対を記録し、dictに保持(eid2tid, tid2sdist) # eid2tidはeidのエンティティが初出のTag位置(tid)を保持 self.eid2tid = {} for tag in self.tag_list: if 'EID' in tag.features: eid = int(tag.features['EID']) self.eid2tid[eid] = tag.tag_id # (tid, sdist) を記録するため、"格解析結果"の値をパース case_analysis = tag_predicate.features.get("格解析結果") for items in self.__parse_case_analysis_items(case_analysis, CaseInfoFormat.CASE): (mycase, caseflag, midasi, eid, tid, sdist, sid) = items self.tid2sdist[tid] = sdist self.__set_args(tag_predicate.features.get("項構造"), CaseInfoFormat.PASv41) elif "述語項構造" in tag_predicate.features: # KNP v4.2 で -anaphora self.__set_args(tag_predicate.features.get("述語項構造"), CaseInfoFormat.PASv42) elif "格解析結果" in tag_predicate.features: self.__set_args(tag_predicate.features.get("格解析結果"), CaseInfoFormat.CASE) else: self.valid = False
[docs] def is_valid(self): return self.valid
[docs] def get_arguments(self, case): """ 指定した格の各項ごとに代表表記の配列を返す Usage: result = knp.parse("研究者になる") print(result.tag_list()[2].midasi) >> なる print(result.tag_list()[2].pas.get_arguments("ニ")) >> [ArgRepname(repname='者/しゃ', tid_list=1)] Args: case (str): 格の文字列 Returns: list: 項の代表表記を格納したnamedtupleである ArgRepname のリスト """ output = [] for arg in self.arguments[case]: tid = arg.tid if self.tag_list[tid].head_prime_repname: rep = self.tag_list[tid].head_prime_repname else: rep = self.tag_list[tid].repname output.append(ArgRepname(rep, tid)) return output
[docs] def get_orig_result(self): return self.tag_list[self.tid].features.get("格解析結果")
def __parse_case_info_format(self, items, case_info_format): """ 格/述語項構造フォーマットを読み取る """ if case_info_format == CaseInfoFormat.CASE: mycase = items[0] caseflag = items[1] midasi = items[2] tid = int(items[3]) sdist = int(items[4]) sid = items[5] eid = None elif case_info_format == CaseInfoFormat.PASv41: mycase = items[0] caseflag = items[1] midasi = items[2] eid = int(items[3]) if eid in self.eid2tid: # eidのエンティティが同一文内にない場合、KeyError tid = self.eid2tid[eid] sdist = self.tid2sdist[tid] if tid in self.tid2sdist else None sid = self.sid else: tid = -1 # FIXME: EIDが登録された文内のTag位置 sdist = None sid = "-1" # FIXME: EIDが登録された文のsid (複数文を辿れる必要がある) else: # FIXME: tid,sidについてv4.1と同じ問題がある assert case_info_format == CaseInfoFormat.PASv42 mycase = items[0] caseflag = items[1] midasi = items[2] sdist = int(items[3]) tid = int(items[4]) eid = int(items[5]) sid = self.sid return mycase, caseflag, midasi, eid, tid, sdist, sid def __parse_case_analysis_items(self, analysis_result, case_info_format): """ 述語情報の設定・格情報の抽出 """ assert isinstance(analysis_result, six.text_type) # language=RegExp cfid_pat = r'(.*?):([^:/]+?)' if case_info_format == CaseInfoFormat.CASE: # language=RegExp arg_pat = r'(.+?/[CNODEU-]/.+?(?:/(?:-|\d+)){2}/[^;/]+)' elif case_info_format == CaseInfoFormat.PASv41: # language=RegExp arg_pat = r'(.+?/[CNODEU-]/.+?/(?:-|\d+))' else: assert case_info_format == CaseInfoFormat.PASv42 # language=RegExp arg_pat = r'(.+?/[CNODEU-]/.+?(?:/(?:-?\d*)){3})' match = re.match(r'{}(?::{}|$)'.format(cfid_pat, arg_pat), analysis_result) if match is None: print("invalid tag format: '{}' is ignored".format(analysis_result), file=sys.stderr) return self.cfid = match.group(1) + ':' + match.group(2) if match.group(3) is None: # <述語項構造:束の間/つかのま:判0> など return arg_pat_compiled = re.compile(';' + arg_pat) cases = [match.group(3)] pos = match.end(3) while True: match = arg_pat_compiled.match(analysis_result, pos=pos) if match is None: break cases.append(match.group(1)) pos = match.end(1) for k in cases: items = k.split('/') caseflag = items[1] if caseflag == 'U' or caseflag == '-': continue yield self.__parse_case_info_format(items, case_info_format) def __set_args(self, analysis_result, case_info_format): """ 述語項構造情報をself.argumentsに設定 """ for items in self.__parse_case_analysis_items(analysis_result, case_info_format): (mycase, caseflag, midasi, eid, tid, sdist, sid) = items arg = Argument(sid=sid, tid=tid, eid=eid, midasi=midasi, flag=caseflag, sdist=sdist) self.arguments[mycase].append(arg)