【问题标题】:Search for series of ordered tokens in sentences represented as a dataframe of individual tokens在表示为单个标记的数据框的句子中搜索一系列有序标记
【发布时间】:2020-12-21 21:39:44
【问题描述】:

我正在尝试更多地了解 R 中的语料库、单词分析。最近我开始使用 CleanNLP 和 Spacy Backend。问题是,在解析文本后,我想看看一个句子是否有标记有不同关系的标记。

比方说,

library(cleanNLP)
library(tidyverse)
text <- cnlp_annotate(c("I gave him money"))

结果是

 doc_id   sid   tid token token_with_ws lemma  upos  xpos  tid_source relation
   <int> <int> <int> <chr> <chr>         <chr>  <chr> <chr>      <int> <chr>   
1      1     1     1 I     "I "          -PRON- PRON  PRP            2 nsubj   
2      1     1     2 gave  "gave "       give   VERB  VBD            0 root    
3      1     1     3 money "money "      money  NOUN  NN             2 dobj    
4      1     1     4 to    "to "         to     ADP   IN             2 dative  
5      1     1     5 him   "him"         -PRON- PRON  PRP            4 pobj 

我通过

改变了数据框
dative <- c("dative")
     anno %>%
+     filter(grepl(dative, relation)) %>% 
+     select(sid, sentence)

并在上下文前后查找

anno %>%
+     mutate(kwic = ifelse(grepl(dative, relation),
+                          TRUE, FALSE)) %>%
+     mutate(before = gsub("NA\\s?", "", paste(lag(token, 3), lag(token, 2), lag(token))),
+            after = gsub("NA\\s?", "", paste(lead(token), lead(token, 2), lead(token, 3)))
+     ) %>%
+     filter(kwic) %>%
+     select(before, token, after)

我想从具有所有三个关系标签 (dobj, dative, pobj) 的语料库中提取句子。换句话说,我想检查上下文前后,如果上下文前后有标签"dobj""pobj",则提取句子。

所以基本上,我想提取模式为 Dobj、Dative、Pobj 的句子(带有双宾语的句子;我给了他钱),但不是用一两个变量的模式,我们只说 Dobj;我给了钱还是介词+Pobj;我给了他。

我该怎么做?任何帮助都非常感谢

到目前为止,在@GeoffreyPoole 的大力帮助下,我已经获得了这份名单。对下面的代码进行一些编辑,输出是;

target <- "root dobj dative pobj"
text %>%
  select(sid, relation, lemma) %>%
  
  # get rid of any sentences with less than three words...
  group_by(sid) %>%
  summarize(n = n()) %>%
  filter(n >= 4) %>%
  left_join(text) %>%
  
  # make sure tokens are in order...
  arrange(sid, tid, lemma) %>%
  
  # now, for each sentence...
  group_by(sid) %>%
  group_modify(
    function(x,y,z) {
      #paste together each triplet of relations and convert to a dataframe.
      rollapply(x[,c("relation", "token")], 4, paste, collapse = " ") %>%
                              as.data.frame
    }
  ) %>% 
  
  # get all unique combinations of sid and pasted triplets
  distinct %>%
  
  # select records with the desired pasted triplet
  filter(relation == target) %>%
  
  # and pull all of the tokens for associated sentences from text
  left_join(text)

sid relation              token                             doc_id   tid token_with_ws lemma upos  xpos  tid_source
   <int> <chr>                 <chr>                              <int> <int> <chr>         <chr> <chr> <chr>      <int>
 1   949 root dobj dative pobj gives ideas to people                 NA    NA NA            NA    NA    NA            NA
 2  1242 root dobj dative pobj provided advantages for customers     NA    NA NA            NA    NA    NA            NA
 3  1631 root dobj dative pobj give harm to themselves               NA    NA NA            NA    NA    NA            NA
 4  2275 root dobj dative pobj say this to us                        NA    NA NA            NA    NA    NA            NA
 5  3016 root dobj dative pobj write fine to you                     NA    NA NA            NA    NA    NA            NA
 6  3826 root dobj dative pobj cause problem for society             NA    NA NA            NA    NA    NA            NA
 7  4184 root dobj dative pobj gives harm to women                   NA    NA NA            NA    NA    NA            NA

只剩下一个问题,我需要编辑target 来查看更多关系吗?例如当target &lt;- "root dobj dative pobj", 结果是

1242 root dobj dative pobj provided advantages for customers

如果实际的句子是,会发生什么

“为the 客户提供优势”

我是否需要将target 重写为"root dobj dative (det) pobj" 才能观察到这些模式?

谢谢。

【问题讨论】:

  • 如果您也提供了否定测试,这样可以验证解决方案并且我们可以确保我们只返回您想要的内容,这将有所帮助
  • 也许只需要提供dput(text) 的值,这样就不需要安装 cleanNLP/spacy 来测试可能的解决方案。这似乎只是一个数据过滤问题,而不是 cleanNLP 特有的任何问题。
  • 嗯,是的,这正是一个数据过滤问题。你能澄清一下“阴性测试”吗?我不确定我是否得到它。
  • 否定测试是不应该匹配的句子。也许一个句子只有三分之二。
  • 我明白了,我会马上编辑我的文字。

