Base R版本:
library(dplyr) # only for pipe operator
rbind(data1, data2) %>%
split(.$NAMES) %>%
lapply(function(x){
data.frame(NAMES = unique(x$NAMES),as.list(colSums(x[,-1])))
}) %>%
do.call(rbind, .)
# NAMES X1 X2
# name1 name1 5 NA
# name2 name2 NA 22
# name3 name3 9 24
请注意,NAMES 现在也显示为行名。这是因为split 输出了一个命名列表。您可以保留行名并删除NAMES = unique(x$NAMES),或者在split 之后添加unname() 管道:
rbind(data1, data2) %>%
split(.$NAMES) %>%
lapply(function(x){
data.frame(as.list(colSums(x[,-1])))
}) %>%
do.call(rbind, .)
# X1 X2
# name1 5 NA
# name2 NA 22
# name3 9 24
rbind(data1, data2) %>%
split(.$NAMES) %>%
unname() %>%
lapply(function(x){
data.frame(NAMES = unique(x$NAMES),as.list(colSums(x[,-1])))
}) %>%
do.call(rbind, .)
# NAMES X1 X2
# 1 name1 5 NA
# 2 name2 NA 22
# 3 name3 9 24
要将 NA 视为零,只需将 na.rm = TRUE 添加到 colSums:
rbind(data1, data2) %>%
split(.$NAMES) %>%
unname() %>%
lapply(function(x){
data.frame(NAMES = unique(x$NAMES),as.list(colSums(x[,-1], na.rm = TRUE)))
}) %>%
do.call(rbind, .)
# NAMES X1 X2
# 1 name1 5 10
# 2 name2 0 22
# 3 name3 9 24
dplyr + purrr 版本:
library(purrr)
library(dplyr)
list(data1, data2) %>%
reduce(function(x, y) cbind(NAMES = x$NAMES, x[,-1] + y[-1]))
结果:
NAMES X1 X2
1 name1 5 NA
2 name2 NA 22
3 name3 9 24
将 NA 视为零:
list(data1, data2) %>%
map(function(x){
modify_if(x, is.numeric, function(y) ifelse(is.na(y), 0, y))
}) %>%
reduce(function(x, y) cbind(NAMES = x$NAMES, x[,-1] + y[-1]))
结果:
NAMES X1 X2
1 name1 5 10
2 name2 0 22
3 name3 9 24
重要提示:
用零替换 NA 通常是个坏主意,因为它们的含义不同。 NA 可能意味着数据丢失,不一定为零,因此用零替换所有 NA 可能会使您的结果产生偏差。请仅在您确定数据上下文中 NA 的平均值为零时才这样做。
附加说明:
-
map 和modify_if 都来自purrr 包。 map 将函数应用于列表的每个元素并始终返回一个列表。 modify 的作用相同,只是它返回与输入相同的类型。
-
modify_if 仅“映射”满足条件的元素。
- 在第一个管道中,我使用
map 将list(data1, data2) 的每个元素与modify_if 函数“映射”在一起,而modify_if 仅将每个数字列的NA 替换为零。这样我就可以在下一个管道中使用 + 运算符,而不必担心 NA。
-
reduce 对 data1 和 data2 进行矩阵加法,然后将 cbinds 与来自 data1 的 NAMES 列一起添加。