Ant*_*nce 8 r coalesce dataframe dplyr purrr
我想用来dplyr::coalesce查找包含多对变量的数据框中变量对之间的第一个非缺失值。目标是创建一个新的数据帧,现在每对变量只有一个副本(没有 NA 值的合并变量)。
这是一个例子:
df <- data.frame(
A_1=c(NA, NA, 3, 4, 5),
A_2=c(1, 2, NA, NA, NA),
B_1=c(NA, NA, 13, 14, 15),
B_2=c(11, 12, NA, NA, NA))
Expected output:
A B
1 11
2 12
3 13
4 14
5 15
Run Code Online (Sandbox Code Playgroud)
我猜测可以使用基于正则表达式的混合dplyr::coalesce,dplyr::mutate_at但我不知道该怎么做。有没有办法用 tidyverse 语法来完成这个任务?
谢谢!
编辑:感谢大家的回答!但是,我应该包含变量的命名约定,以便于将您的答案转移到我的实际问题上。对此我感到很抱歉。我的变量是地球化学变量,由两部分命名(化学元素名称加核心名称)。
示例:Al_TAC4.25.275其中Al是元素,TAC4.25.275是核心。我想为每个元素(名称的第一部分)合并来自 3 个不同核心(名称的第二部分)的数据。我有 25 对元素需要合并。
你可以使用transmute,例如
\nlibrary(dplyr)\n\ndf <- data.frame(\n A_1 = c(NA, NA, 3, 4, 5),\n A_2 = c(1, 2, NA, NA, NA),\n B_1 = c(NA, NA, 13, 14, 15),\n B_2 = c(11, 12, NA, NA, NA)\n )\n\ndf %>%\n transmute(A = coalesce(A_1, A_2),\n B = coalesce(B_1, B_2))\n#> A B\n#> 1 1 11\n#> 2 2 12\n#> 3 3 13\n#> 4 4 14\n#> 5 5 15\nRun Code Online (Sandbox Code Playgroud)\n由reprex 包于 2021 年 12 月 22 日创建(v2.0.1)
\n另一种选择,如果您有很多“A_*”和“B_*”列(来源:Romain Fran\xc3\xa7ois,用户:@Romain Francois):
\nlibrary(dplyr)\n\ndf <- data.frame(\n A_1 = c(NA, NA, 3, 4, 5),\n A_2 = c(1, 2, NA, NA, NA),\n B_1 = c(NA, NA, 13, 14, 15),\n B_2 = c(11, 12, NA, NA, NA)\n )\n\ncoacross <- function(...) {\n coalesce(!!!across(...))\n}\n\ndf %>%\n transmute(A = coacross(starts_with("A_")),\n B = coacross(starts_with("B_")))\n#> A B\n#> 1 1 11\n#> 2 2 12\n#> 3 3 13\n#> 4 4 14\n#> 5 5 15\nRun Code Online (Sandbox Code Playgroud)\n由reprex 包于 2021 年 12 月 22 日创建(v2.0.1)
\n根据您更新的问题,您没有大量的“A_*”或“B_*”列,而是大量的“*_1”、“*_2”和“*_3”列。我认为这是适合您的用例的最直接的解决方案:
\nlibrary(dplyr)\n\ndf <- data.frame(Al_TAC4.25.275 = c(1, 1, 1, NA, NA, NA),\n Al_TAC4.25.276 = c(NA, NA, 2, 2, 2, NA),\n Al_TAC4.25.277 = c(NA, NA, 3, NA, NA, 3),\n Au_TAC4.25.275 = c(1, 1, 1, NA, NA, NA),\n Au_TAC4.25.276 = c(NA, NA, 2, 2, 2, NA),\n Au_TAC4.25.277 = c(NA, NA, 3, NA, NA, NA),\n Ar_TAC4.25.275 = c(1, 1, 1, NA, NA, 1),\n Ar_TAC4.25.276 = c(NA, NA, 2, 2, 2, 2),\n Ar_TAC4.25.277 = c(NA, NA, 3, NA, NA, 3))\n\ndf\n#> Al_TAC4.25.275 Al_TAC4.25.276 Al_TAC4.25.277 Au_TAC4.25.275 Au_TAC4.25.276\n#> 1 1 NA NA 1 NA\n#> 2 1 NA NA 1 NA\n#> 3 1 2 3 1 2\n#> 4 NA 2 NA NA 2\n#> 5 NA 2 NA NA 2\n#> 6 NA NA 3 NA NA\n#> Au_TAC4.25.277 Ar_TAC4.25.275 Ar_TAC4.25.276 Ar_TAC4.25.277\n#> 1 NA 1 NA NA\n#> 2 NA 1 NA NA\n#> 3 3 1 2 3\n#> 4 NA NA 2 NA\n#> 5 NA NA 2 NA\n#> 6 NA 1 2 3\n\nnames(df) %>% \n split(str_extract(., \'[:alpha:]+\')) %>%\n map_dfc(~ coalesce(!!!df[.x][c(1,2,3)]))\n#> # A tibble: 6 \xc3\x97 3\n#> Al Ar Au\n#> <dbl> <dbl> <dbl>\n#> 1 1 1 1\n#> 2 1 1 1\n#> 3 1 1 1\n#> 4 2 2 2\n#> 5 2 2 2\n#> 6 3 1 NA\n\n# change the order of the list to change the \'priority\'\nnames(df) %>% \n split(str_extract(., \'[:alpha:]+\')) %>%\n map_dfc(~ coalesce(!!!df[.x][c(3,2,1)]))\n#> # A tibble: 6 \xc3\x97 3\n#> Al Ar Au\n#> <dbl> <dbl> <dbl>\n#> 1 1 1 1\n#> 2 1 1 1\n#> 3 3 3 3\n#> 4 2 2 2\n#> 5 2 2 2\n#> 6 3 3 NA\n\nnames(df) %>% \n split(str_extract(., \'[:alpha:]+\')) %>%\n map_dfc(~ coalesce(!!!df[.x][c(2,1,3)]))\n#> # A tibble: 6 \xc3\x97 3\n#> Al Ar Au\n#> <dbl> <dbl> <dbl>\n#> 1 1 1 1\n#> 2 1 1 1\n#> 3 2 2 2\n#> 4 2 2 2\n#> 5 2 2 2\n#> 6 3 2 NA\nRun Code Online (Sandbox Code Playgroud)\n由reprex 包于 2021 年 12 月 22 日创建(v2.0.1)
\n与我的另一个解决方案相比,这是另一个更简洁的解决方案。我认为使用cur_data()function 非常有帮助,但您也可以across(everything())在其位置使用:
library(dplyr)
library(purrr)
unique(sub("(\\D)_\\d+", "\\1", names(df))) %>%
map_dfc(~ df %>%
select(starts_with(.x)) %>%
summarise(!!.x := do.call(coalesce, cur_data())))
A B
1 1 11
2 2 12
3 3 13
4 4 14
5 5 15
Run Code Online (Sandbox Code Playgroud)
这是针对尽可能多的对的另一种解决方案。请注意,我使用bang bang运算符 ( !!!) 来将数据帧的元素折叠为独立的单个参数,以便我可以应用coalesce它们:
library(dplyr)
library(rlang)
as.data.frame(do.call(cbind, lapply(split.default(df, sub("(\\D)_\\d+", "\\1", names(df))), function(x) {
coalesce(!!!x)
})))
A B
1 1 11
2 2 12
3 3 13
4 4 14
5 5 15
Run Code Online (Sandbox Code Playgroud)
基础 R 选项
list2DF(
lapply(
split.default(df, gsub("_.*", "", names(df))),
rowSums,
na.rm = TRUE
)
)
Run Code Online (Sandbox Code Playgroud)
给出
A B
1 1 11
2 2 12
3 3 13
4 4 14
5 5 15
Run Code Online (Sandbox Code Playgroud)