我正在尝试将两个包含NA值和因子变量的数据帧相加。这些值应逐个单元求和,仅用于非因子变量,并忽略NA。
例如,我正在处理的数据帧如下:
data1 <- data.frame(NAMES=c("name1", "name2", "name3"),
X1=c(1, NA, 3),
X2 = c(10, 11, 12))
data2 <- data.frame(NAMES=c("name1", "name2", "name3"),
X1=c(4,NA,6),
X2 = c(NA, 11, 12))
如果我使用函数sum (..., na.rm=TRUE)
,我不会得到数据帧。我希望通过使用运算符+
返回输出。也就是说,具有相同行数和列数的单个数据帧,但每个单元格是在加数数据帧的相同坐标中添加单元格的结果。但是,需要忽略NAs和因素,例如:
NAMES X1 X2
1 name1 5 10
2 name2 NA 22
3 name3 9 24
可能吗?
答案 0 :(得分:1)
这是使用data.table
的解决方案。
require(data.table)
data1 <- data.table(NAMES = c("name1", "name2", "name3"),
X1 = c(1, NA, 3),
X2 = c(10, 11, 12))
data2 <- data.table(NAMES = c("name1", "name2", "name3"),
X1 = c(4, NA, 6),
X2 = c(NA, 11, 12))
dat <- rbind(data1, data2)
dat[, lapply(.SD, sum, na.rm = T), keyby = "NAMES", .SDcols = c("X1", "X2")]
请注意,结果与您在问题中显示的结果并不完全相同。希望它仍然有用。
> dat[, lapply(.SD, sum, na.rm = T), keyby = "NAMES", .SDcols = c("X1", "X2")]
NAMES X1 X2
1: name1 5 10
2: name2 0 22
3: name3 9 24
答案 1 :(得分:1)
另一种选择:
as.data.frame(
mapply(function(x, y)
if(is.numeric(x) && is.numeric(y))
ifelse(is.na(y), x, x + y)
else x,
data1, data2, SIMPLIFY = FALSE))
输出:
NAMES X1 X2
1 name1 5 10
2 name2 NA 22
3 name3 9 24
答案 2 :(得分:1)
这是仅使用基础R的解决方案。
icol <- which(!(sapply(data1, is.factor) | sapply(data2, is.factor)))
result <- sapply(icol, function(i) rowSums(cbind(data1[i], data2[i]), na.rm = TRUE))
result <- cbind(data1[1], result)
is.na(result[icol]) <- is.na(data1[icol]) & is.na(data2[icol])
result
# NAMES X1 X2
#1 name1 5 10
#2 name2 NA 22
#3 name3 9 24
答案 3 :(得分:1)
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现在也显示为rownames。这是因为split
输出了一个命名列表。您可以保留rownames并删除NAMES = unique(x$NAMES)
,也可以在unname()
之后添加split
管道:
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&#39}视为零,只需将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&#39视为零:
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
仅限&#34;地图&#34;满足条件的元素。 map
来&#34;映射&#34;具有list(data1, data2)
函数的modify_if
的每个元素,而modify_if
仅用每个数字列替换NA为零。这样我就可以在下一个管道中使用+
运算符而无需担心NA。reduce
在data1
和data2
上添加了矩阵,然后在cbind
的{{1}}列NAMES
添加矩阵。