使用NA值和因子对两个数据帧求和

时间:2017-09-14 14:11:09

标签: r

我正在尝试将两个包含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

可能吗?

4 个答案:

答案 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在您的数据环境中的平均值为零,请仅执行此操作。

附加说明:

  1. mapmodify_if都来自purrr包。 map将函数应用于列表的每个元素,并始终返回列表。 modify执行相同的操作,只是它返回与输入相同的类型。
  2. modify_if仅限&#34;地图&#34;满足条件的元素。
  3. 在第一个管道中,我使用map来&#34;映射&#34;具有list(data1, data2)函数的modify_if的每个元素,而modify_if仅用每个数字列替换NA为零。这样我就可以在下一个管道中使用+运算符而无需担心NA。
  4. reducedata1data2上添加了矩阵,然后在cbind的{​​{1}}列NAMES添加矩阵。