R:有条件地合并来自相邻行的文本,同时保留相关信息

R: conditionally combine text from adjacent rows while retaining associated information

脚本需要:

a) 合并相邻行中的文本,相邻行的数量可能会有所不同,要合并的行的分组由第一行前面的 NA 和最后一行后面的 NA 决定,

b) 保留行 ID 以供将来检查

c) 保留与要合并的相邻行中的一行相关联的数值变量

d) 保留整体顺序

我使用 for 循环和使用 dplyr 和 stringer 处理大量数据实现了这一点。

for 循环不够优雅,因为我正在努力处理按顺序识别相邻行的逻辑。这并不重要,因为分组变量只是一个帮手 - 但它让我很恼火。

我也想知道是否有更有效的方法来完全做到这一点,比如使用 rowwise 和 mutate with lead or lag。

如有任何指导或指点,我们将不胜感激。

library(tidyverse)

tib <- tibble(id = 1:11,
              var = c("a", NA, NA, "b", "c" , NA, "d", NA, NA, NA, "e"),
              txt = c( NA, "the", "cat",  NA,  NA, "sat", NA, "on", "the", "mat", NA),
              nr = c( NA,  NA, 5, NA, NA, 10, 7, NA, NA, 15, 11),
              txt_group = NA_integer_)

# txt_group = helper column for text grouping variable

txt_group_counter <- 1L


for(i in seq_len(nrow(tib))){

  if (!is.na(tib$txt[i]) | !is.na(lag(tib$txt[i]))){

    tib$txt_group[i] <- txt_group_counter
   } 

  if(is.na(tib$txt[i]) | !is.na(lead(tib$txt[i]))){

    txt_group_counter <- txt_group_counter + 1
  }

}


tib1 <- 
  tib %>%
  filter(!is.na(txt_group)) %>% 
  group_by(txt_group) %>% 
  mutate(id_comb = paste(id, collapse = ", "),
         txt = paste(txt, collapse = " "),
         nr = paste(nr, collapse = "")) %>% 
  select(-id) %>% 
  distinct() %>% 
  ungroup() %>% 
  mutate(id = as.numeric(str_extract(id_comb, "^\d")),
         nr = as.numeric(str_remove_all(nr, "[NA]"))) %>% 
  select(id, id_comb, everything()) %>% 
  bind_rows(tib %>% filter(is.na(txt_group))) %>% 
  arrange(id) %>% 
  select(-txt_group)

以下使用使用标准 cumsum/diff 技巧创建的辅助变量来定义组,然后 paste 将行放在一起。

代码执行以下操作:

  1. 创建一个逻辑变量eq,判断var的两个连续值是否相等。由于第一个不能等于之前(在它不存在之前)我用 FALSE.
  2. 填充
  3. 一些值是 NA,将它们替换为 FALSE,所有 NA 都不同于其他所有值,包括其他 NA
  4. 现在有一个 cumsum 技巧,在有 TRUE 的地方设置断点(var 与下一个值不同,请参见上面的第 1 点),就像运行 数。这通过 var.
  5. 的变化给出了分组
  6. is.na(var)基本相同的技巧。 cumsum 对于创建分组向量非常有用,值得在 R 技巧包中占有一席之地。
  7. mutate_at 删除了 NA 值,它们将在合并行时重复 NA NA。像这样,它是 "" 个组合的空字符串。
  8. okeq 分组并与 paste 组合。 trimws 可能不需要,但也没什么坏处,除非数据集非常大并且代码要针对时间进行优化。
  9. 取消分组并删除创建的临时列;用逗号替换结果中的空格。

这里是:

tib %>% 
  mutate(eq = c(FALSE, var[-length(var)] != var[-1]),
         eq = ifelse(is.na(eq), FALSE, eq),
         eq = cumsum(abs(c(diff(eq), 0))),
         ok = cumsum(abs(c(0, diff(is.na(var)))))) %>%
  mutate_at(vars(var:txt_group), list(function(x) ifelse(is.na(x), "", x))) %>%
  group_by(ok, eq) %>% 
  summarise_all(funs(trimws(paste(., collapse = " ")))) %>%
  ungroup() %>%
  select(-ok, -eq) %>%
  mutate(id = gsub(" ", ",", id),
         var = gsub(" ", ",", var))
## A tibble: 8 x 5
#  id     var   txt          nr    txt_group
#  <chr>  <chr> <chr>        <chr> <chr>    
#1 1      "a"   ""           ""    ""       
#2 2,3    ""    "the cat"    "5"   ""       
#3 4      "b"   ""           ""    ""       
#4 5      "c"   ""           ""    ""       
#5 6      ""    "sat"        "10"  ""       
#6 7      "d"   ""           "7"   ""       
#7 8,9,10 ""    "on the mat" "15"  ""       
#8 11     "e"   ""           "11"  ""