如何使用循环访问句子中动词前面的单词,使用 spaCy? Python
How to use a loop to access the word preceding a verb in a sentence, using spaCy? Python
我使用 spaCy 通过 POS 标签定位句子中的动词,然后尝试操纵动词。动词的操作取决于条件——例如取决于动词前面的词。例如,我可能想转换这句话 - 包含三个动词 (does, hurt, 运行):
(1) "Why does it hurt to run very fast."
改成这句话:
(2) "It hurts to run very fast."
这对我来说看起来很简单。但是,当我的函数在同一个句子中两次遇到相同的 POS 标记时,不知何故出现了问题。看起来在那种情况下,其中一个 IF 子句(下面的第 13 行)没有更新,因此它的计算结果为 False
而它应该是 True
。我无法弄清楚我忽略了什么以及如何解决它。这是我的代码:
import pandas as pd
import spacy
nlp = spacy.load('en')
s = "Why does it hurt to run very fast."
df = pd.DataFrame({'sentence':[s]})
k = df['sentence']
1 def marking(row):
2 L = row
3 verblst = [('VB'), ('VBZ'), ('VBP')] # list of verb POS tags to focus on
4 chunks = []
5 pos = []
6 for token in nlp(L):
7 pos.append(token.tag_) # Just to check if POS tags are handled well
8 print(pos)
9 if "Why" in L:
10 for token in nlp(L):
11 if token.tag_ in verblst:
# This line checks the POS tag of the word preceding the verb:
12 print(pos[pos.index(token.tag_)-1])
13 if pos[pos.index(token.tag_)-1] == 'TO': # Here things go wrong
14 chunks.append(token.text + token.whitespace_)
15 elif pos[pos.index(token.tag_)-1] == 'WRB':
16 chunks.append(token.text + token.whitespace_)
17 else:
18 chunks.append(token.text + 's' + token.whitespace_)
19 else:
20 chunks.append(token.text_with_ws)
L = chunks
L.pop(0)
L.pop(0)
L = [L[0].capitalize()] + L[1:]
L = "".join(L)
return L
x = k.apply(marking)
print(x)
结果如下:
"It hurts to runs very fast." # The 's' after run should not be there
0 1 2 3 4 5 6 7 8
POS list of s: ['WRB', 'VBZ', 'PRP', 'VB', 'TO', 'VB', 'RB', 'RB', '.']
sentence s: "Why does it hurt to run very fast. ."
问题是由于 'VB' 在索引 3 和 5 处都存在。看起来第 13 行中的索引在第一个 'VB' 之后没有更新 - 这是我所期望的自动发生。结果,对于第二个 'VB',第 13 行查看索引 2 而不是索引 4。因此,不满足第 13 行中的条件,第二个 VB 在第 18 行中处理 - 导致一个错误。我对为什么会这样感到困惑。我没看到什么?如何解决?
非常感谢您的帮助。
这里的问题似乎是您只是在预先编译的词性标记字符串列表中查找 token.tag_
字符串值的索引。这总是 returns 第一个 匹配 – 所以在 "run" 的情况下,您的脚本实际上不会检查索引 5 之前的 POS(这将是 TO
),而是索引 3 之前的 POS(即 PRP
)。
考虑以下抽象示例:
test = ['a', 'b', 'c', 'a', 'd']
for value in test:
print(test.index(value)) # this will print 0, 1, 2, 0, 4
更好(也可能更简单)的解决方案是只遍历 Token
对象并使用 Token.i
attribute,returns 它的索引在父文档中。理想情况下,您希望处理文本一次,存储 doc
,然后在需要时对其进行索引。例如:
chunks = []
doc = nlp("Why does it hurt to run very fast.")
if doc[0].text == 'Why': # the first token's text is "Why"
for token in doc:
if token.tag_ in ['VB', 'VBZ', 'VBP']:
token_index = token.i # this is the token index in the document
prev_token = doc[token_index - 1] # the previous token in the document
if prev_token.tag_ == 'TO':
chunks.append(token.text_with_ws) # token text + whitespace
# and so on
理想情况下,您总是希望将 spaCy 的输出转换为纯文本尽可能晚。您试图在代码中解决的大部分问题都是 spaCy 已经为您完成的事情——例如,它为您提供了 Doc
对象及其视图 Span
和 Token
高性能,让你索引它们,在任何地方迭代标记,更重要的是,永远不会破坏原始文本中可用的任何信息。一旦您的输出是单个文本字符串加上空格和您添加的其他字符,您将无法很容易地恢复原始标记。您也不会知道哪个标记附加了空格以及各个标记如何相互关联 are/were。
有关 Doc
、Token
和 Span
对象的更多详细信息,请参阅 this section in the docs and the API reference,其中列出了每个对象的可用属性。
我使用 spaCy 通过 POS 标签定位句子中的动词,然后尝试操纵动词。动词的操作取决于条件——例如取决于动词前面的词。例如,我可能想转换这句话 - 包含三个动词 (does, hurt, 运行):
(1) "Why does it hurt to run very fast."
改成这句话:
(2) "It hurts to run very fast."
这对我来说看起来很简单。但是,当我的函数在同一个句子中两次遇到相同的 POS 标记时,不知何故出现了问题。看起来在那种情况下,其中一个 IF 子句(下面的第 13 行)没有更新,因此它的计算结果为 False
而它应该是 True
。我无法弄清楚我忽略了什么以及如何解决它。这是我的代码:
import pandas as pd
import spacy
nlp = spacy.load('en')
s = "Why does it hurt to run very fast."
df = pd.DataFrame({'sentence':[s]})
k = df['sentence']
1 def marking(row):
2 L = row
3 verblst = [('VB'), ('VBZ'), ('VBP')] # list of verb POS tags to focus on
4 chunks = []
5 pos = []
6 for token in nlp(L):
7 pos.append(token.tag_) # Just to check if POS tags are handled well
8 print(pos)
9 if "Why" in L:
10 for token in nlp(L):
11 if token.tag_ in verblst:
# This line checks the POS tag of the word preceding the verb:
12 print(pos[pos.index(token.tag_)-1])
13 if pos[pos.index(token.tag_)-1] == 'TO': # Here things go wrong
14 chunks.append(token.text + token.whitespace_)
15 elif pos[pos.index(token.tag_)-1] == 'WRB':
16 chunks.append(token.text + token.whitespace_)
17 else:
18 chunks.append(token.text + 's' + token.whitespace_)
19 else:
20 chunks.append(token.text_with_ws)
L = chunks
L.pop(0)
L.pop(0)
L = [L[0].capitalize()] + L[1:]
L = "".join(L)
return L
x = k.apply(marking)
print(x)
结果如下:
"It hurts to runs very fast." # The 's' after run should not be there
0 1 2 3 4 5 6 7 8
POS list of s: ['WRB', 'VBZ', 'PRP', 'VB', 'TO', 'VB', 'RB', 'RB', '.']
sentence s: "Why does it hurt to run very fast. ."
问题是由于 'VB' 在索引 3 和 5 处都存在。看起来第 13 行中的索引在第一个 'VB' 之后没有更新 - 这是我所期望的自动发生。结果,对于第二个 'VB',第 13 行查看索引 2 而不是索引 4。因此,不满足第 13 行中的条件,第二个 VB 在第 18 行中处理 - 导致一个错误。我对为什么会这样感到困惑。我没看到什么?如何解决?
非常感谢您的帮助。
这里的问题似乎是您只是在预先编译的词性标记字符串列表中查找 token.tag_
字符串值的索引。这总是 returns 第一个 匹配 – 所以在 "run" 的情况下,您的脚本实际上不会检查索引 5 之前的 POS(这将是 TO
),而是索引 3 之前的 POS(即 PRP
)。
考虑以下抽象示例:
test = ['a', 'b', 'c', 'a', 'd']
for value in test:
print(test.index(value)) # this will print 0, 1, 2, 0, 4
更好(也可能更简单)的解决方案是只遍历 Token
对象并使用 Token.i
attribute,returns 它的索引在父文档中。理想情况下,您希望处理文本一次,存储 doc
,然后在需要时对其进行索引。例如:
chunks = []
doc = nlp("Why does it hurt to run very fast.")
if doc[0].text == 'Why': # the first token's text is "Why"
for token in doc:
if token.tag_ in ['VB', 'VBZ', 'VBP']:
token_index = token.i # this is the token index in the document
prev_token = doc[token_index - 1] # the previous token in the document
if prev_token.tag_ == 'TO':
chunks.append(token.text_with_ws) # token text + whitespace
# and so on
理想情况下,您总是希望将 spaCy 的输出转换为纯文本尽可能晚。您试图在代码中解决的大部分问题都是 spaCy 已经为您完成的事情——例如,它为您提供了 Doc
对象及其视图 Span
和 Token
高性能,让你索引它们,在任何地方迭代标记,更重要的是,永远不会破坏原始文本中可用的任何信息。一旦您的输出是单个文本字符串加上空格和您添加的其他字符,您将无法很容易地恢复原始标记。您也不会知道哪个标记附加了空格以及各个标记如何相互关联 are/were。
有关 Doc
、Token
和 Span
对象的更多详细信息,请参阅 this section in the docs and the API reference,其中列出了每个对象的可用属性。