减去/查找具有不同列数的2个数据帧之间的差异

时间:2019-02-07 15:24:26

标签: r dataframe dplyr difference subtraction

我是R的新手,却找不到能够回答该查询的地方。

我有2个数据框,它们具有相同的行数但具有不同的列数。我想减去匹配列中的值以标识2个数据帧之间的差异。

例如,这两个数据框类似于我正在使用的数据框:

df1<-data.frame(Measure=rep("test",3),Filename=c("filename1","filename2","filename3"),Op1=c(79,72,95),Op2=c(NA,NA,5),Op3=c(75,64,66),Op4=c(86,71,58))

   Filename  Op1  Op2  Op3  Op4
1  filename1  79   NA   75   86
2  filename2  72   NA   64   71
3  filename3  95   5    66   58

df2<-data.frame(Measure=rep("test",3),Filename=c("filename1","filename2","filename3"),Op1=c(9,NA,5),Op4=c(80,70,50))

   Filename  Op1  Op4
1  filename1   9   80
2  filename2   NA  70
3  filename3   5   50

当前,我有一个函数可以融合2个数据帧,并对看起来像这样的数据求和:

CalcFunSum<-function(MeasureName,BoxNumbers){
  temp<-data.frame()
  for (i in BoxNumbers){
    data<-melt(BoxNumbers[i])
    temp<-temp %>% bind_rows(data)
  }
  temp<-cbind(Measure = MeasureName,dcast(temp,Filename~variable,sum,fill = 0))
  temp
}

所以CalcFunSum(test,c(df1,df2))将把这两个数据帧相加并产生

  Measure  Filename  Op1  Op2  Op3  Op4
1  test   filename1  88   NA   75   166
2  test   filename2  72   NA   64   141
3  test   filename3  100  5    66   108

我想要执行类似于df1-df2的计算以获得以下内容:

  Measure  Filename  Op1  Op2  Op3  Op4
1  test   filename1  70   NA   75   6
2  test   filename2  72   NA   64   1
3  test   filename3  90   5    66   8

我尝试用sum替换函数中的diff,但这没用

有什么想法可以解决这个问题吗?

编辑-我意识到该功能包括对列表的引用,我将这些数据帧保存在其中并进行了更改。

跟进:处理NA值

到目前为止,答案一直有效,但是在测试我的实际数据时,我注意到在df2具有NA但df1具有值的情况下,结果输出包含NA而不是df1中的值。我将df2中的值之一更改为NA以反映这一点。

在@akrun和@IceCreamToucan的当前答案中,输出将为

  Measure  Filename  Op1  Op2  Op3  Op4
1  test   filename1  70   NA   75   6
2  test   filename2  NA   NA   64   1
3  test   filename3  90   5    66   8

我认为这是代码中某个地方的NA.rm = T,或者我需要在处理过程的早期处理NA值,但是知道对这些答案的调整是否会有所帮助将很有用

2 个答案:

答案 0 :(得分:2)

这是使用data,table进行联接的一个选项。获取两个数据集中的公用列名(intersect)并删除比较不需要的名称(setdiff

library(data.table)
nm1 <- setdiff(intersect(names(df1), names(df2)), c("Measure", "Filename"))

然后进行on'度量','文件名'的联接,从'df1'和'df2'中的对应列获取列(nm1')的值。在这里,它将是i.,因为'df2'在第i位置(data.table遵循相同的格式[i, j, by]。通过使用mget,它返回list中的列,我们用-得到两组列的差(Map),并通过分配(:=)反映原始值的值来更新数据集('df1')

setDT(df1)[df2, (nm1) := Map(`-`, mget(nm1),
           mget(paste0("i.", nm1))), on = .(Measure, Filename)]
df1
#   Measure  Filename Op1 Op2 Op3 Op4
#1:    test filename1  70  NA  75   6
#2:    test filename2  70  NA  64   1
#3:    test filename3  90   5  66   8

更新

任何与NA相比的值都将返回NA,同样,

72-NA
#[1] NA

为避免此问题,我们可以将replaceNA设为0,然后进行求和

setDT(df1)[df2, (nm1) := Map(function(x, y) replace(x, is.na(x), 0) - 
     replace(y, is.na(y), 0), 
    mget(nm1),mget(paste0("i.", nm1))), on = .(Measure, Filename)]

df1
#   Measure  Filename Op1 Op2 Op3 Op4
#1:    test filename1  70  NA  75   6
#2:    test filename2  72  NA  64   1
#3:    test filename3  90   5  66   8

答案 1 :(得分:1)

如果您要比较df1中的一行与df2中的同一行,即匹配索引而不检查某些连接列是否相等,则可以从中减去df2[common_columns] df1[common_columns],然后将结果分配回df1(或副本)。

common <- intersect(names(df1), names(df2))[-(1:2)]
new <- df1 # or copy(df1) if df1 is a data.table
new[common] <- df1[common] - df2[common]


new
#   Measure  Filename Op1 Op2 Op3 Op4
# 1    test filename1  70  NA  75   6
# 2    test filename2  70  NA  64   1
# 3    test filename3  90   5  66   8

编辑: 如果df2中的某些值为NA,则可以在减去之前将replace设为0。

common <- intersect(names(df1), names(df2))[-(1:2)]
new <- df1
new[common] <- new[common] - replace(df2[common], is.na(df2[common]), 0)


new
#   Measure  Filename Op1 Op2 Op3 Op4
# 1    test filename1  70  NA  75   6
# 2    test filename2  72  NA  64   1
# 3    test filename3  90   5  66   8