问题描述
我是 R 业余爱好者,学习缓慢。我介绍一下情况:
我有两个数据框,其中有几列 (4) 和 +10000 行,如下所示:
df1: df2:
Nº x y attr Nº x y attr
1 45 34 X 1 34 23 x
1 48 45 XX 4 123 45 x
1 41 23 X 4 99 69 xx
4 23 12 X 4 112 80 xx
4 28 16 X 5 78 80 x
5 78 80 XXX 5 69 74 xx
...
我想比较基于 x,y(坐标)的两个数据框,以删除 df1 中也出现在 df2 中的所有值(两个数据集中包含的所有值/坐标,在 df1 中删除它们)。
所以在我的例子中,df1 的最后一行将被删除,因为相同的坐标在 df2 中。
我正在做的是使用双循环 for(),一个用于一个数据集,另一个用于另一个,将所有可能的值一一比较。 我知道这是非常低效的,而且如果我增加数据量也需要很多时间。
还有什么其他方法可以做到这一点? 可能有一些功能,但我通常不知道如何使用它们,这给我带来了问题。
非常感谢!!
解决方法
不是最优雅的解决方案,但可以完成工作:
df2 = fread('Nº x y attr
1 34 23 x
4 123 45 x
4 99 69 xx
4 112 80 xx
5 78 80 x
5 69 74 xx')
df1 = fread('Nº x y attr
1 45 34 X
1 48 45 XX
1 41 23 X
4 23 12 X
4 28 16 X
5 78 80 XXX')
> df1[!stringr::str_c(df1$x,df1$y,sep="_") %in% stringr::str_c(df2$x,df2$y,sep="_"),]
Nº x y attr
1: 1 45 34 X
2: 1 48 45 XX
3: 1 41 23 X
4: 4 23 12 X
5: 4 28 16 X
说明:
最好使用 vectorised 函数而不是循环。 !stringr::str_c(df1$x,sep="_")
将 x 和 y 列连接成一个字符串,然后从 df1 中查找不在 df2 中的元素。这将创建一个由 TRUE FALSE 值组成的逻辑向量,然后我们可以使用它来对 df1 进行子集化。
编辑:
我很好奇我的或@dww 的回答是否更快:
> library(microbenchmark)
>
> n=100000
>
> df1 = data.table(x = sample(n),y=sample(n))
> df2 = data.table(x = sample(n),y=sample(n))
>
>
>
> microbenchmark(
... df1[!stringr::str_c(df1$x,],... df1[fsetdiff(df1[,.(x,y)],df2[,y)] ),on=c('x','y')]
... )
Unit: milliseconds
expr
df1[!stringr::str_c(df1$x,sep = "_") %in% stringr::str_c(df2$x,sep = "_"),]
df1[fsetdiff(df1[,y)]),on = c("x","y")]
min lq mean median uq max neval
168.40953 199.37183 219.30054 209.61414 222.08134 364.3458 100
41.07557 42.67679 52.34855 44.34379 59.27378 152.1283 100
似乎 dww 的 data.table 版本快了大约 5 倍。
,library(data.table)
方法:
df1[fsetdiff(df1[,'y')]
# Nº x y attr
#1: 1 45 34 X
#2: 1 48 45 XX
#3: 1 41 23 X
#4: 4 23 12 X
#5: 4 28 16 X
,
3 行代码
#generate sample data
x1 <- sample(1:50,9001,T)
y1 <- sample(1:50,T)
x2 <- sample(1:50,T)
y2 <- sample(1:50,T)
df1 <- data.frame(id =1:9001,x1,y1,stringsAsFactors = F)
df2 <- data.frame(id =1:9001,x2,y2,stringsAsFactors = F)
#add a match column to each dataframe
df1$match <- paste(df1$x1,df1$y1)
df2$match <- paste(df2$x2,df2$y2)
#overwrite df1 with the date of df1 that does not appear in df2
df1 <- df1[!df1$match %in% df2$match,]