2021SC@SDUSC
(续blog7)
用于提取候选词的三个函数:extract_candidates(text_obj, no_subset=False)、extract_sent_candidates(text_obj)、unique_ngram_candidates(strings)
参数以及返回值说明:
根据一部分文字返回一组候选词:
参数text_obj:输入文本表示方式请参见@InputTextObj。
参数no_subset:如果为真,则一个候选词不能为另一个候选词的子集。
参数lang:语言种类,当前支持en(英语)、fr(法语)、de(德语)。
返回值:一组候选短语(每个候选词以字符串的形式)
一、extract_candidates(text_obj, no_subset=False)
创建一个空集合keyphrase_candidate
keyphrase_candidate = set()
分析预先定义的分块语法grammar,得到多个语法规则,然后利用parser函数对句子分析,得到语法树:
np_parser = nltk.RegexpParser(get_grammar(text_obj.lang)) # Noun phrase parser
trees = np_parser.parse_sents(text_obj.pos_tagged) # Generator with one tree per sentence
筛选出NP并做一定的处理:
for tree in trees:
for subtree in tree.subtrees(filter=lambda t: t.label() == 'NP'): # For each nounphrase
# Concatenate the token with a space
keyphrase_candidate.add(' '.join(word for word, tag in subtree.leaves()))
keyphrase_candidate = {kp for kp in keyphrase_candidate if len(kp.split()) <= 5}
如果no_subset不存在一个候选词是另一个候选词子集的情况,那么将keyphrase转换成list,否则执行unique_ngram_candidates(keyphrase_candidate)函数(这个函数的作用在后面),并返回结果。
if no_subset:
keyphrase_candidate = unique_ngram_candidates(keyphrase_candidate)
else:
keyphrase_candidate = list(keyphrase_candidate)
return keyphrase_candidate
二、extract_sent_candidates(text_obj)
这个函数只有一行代码,标记化句子(字符串)列表,每个标记由字符串中的空格分隔:
return [(' '.join(word for word, tag in sent)) for sent in text_obj.pos_tagged]
三、unique_ngram_candidates(strings)
返回在一个字符串中没有完全包含任何字符串的字符串列表:
results = []
for s in sorted(set(strings), key=len, reverse=True):
if not any(re.search(r'\b{}\b'.format(re.escape(s)), r) for r in results):
results.append(s)
return results
四、对RegexpParser类的学习补充
在函数extract_candidates(text_obj, no_subset=False)中,使用了RegexpPraser类来构造tree。在tree的构成中,分块是个重要的过程,如NP(DT,JJ,JJ,NN)可构成一个NP短语。nltk中有重要的类是RegexpParser,分析预先定义的分块语法grammar,得到多个语法规则,然后利用parser函数对句子分析,得到语法树。下面就介绍一个RegexpParser分析grammar的过程和parser函数的解析过程。
调用:cp=nltk.RegexpParser(grammar)
输入:分块语法grammar,有一个或多个识别块信息的正则表达式,如:
grammar = r"""
NP: {<.*>*} # start by chunking everything
}<[\.VI].*>+{ # chink any verbs, prepositions or periods
<.*>}{<DT> # separate on determiners
PP: {<IN><NP>} # PP = preposition + noun phrase
VP: {<VB.*><NP|PP>*} # VP = verb words + NPs and PPs
"""
输出:
过程:分析过程主要是在read_grammar函数里实现,当grammar是string类型时(如上),就调用read_grammar进行分析。下面就介绍一下分析过程中的要点:
(1)按行读取,并将每行的开头、结尾空格删除
(2)分离非终结符和规则,调用re.py里的match函数进行分离(其细节与chunk模块无关,这里就先不研究了),分离的依据仍然是正则表达式,冒号之前的是非终结符,如NP、PP、VP,冒号之后的是rule
(3)程序用stage来记录有多少个非终结符,一旦遇到非终结符就重设stage,将上一个rule记录下来(之所以记录的是上一个,是因为当前的rule尚未处理,可能存在注释,需要经过处理之后才能记录下来),因此在重设stage时需要判断rule是否为空,那么在首次记录时就不会报错
(4)对于记录rule的过程,首先设定识别chunk、chink、split和merge的规则:
{regexp} # chunk rule
}regexp{ # chink rule
regexp}{regexp # split rule
regexp{}regexp # merge rule
因此,对于传入的rule,将进行如下处理:
①类似于分离非终结符和rule的方法,将rule和#注释分离,依据是#
②对于分离出的rule(不带注释),根据上面讲的规则,识别出对应的类别,分别调用ChunkRule、ChinkRule、SplitRule和MergeRule进行处理。