[英]Is there a %in% operator across multiple columns
想象一下,你有兩個數據幀
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%,這是多個變量的百分比%,或者我們應該滿足於應用(粘貼)解決方案?
在data.frame
上調用duplicated
或使用paste
所有列data.frame
為字符類型,這在數據大小變大時非常低效。 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/2分鍾,而只有1個數字列的5分鍾,~8.6x。 現在誰想要添加另一個數字列並再試一次:)?
IIUC,@ flodel使用interaction
的新解決方案應該沒有太大的不同,因為它仍然將它們存儲為“因素”,其中因子級別必須是字符。
但這個實際上開始交換......
system.time(ans3 <- interaction(DF2) %in% interaction(DF1))
## Had to stop after ~3 min because it took 5.5GB and started to SWAP.
我會去
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關於“可怕的低效率”(有點極端恕我直言)的主張仍然有效,但如果你喜歡一個只使用基數R的簡潔解決方案,並且很快就會出現小數據。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.