标签: r nlp spacy stringr


【解决方案1】:

@Fatih 提出的修改后的问题让我意识到这个问题的答案比我最初发布的更可靠(和有效)。

关键是用词性而不是词素(词)本身来制造“句子”。然后使用regex(例如grepl())查找具有所需模式的“句子”。

以下是一些测试数据:

> text
# A tibble: 16 x 4
     sid   tid token     upos 
   <int> <int> <chr>     <chr>
 1     1     1 When      ADV  
 2     1     2 you       PRON 
 3     1     3 ’re       VERB 
 4     1     4 traveling VERB 
 5     2     1 You       PRON 
 6     2     2 also      ADV  
 7     2     3 see       VERB 
 8     2     4 a         DET  
 9     3     1 These     DET  
10     3     2 strings   NOUN 
11     3     3 of        ADP  
12     3     4 beads     NOUN 
13     4     1 They      PRON 
14     4     2 have      AUX  
15     4     3 been      AUX  
16     4     4 used      VERB 

假设我们要查找模式为“ADV VERB”或“ADV PRON VERB”的句子。正则表达式如下所示:

regex = "ADV (PRON )?VERB"

所以让我们从词性中构建一些“句子”:

library(dplyr)

posSentences = 
  text %>%
  arrange(sid, tid) %>%
  group_by(sid) %>%
  summarize(uposSentence = paste(upos, collapse = " "))

“句子”如下所示:

> posSentences
# A tibble: 4 x 2
    sid uposSentence      
  <int> <chr>             
1     1 ADV PRON VERB VERB
2     2 PRON ADV VERB DET 
3     3 DET NOUN ADP NOUN 
4     4 PRON AUX AUX VERB

你可以看到前两个句子有我们想要的模式。后两个没有。现在只需使用grepl 来查找符合正则表达式的那些:

theAnswer = filter(posSentences, grepl(regex, posSentences$uposSentence))

我们完成了:

> theAnswer
# A tibble: 2 x 2
    sid uposSentence      
  <int> <chr>             
1     1 ADV PRON VERB VERB
2     2 PRON ADV VERB DET 

您可以通过以下方式回到这些句子中的标记:

filter(text, sid %in% theAnswer$sid)

在这种情况下产生:

# A tibble: 8 x 4
    sid   tid token     upos 
  <int> <int> <chr>     <chr>
1     1     1 When      ADV  
2     1     2 you       PRON 
3     1     3 ’re       VERB 
4     1     4 traveling VERB 
5     2     1 You       PRON 
6     2     2 also      ADV  
7     2     3 see       VERB 
8     2     4 a         DET  

上述方法比我在@Fatih 的问题更窄时(寻找三个词性的特定模式)时提供的方法要快得多,也更灵活。所以我以前的答案是没有实际意义的,但我把它留在下面,以防它对任何人有用。


原始答案(针对 3 个值的特定模式)


这是使用dplyr::group_modifyzoo::rollapply 的解决方案。基本上,通过将rollapply 包裹在group_modify 中,您可以将rollapply 包含在每个句子中,并将paste 每个三元组关系组合成一个字符串。然后,只需 filter 即可获得所需的 target 字符串。在运行此代码之前,您可能希望也可能不希望从 text 中删除所有标点符号,具体取决于您的目标。

library(zoo)
library(dplyr)

target = "dobj dative pobj"

text %>%
  select(sid, relation) %>%

  # get rid of any sentences with less than three words...
  group_by(sid) %>%
  summarize(n = n()) %>%
  filter(n >= 3) %>%
  left_join(text) %>%

  # make sure tokens are in order...
  arrange(sid, tid) %>%

  # now, for each sentence...
  group_by(sid) %>%
  group_modify(
    function(x,y) {
      #paste together each triplet of relations and convert to a dataframe.
      rollapply(x[,"relation"], 3, paste, collapse = " ") %>%
        as.data.frame
    }
  ) %>% 

  # get all unique combinations of sid and pasted triplets
  distinct %>%

  # select records with the desired pasted triplet
  filter(relation == target) %>%

  # and pull all of the tokens for associated sentences from text
  left_join(text)

【讨论】:

  • 太棒了!如此绝妙的方式。我现在可以改进您的代码以处理更多数据。非常感谢你。我真的很感谢你的帮助。
  • 我得到的输出是一个表格,其中 sid 和关系信息说明了,是否可以将引理和句子数据添加到表格中?喜欢dobj - dative - pobj == give money to him == I gave money to him
  • 我不确定我理解你想要什么。也许编辑您的原始问题以举例说明您想要的输出?
  • 我解决了我在上一条评论中提出的问题。然而现在我有最后一个问题。我编辑了原始帖子。
  • 可以在rollapply 中使用toString 来替代pastecollpase=
猜你喜欢
  • 2019-04-03
  • 1970-01-01
  • 1970-01-01
  • 2018-05-13
  • 2014-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多