使用 dplyr、group_by 与 mutate() 或 summarise() & str_c() 或 paste() & 折叠连接字符串/行,但保持 NA & 所有字符

Posted

技术标签:

【中文标题】使用 dplyr、group_by 与 mutate() 或 summarise() & str_c() 或 paste() & 折叠连接字符串/行,但保持 NA & 所有字符串【英文标题】:Concatenating strings / rows using dplyr, group_by with mutate() or summarize() & str_c() or paste() & collapse, but maintain NA & all strings 【发布时间】:2021-11-17 00:46:13 【问题描述】:

当使用dplyrgroup_by()mutate()summarize ()paste()collapse 连接字符串时,NA 值被强制转换为字符串"NA"

当使用str_c() 而不是paste() 时,与NA 连接的字符串将被删除(?str_c当缺失值与另一个字符串组合时,结果将始终缺失)。当具有NA 和非NA 值的此类组合时,如何删除串联中的NA 而不是非NA

请参阅下面的示例:

library(dplyr)
library(stringr)
ID <- c(1,1,2,2,3,4)
string <- c(' asfdas ', 'sdf', NA,'sadf', 'NA', NA)
df <- data.frame(ID, string)
#   ID   string
# 1  1  asfdas 
# 2  1      sdf
# 3  2     <NA> # ID 2 has both NA and non-NA values
# 4  2     sadf #
# 5  3       NA
# 6  4     <NA>

两者,

df%>%
 group_by(ID)%>%
 summarize(string = paste(string, collapse = "; "))%>%
 distinct_all()

df_conca <-df%>%
 group_by(ID)%>%
 dplyr::mutate(string = paste(string, collapse = "; "))%>%
 distinct_all()

结果

     ID string               
1     1 " asfdas ; sdf"
2     2 "NA; sadf"           
3     3 "NA"
4     4 "NA" # NA coerced to "NA"

NA 变为“NA”:

同时

df %>%
  group_by(ID)%>%
  summarize(string = str_c(string, collapse = "; "))

结果:

     ID string               
1     1 " asfdas ; sdf"
2     2 NA     
3     3 "NA" 
4     4 NA 

即根据str_c规则删除“sadf”:NA结合字符串,结果为NA

但是,我想保留真正的 NA 值(例如“ID”4)和字符串(例如“ID”2),如下所示:

     ID string             
1     1 " asfdas ; sdf"
2     2 "sadf"           
3     3 "NA"
4     4 NA 

理想情况下,我希望留在dplyr 工作流程中。


这个问题是Concatenating strings / rows using dplyr, group_by & collapse or summarize, but maintain NA values的延伸

【问题讨论】:

也许df %&gt;% group_by(ID) %&gt;% summarise(str_c(if(any(!is.na(string))) na.omit(string) else string, collapse = ",")) 【参考方案1】:

使用pivot_widerunite

library(dplyr)
library(tidyr)
library(data.table)
df %>% 
   mutate(rn = rowid(ID)) %>%
   pivot_wider(names_from = rn, values_from = string) %>% 
   unite(string, `1`, `2`, na.rm = TRUE, sep = " ; ")%>%
   mutate(string = na_if(string, ""))

-输出

# A tibble: 4 x 2
     ID string          
  <dbl> <chr>           
1     1 " asfdas  ; sdf"
2     2 "sadf"          
3     3 "NA"            
4     4  <NA>         

或者也可以使用coalesce

df %>%
    group_by(ID) %>%
    summarise(string = na_if(coalesce(str_c(string, collapse = " ; "),
     str_c(string[complete.cases(string)], collapse = " ; ")), ""))

-输出

# A tibble: 4 x 2
     ID string          
  <dbl> <chr>           
1     1 " asfdas  ; sdf"
2     2 "sadf"          
3     3 "NA"            
4     4  <NA>          

【讨论】:

与第一种解决方案相比,第二种解决方案需要 1/10 的处理时间。它的代码强度也较低。【参考方案2】:

这是 dplyr 框架中的一个解决方案。 这将使用 filter() 删除所有 'NA' 值 - 它最初会丢失 ID 4 - 然后使用连接替换丢失的 ID。

df_IDs <- data.frame(ID = unique(df$ID))
df%>%
  group_by(ID)%>%
  filter(!is.na(string)) %>%
  summarize(string = paste(string, collapse = "; ")) %>%
  full_join(df_IDs, by = "ID")

结果

     ID string                  
1     1 " asfdas ; sdf"
2     2 "sadf"         
3     3 "NA"           
4     4  NA  

【讨论】:

【参考方案3】:

感谢大家的努力,同时我想出了自己的答案:

 replace(is.na(.),'XXX_MY_NAs_XXXX')%>%
 group_by(ID)%>%
 summarize(string = paste(string, collapse = "; "))%>%
 dplyr::mutate_all(funs(str_replace_all(., c('XXX_MY_NAs_XXXX; ' = ''
                                          ,'; XXX_MY_NAs_XXXX' = ''))))%>%
 na_if(., 'XXX_MY_NAs_XXXX')

【讨论】:

【参考方案4】:

那么,获得已接受答案认可的最佳答案是什么?

我放大了样本数据并运行了一个简短的基准测试。

ID <- sample(1:4, 1000000, replace = T)
string <-  sample(c(' asfdas ', 'sdf', NA,'sadf', 'NA', NA), 1000000, replace = T)
df <- data.frame(ID, string)

获胜者是阿昆的第二个答案。最短的代码和最短的处理时间。但是,处理时间仅相差几毫秒(除了 arkun 的第一个答案,它需要十倍)。

df %>%
    group_by(ID) %>%
    summarise(string = na_if(coalesce(str_c(string, collapse = " ; "),
     str_c(string[complete.cases(string)], collapse = " ; ")), ""))

无论如何,我想应该可以在堆栈交换中接受多个答案,因为不同的答案可能在不同的情况下效果最好。

此外,dplyr::mutate(string = paste(string, collapse = "; ")) 的行为对我来说似乎很意外,值得通过一些 dplyr 更新来改变。

【讨论】:

以上是关于使用 dplyr、group_by 与 mutate() 或 summarise() & str_c() 或 paste() & 折叠连接字符串/行,但保持 NA & 所有字符的主要内容,如果未能解决你的问题,请参考以下文章

了解 dplyr 和 group_by

R语言dplyr包使用arrange函数group_by函数mutate函数生成分组数据的排名(rank)实战(Rank Variable by Group):升序排名降序排名以及相同排名的处理

R语言使用dplyr包使用group_by函数summarise函数和mutate函数计算分组下的均值标准差样本个数以及分组均值的95%执行区间对应的下限值和上限值(Calculate CI)

使用 group_by(多个变量)时的 dplyr 问题

R使用dplyr group_by / sum for循环,作为连接列表输出

dplyr:为什么个人计数摘要和索引摘要有所不同