Subtract/find 2个不同列数的数据框的区别

Subtract/find the difference between 2 data frames with different numbers of columns

我对 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))会将2个数据帧加在一起产生

  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

我试过用 diff 替换函数中的 sum 但没有用

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

编辑 - 我意识到该函数包括对我保存这些数据框的列表的引用并对其进行了更改。

跟进:处理 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 值,但了解答案是否有调整会很有用可以解决这个问题。

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

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

然后做一个连接 on the 'Measure', 'Filename', 从 'df1' 和相应的列中获取列 ('nm1') 的值在 'df2'。在这里,它将是 i.,因为 'df2' 位于第 i 个位置(data.table 遵循相同的格式 [i, j, by]。通过使用 mget ,它 returns 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相比的任意值returns 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

如果您将 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