这是一个会员测试。
首先,我们的导入和doc 创建:
import spacy
from spacy.tokens.doc import Doc
from spacy.tokens.span import Span
from typing import Union, Tuple
nlp = spacy.load("en_core_web_sm")
doc = nlp('the angry programmer wrote a nice stackover question to another angry programmer')
方法一:
def approach_1(doclike: Union[Doc, Span]) -> None:
for token in doclike:
print(token, next((noun_chunk for noun_chunk in doclike.noun_chunks if token in noun_chunk), None))
但是,我们可以看到这不是一个理想的方法,因为我们为每个 token 循环了 noun_chunks。
%%timeit
approach_1(doc)
112 µs ± 1.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
相反,正如 Matthew Cox 建议的那样,我们应该保存 noun_chunks。
def approach_2(doclike: Union[Doc, Span]) -> None:
noun_chunks: Tuple = (*(stack_overflow_doc.noun_chunks),)
for token in doclike:
for noun_chunk in noun_chunks:
if token in noun_chunk:
print(token, noun_chunk)
else:
print(token)
这在时间上给我们带来了明显更好的性能:
%%timeit
approach_2(doc)
29.6 µs ± 1.13 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
但是,如果您打算在同一个 doc 上多次执行此操作,我不建议使用任何一种方法。相反,为令牌提供自定义 noun_chunk 属性。这样做会产生设置成本,但后续查询应该会更便宜。
Token.set_extension('noun_chunk', default=None)
%%timeit
for noun_chunk in doc.noun_chunks:
for token in noun_chunk:
token._.noun_chunk = noun_chunk
25.2 µs ± 736 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
def approach_3(doclike: Union[Doc, Span]) -> None:
for token in doclike:
print(token, token._.noun_chunk)
%%timeit
approach_3(doc)
21 µs ± 262 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
然后考虑adding a custom pipeline component 在初始doc 创建期间添加noun_chunk 属性。
编辑: 对于这个例子,Matthew 的方法似乎更快。他的方法与管道组件相结合,可能正是您想要的。