簡體   English   中英

多列中的%運算符是否為%

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM