填写字母所在的单词

Fill in word that letter is located in

我正在处理击键数据,需要找到击键所在的单词。因为可能存在不可见的击键(如 Shift)或删除的击键,所以这不是一个简单的问题,我可以只迭代击键的索引并找到单词。相反,我需要找到击键产生的 space 分隔的单词。我确实有全文和现有文本可用,我应该能够利用它们。我尝试过使用 fill()lag()cumsum() 的解决方案,但 none 有效。

我有一个如下所示的数据框,我按 experiment_id:

分组
x <- tibble(
  experiment_id = rep(c('1a','1b'),each=12),
  keystroke = rep(c('a','SPACE','SHIFT','b','e','DELETE','a','d','SPACE','m','a','n'),2),
  existing_text = rep(c('a','a ','a ','a B','a Be','a B','a Ba','a Bad','a Bad ',
                    'a Bad m','a Bad ma','a Bad man'),2),
  final_text = 'a Bad man'
)

附加列应如下所示,其中 SPACE 属于它后面的单词,DELETE 和删除的击键是最后一个单词的一部分:

within_word = c('a','a','BeDELETEad','BeDELETEad','BeDELETEad','BeDELETEad','BeDELETEad','BeDELETEad','BeDELETEad','man','man','man')

有没有办法得出这个?

编辑以获得更多帮助: 在答案下方的评论中,@Onyambu 提到使用 keystroke 列有一个更简单的解决方案。我发现在我更大、更复杂的数据中 existing_text 并不总是可靠的。我强烈希望主要依赖 keystroke 的解决方案。由于删除,我还添加了并发症。

x %>%
  mutate(ww = str_remove(existing_text, fixed(lag(existing_text, default = ".")))) %>%
  group_by(grp = cumsum(ww== ' '|lag(ww == ' ', default = F))) %>%
  mutate(within_word = str_c(ww, collapse = ''),
         within_word = na_if(within_word, ' '))

# A tibble: 10 x 6
# Groups:   grp [5]
   keystroke existing_text final_text ww      grp within_word
   <chr>     <chr>         <chr>      <chr> <int> <chr>      
 1 a         "a"           a Bad man  "a"       0 a          
 2 SPACE     "a "          a Bad man  " "       1 NA         
 3 SHIFT     "a "          a Bad man  ""        2 Bad        
 4 b         "a B"         a Bad man  "B"       2 Bad        
 5 a         "a Ba"        a Bad man  "a"       2 Bad        
 6 d         "a Bad"       a Bad man  "d"       2 Bad        
 7 SPACE     "a Bad "      a Bad man  " "       3 NA         
 8 m         "a Bad m"     a Bad man  "m"       4 man        
 9 a         "a Bad ma"    a Bad man  "a"       4 man        
10 n         "a Bad man"   a Bad man  "n"       4 man        

以下是两种方法:

第一个仅使用 existing_text 中的信息进行分组,并根据此分组和 keystroke.

构造 within_words

第二种方法仅使用 keystroke 中的信息。


第一种方法:基于 existing_text 的分组和基于 keystroke 的内容:

我们采取三个步骤:

首先,我们根据 strsplit 计算分组,我们在其中查找前面有单词 \w 的空格 \s。我们需要更正 "SHIFT" 的值,因为它们应该计入 "SPACE" 之后的单词。

第二步是将 "SHIFT"(以及示例数据不包含的所有其他类似函数)替换为 ""

第三,我们用paste0(..., collapse = "")折叠字符串。

library(tidyverse)

x %>%

  # step1: construct grouping:
  mutate(word_grp = lengths(strsplit(existing_text, "(?<=\w)\s", perl = TRUE)) %>% 
           if_else(keystroke == "SHIFT", lead(., default = last(.)), .)) %>%
  group_by(experiment_id, word_grp) %>% 

  # step 2 & 3: first replace keys like "SHIFT" with "", the collapse with `paste0`
  mutate(within_word = str_replace_all(keystroke, c("SHIFT" = "", "SPACE" = "")) %>% 
           paste0(., collapse = ""))

#> # A tibble: 24 x 6
#> # Groups:   experiment_id, word_grp [6]
#>    experiment_id keystroke existing_text final_text word_grp within_word
#>    <chr>         <chr>     <chr>         <chr>         <int> <chr>      
#>  1 1a            a         "a"           a Bad man         1 a          
#>  2 1a            SPACE     "a "          a Bad man         1 a          
#>  3 1a            SHIFT     "a "          a Bad man         2 beDELETEad 
#>  4 1a            b         "a B"         a Bad man         2 beDELETEad 
#>  5 1a            e         "a Be"        a Bad man         2 beDELETEad 
#>  6 1a            DELETE    "a B"         a Bad man         2 beDELETEad 
#>  7 1a            a         "a Ba"        a Bad man         2 beDELETEad 
#>  8 1a            d         "a Bad"       a Bad man         2 beDELETEad 
#>  9 1a            SPACE     "a Bad "      a Bad man         2 beDELETEad 
#> 10 1a            m         "a Bad m"     a Bad man         3 man        
#> # … with 14 more rows


