减去两个具有不同行数(匹配 XYZ)的非常大的数据帧的有效方法

efficient way substracting two very large data frames with different number of rows (matching XYZ)

我有一对非常大的 data.frames df1 和 df2(>500,000 行),行数不同,它们都包含相同的 4 列(X、Y、Z 坐标和计数属性)。末日例子:

df1<-data.frame(x=c(3,5,2,4),y=c(8,5,7,6),z=c(13,15,12,10),
      count=c(10,20,4,12))
df2<-data.frame(x=c(4,3,6),y=c(6,9,8),z=c(10,13,15),count=c(4,7,3))

我只想为匹配 XYZ(相同空间点)的行减去计数列 (df1$count - df2$count)。我找到了一种使用函数 merge() {base} 来完成它的方法,但是它很慢而且 df 非常大。

任何提示我怎样才能让它更快???我应该尝试引入并行处理吗?任何提示如何与这样的例子并行进行而不需要将 df 切成块?

谢谢。

我的实现:

df3<-merge(df1,df2,by.x=c("x","y", "z"),by.y=c("x","y", "z"),all.x=T,all.y=TRUE)
df3[is.na(df3$count.x),4]<-0
df3[is.na(df3$count.y),5]<-0
df3$countdif<-df3$count.y-df3$count.x

新编辑。 答案: Akrun 回答中的 2 个建议效果很好.第一个在微基准测试中的速度提高了 2 倍,并且也适用于我的大型数据帧。这里是他们的基准:

使用 dplyr {}

dodplyr<- function (a,b){
    dfN<- full_join(a,b, by=c('x', 'y', 'z')) %>%
        mutate_each(funs(replace(., which(is.na(.)), 0)), 
                starts_with('count')) %>%  
        mutate(countdif= count.y-count.x)
    dfN<-select(dfN,-count.x,-count.y)
    return(dfN)
}

并使用 data.table {}

dodata.table<-function(a,b){
    setDT(a)
    setDT(b)
    DT <- merge(a,b, by=c('x', 'y', 'z'), all=TRUE)
    for(j in 4:5){set(DT, i=which(is.na(DT[[j]])), j=j, value=0)}
    DT[, countdif:= count.y-count.x]
    DT[,c("count.x","count.y"):=NULL]
    return(DT)
}

微基准测试:

times <- microbenchmark( dodplyr(df1,df2), dodata.table(df1,df2), times=1e3)
> times
Unit: milliseconds
                   expr      min       lq     mean   median       uq      max neval
      dodplyr(df1, df2) 2.374164 2.489710 2.978814 2.590829 2.704017 18.15356  1000
 dodata.table(df1, df2) 5.094271 5.308994 6.458764 5.534259 5.675328 37.23370  1000

但是我无法将它们与我使用 merge{base} 和 dfs 的实现进行比较。我试图包含它,但在调用微基准时出现错误。这是我尝试过的:

domerge<- function(a,b){
  dfm<-merge(a,b,by.x=c("x","y", "z"),by.y=c("x","y", "z"),all.x=T,all.y=TRUE)
  dfm[is.na(dfm$count.x),4]<-0
  dfm[is.na(dfm$count.y),5]<-0
  dfm$countdif<-dfm$count.y-dfm$count.x
  dfm<-dfm[,c(1:3,6)]
  return(dfm)
}

这在调用它时有效,例如df3<-domerge(df1,df2) 但微基准测试时出错:

> times <- microbenchmark(domerge(df1,df2), dodplyr(df1,df2), dodata.table(df1,df2), times=1e3)

Show Traceback

 Rerun with Debug
 Error in merge.data.table(a, b, by.x = c("x", "y", "z"), by.y = c("x",  : 
  Can not match keys in x and y to automatically determine appropriate `by` parameter. Please set `by` value explicitly.

我猜测 dplyr 中的 full_join 相对于 merge 会更快(尽管未测试)。在我们完成 full_join 后,'count' 列中的 'NAs' 使用 mutate_each 由 '0' 进行 replaced,然后我们创建 'countdif' 列使用 mutate

library(dplyr)
dfN <- full_join(df1,df2, by=c('x', 'y', 'z')) %>%
           mutate_each(funs(replace(., which(is.na(.)), 0)), 
                           starts_with('count')) %>%  
           mutate(countdif= count.y-count.x) 
dfN
#  x y  z count.x count.y countdif
#1 3 8 13      10       0      -10
#2 5 5 15      20       0      -20
#3 2 7 12       4       0       -4
#4 4 6 10      12       4       -8
#5 3 9 13       0       7        7
#6 6 8 15       0       3        3

或者使用 data.table 中的 merge.data.table 的可能方法。我们将 'data.frame' 都转换为 'data.table'(setDT(df1)setDT(df2))。然后,使用 merge.data.table 执行 full-join。我们在 for 循环中用 set 将 'count' 列中的 'NA' 值替换为 0(这里是第 4 和第 5 列)(set 非常高效,因为它没有 .[data.table 开销。我们分配 (:=) 新列 'countdif` 作为 'count.y' 和 'count.x'

之间的差异
library(data.table)
setDT(df1)
setDT(df2)
DT <- merge(df1, df2, by=c('x', 'y', 'z'), all=TRUE)

for(j in 4:5){
         set(DT, i=which(is.na(DT[[j]])), j=j, value=0)
  }
DT[, countdif:= count.y-count.x]
DT
#   x y  z count.x count.y countdif
#1: 2 7 12       4       0       -4
#2: 3 8 13      10       0      -10
#3: 3 9 13       0       7        7
#4: 4 6 10      12       4       -8
#5: 5 5 15      20       0      -20
#6: 6 8 15       0       3        3

你必须创建一个像这个例子那样减去的算法:

(如果他们有一个独立的 ALU)

y=1
while y <= "end.of.y"
 Core one > df1={1,1,y} - df2={1,1,y}
 //you subtract the y until the end of y column is reached 
 //saving in another matrix called as you want, then
 Core two > df1={1,1,y+1} - df2={1,1,y+1}
 ...
 Core eight > df1={1,1,y+7} - df2={1,1,y+7}
 y=y+8 
endwhile

并对另一个轴 x 和 z 执行相同操作(嵌套循环)。直到他们结束。

如果处理器中只有 4 个 ALU,则必须执行相同的操作但只使用 4 个 'cores'

希望对您有所帮助。