想象一下,你有两个数据框
df1 <- data.frame(V1 = c(1, 2, 3), v2 = c("a", "b", "c"))
df2 <- data.frame(V1 = c(1, 2, 2), v2 = c("b", "b", "c"))
这里是他们的样子,并排:
> cbind(df1, df2)
V1 v2 V1 v2
1 1 a 1 b
2 2 b 2 b
3 3 c 2 c
您希望知道哪些观察结果是重复的,而不是所有变量。
这可以通过将cols粘贴在一起然后使用%in%:
来完成df1Vec <- apply(df1, 1, paste, collapse= "")
df2Vec <- apply(df2, 1, paste, collapse= "")
df2Vec %in% df1Vec
[1] FALSE TRUE FALSE
第二次观察是df2和df1中唯一的观察。
是否没有更快的方法来生成此输出 - 例如%IN%,多个变量的百分比%,或者我们应该满足应用(粘贴)解决方案?
答案 0 :(得分:4)
在duplicated
上调用data.frame
或使用paste
将所有列强制转换为字符类型,当数据大小变大时,非常效率低下。 duplicated.data.table
方法不将它们强制转换为字符,因此非常有效且可以很好地扩展。
这是使用data.table
的一种方式:
`%dtIN%` <- function(y, x) {
tmp = rbindlist(list(x,y))
len_ = nrow(x)
tmp[, idx := any(.I <= len_) & .N > 1L, by=names(tmp)]
tail(tmp$idx, nrow(y))
}
# example:
df1 <- data.frame(V1 = c(1, 2, 3), v2 = c("a", "b", "c"))
df2 <- data.frame(V1 = c(1, 2, 1, 2, 1), v2 = c("b", "b", "b", "c", "b"))
df2 %dtIN% df1
# [1] FALSE TRUE FALSE FALSE FALSE
@ flodel(早期)的基准测试很好(见历史),但并没有真正展示这种不必要的强制的真实效果,因为整个数据大小是:
print(object.size(df1), units="Kb") # 783.8 Kb
小于1 MB 。让我们构建一个更大的数据集来查看效果。
set.seed(45L)
df1 <- data.frame(x=sample(paste0("V", 1:1000), 1e7, TRUE),
y = sample(1e2, 1e7, TRUE), stringsAsFactors=FALSE)
df2 <- data.frame(x=sample(paste0("V", 1:700), 1e6, TRUE),
y=sample(1e2, 1e6, TRUE), stringsAsFactors=FALSE)
print(object.size(df1), units="Mb") # 114.5Mb
system.time(ans1 <- df2 %dtIN% df1)
# user system elapsed
# 1.896 0.296 2.265
system.time(ans2 <- df2 %IN% df1)
# user system elapsed
# 13.014 0.510 14.417
identical(ans1, ans2) # [1] TRUE
Flodel的解决方案在这里慢了6.3倍。
这是另一个尝试说服它真的非常低效的例子;):
set.seed(1L)
DF1 <- data.frame(x=rnorm(1e7), y=sample(letters, 1e7, TRUE))
DF2 <- data.frame(x=sample(DF1$x, 1e5, TRUE), y=sample(letters, 1e5, TRUE))
require(data.table)
system.time(ans1 <- DF2 %dtIN% DF1)
# user system elapsed
# 35.024 0.884 37.225
system.time(ans2 <- DF2 %IN% DF1) ## flodel's earlier answer
# user system elapsed
# 312.931 2.591 319.652
仅1个数字列,每分钟1/2分钟,相当于5分钟,约为8.6倍。现在谁想为它添加另一个数字列并再试一次:)?
IIUC,@ flodel使用interaction
的新解决方案不应该有太大的不同,因为它仍然将它们存储为&#34;因素&#34;,其中因子级别必须是人物..
但是这个实际上开始交换......
system.time(ans3 <- interaction(DF2) %in% interaction(DF1))
## Had to stop after ~3 min because it took 5.5GB and started to SWAP.
答案 1 :(得分:4)
我会选择
interaction(df2) %in% interaction(df1)
# [1] FALSE TRUE FALSE
您可以将其包装在二元运算符中:
"%IN%" <- function(x, y) interaction(x) %in% interaction(y)
然后
df2 %IN% df1
# [1] FALSE TRUE FALSE
rbind(df2, df2) %IN% df1
# [1] FALSE TRUE FALSE FALSE TRUE FALSE
免责声明:我对之前使用do.call(paste, ...)
而不是interaction(...)
的答案进行了一些修改。如果您愿意,请查阅历史记录。我认为Arun声称可怕的低效率&#34; (有点极端恕我直言)仍然坚持,但如果你喜欢一个只使用基数R的简洁解决方案,并且可能是快速的小数据。