第二种方法:仅基于 keystrokes 中的信息。

这是一种仅使用 keystroke 中的信息的方法。但是,如果我们只想使用 keystroke 中的数据,事情就会变得更加费力。

以下是对以下步骤的简短说明:

步骤 1a:数据清理
我们需要清理 keystrokes 中的数据,以便它们可用于新列 within_word。这意味着两件事:(a) 我们需要用 "" 替换每个不应在 within_word 中打印的击键。在此之前,我们需要 (b) 根据该键的功能更改前导击键。在 SHIFT 的情况下,这意味着我们需要设置前导 keystroke toupper。对于您的示例数据,这非常简单,因为只有 SHIFT 我们需要处理。但是,在您的真实数据中可能有许多类似的其他键,例如 ALT^。所以我们需要为每个键重复步骤 1a。理想情况下,我们会想出一个函数,该函数采用键的名称和它在前导 keystroke 上使用的函数。请注意,我们尚未在此步骤中包含 "SPACE",因为我们在步骤 2 中需要它。

要查看您需要在实际数据中处理多少个键,我们可以过滤那些 keystroke 不会更改 existing_text 的键。在您的示例数据中,这只是 SHIFT:

# get all keystrokes that don't change the existing_text directly
x %>% 
  select(keystroke, existing_text) %>% 
  filter(existing_text == lag(existing_text, default = ""))

#> # A tibble: 2 x 2
#>   keystroke existing_text
#>   <chr>     <chr>        
#> 1 SHIFT     "a "         
#> 2 SHIFT     "a "

步骤 2:创建分组
我们需要在 within_text 中创建单词分组。这是最复杂的一步。下面我们首先查找 within_word == "SPACE" 以及后续行是 != "SPACE" 的行。我们在结果上使用 data.table::rleid 以获得此变量的 运行-length id。最后,我们需要为那些 within_word == "SPACE".

的行减去 1

第 3 步:最后一步前的数据准备
这基本上类似于步骤 1a,我们需要用 "" 替换 "SPACE" 因为我们不希望它出现在我们的结果中。但是,由于我们在步骤 2 中需要此列,因此我们必须在此步骤中完成数据清理。

第 4 步:折叠 within_word
中的字符串 最后,我们按 experiment_idword_grp 分组,并用 paste0(..., collapse = "").

折叠 within_word 中的字符串
library(tidyverse)

  # step 1a: data cleaning
  mutate(within_word = if_else(lag(keystroke, default = first(keystroke)) == "SHIFT",
                               toupper(keystroke),
                               keystroke) %>%
                          str_replace_all(., c("SHIFT" = ""))) %>%  
 
  # step 1b to 1n: repeat step 1a for other keys like ALT, ^ etc. 

  # step 2: create groups
  group_by(experiment_id) %>% 
  mutate(word_grp = data.table::rleid(
      within_word == "SPACE" & lead(within_word, default = first(keystroke)) != "SPACE"
    ) %>% if_else(within_word == "SPACE", . - 1L, .)) %>% 

  # step 3: data prep before final step
  ungroup %>% 
  mutate(within_word = str_replace(within_word, "SPACE", "")) %>%
 
  # step 4: collapse
  group_by(experiment_id, word_grp) %>% 
  mutate(within_word = paste0(within_word, collapse = ""))

#> # A tibble: 24 x 6
#> # Groups:   experiment_id, word_grp [6]
#>    experiment_id keystroke existing_text final_text within_word word_grp
#>    <chr>         <chr>     <chr>         <chr>      <chr>          <int>
#>  1 1a            a         "a"           a Bad man  a                  1
#>  2 1a            SPACE     "a "          a Bad man  a                  1
#>  3 1a            SHIFT     "a "          a Bad man  BeDELETEad         3
#>  4 1a            b         "a B"         a Bad man  BeDELETEad         3
#>  5 1a            e         "a Be"        a Bad man  BeDELETEad         3
#>  6 1a            DELETE    "a B"         a Bad man  BeDELETEad         3
#>  7 1a            a         "a Ba"        a Bad man  BeDELETEad         3
#>  8 1a            d         "a Bad"       a Bad man  BeDELETEad         3
#>  9 1a            SPACE     "a Bad "      a Bad man  BeDELETEad         3
#> 10 1a            m         "a Bad m"     a Bad man  man                5
#> # … with 14 more rows

reprex package (v0.3.0)

于 2021-12-23 创建