簡體   English   中英

R中的data.table-使用多個鍵的多個過濾器-二進制搜索

[英]data.table in R - multiple filters using multiple keys - binary search

我不明白如何根據data.table多個鍵進行data.table 取得內置的mtcars數據集。

DT <- data.table(mtcars)
setkey(DT, am, gear, carb)

遵循小插圖 ,我知道如果我想進行與am == 1 & gear == 4 & carb == 4對應的過濾,我可以說

> DT[.(1, 4, 4)]
   mpg cyl disp  hp drat    wt  qsec vs am gear carb
1:  21   6  160 110  3.9 2.620 16.46  0  1    4    4
2:  21   6  160 110  3.9 2.875 17.02  0  1    4    4

並給出正確的結果。 此外,如果我想讓am == 1 & gear == 4 & (carb == 4 | carb == 2) ,這也有效

> DT[.(1, 4, c(4, 2))]
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
1: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
2: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
3: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
4: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

但是,當我想讓am == 1 & (gear == 3 | gear == 4) & (carb == 4 | carb == 2) ,似乎是合理的

> DT[.(1, c(3, 4), c(4, 2))]
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
1:   NA  NA    NA  NA   NA    NA    NA NA  1    3    4
2: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
3: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

失敗。 您能給我解釋一下什么是正確的方法嗎?

您沒有從查詢中得到錯誤的原因是,當data.table是其他值的倍數時,它們將重用這些值。 換句話說,因為1 for am可以使用2次,所以它不會告訴您。 如果要進行查詢,其中允許的值的數量不是彼此的倍數,則會發出警告。 例如

DT[.(c(1,0),c(5,4,3),c(8,6,4))]

將警告您抱怨剩余一項,這與您輸入data.table(c(1,0),c(5,4,3),c(8,6,4))時看到的錯誤相同。 每當合並X[Y]XY都應視為data.tables。

如果您改用CJ

DT[CJ(c(1,0),c(5,4,3),c(8,6,4))]

然后它將為您和data.table的所有值進行每種組合,從而給出您期望的結果。

從小插圖開始(粗體是我的):

這里發生了什么事? 再讀一遍。 為第二個鍵列“ MIA”提供的值必須在第一個鍵列原點提供的匹配行上的dest鍵列中找到匹配的值。 我們之前不能跳過鍵列的值。 因此,我們提供了來自鍵列原點的所有唯一值。 “ MIA”將自動回收以適合唯一(原始)長度的3。

僅出於完整性考慮,矢量掃描語法無需使用CJ就可以使用

DT[am == 1 & gear == 4 & carb == 4]

要么

DT[am == 1 & (gear == 3 | gear == 4) & (carb == 4 | carb == 2)]

您如何知道是否需要二進制搜索? 如果子集的速度難以忍受,則需要進行二進制搜索。 例如,我有一個48M的行數據。我正在使用該表,並且二進制搜索和向量之間的差異相對於彼此是驚人的。 具體而言,矢量掃描花費的時間為1.490秒,而二進制搜索僅花費0.001秒。 當然,這假定我已經將data.table設置為鍵。 如果我包括設置密鑰所需的時間,則設置密鑰和執行子集的組合為1.628。 所以你必須選擇你的毒葯

現在,該問題已成為重復問題的目標,我認為可以改善現有答案,以幫助新手data.table用戶。

1. DT[.()]DT[CJ()]什么區別?

根據?data.table.()list()的別名,作為參數i提供的list在內部轉換為data.table 因此, DT[.(1, c(3, 4), c(2, 4))]等效於DT[data.table(1, c(3, 4), c(2, 4))]

data.table(1, c(3, 4), c(2, 4))
#   V1 V2 V3
#1:  1  3  2
#2:  1  4  4

data.table由兩行組成,這是最長向量的長度。 1被回收。

這與交叉連接不同, 交叉連接創建提供的向量的所有組合。

CJ(1, c(3, 4), c(2, 4))
   V1 V2 V3
#1:  1  3  2
#2:  1  3  4
#3:  1  4  2
#4:  1  4  4

請注意, setDT(expand.grid())將產生相同的結果。

這說明了OP為什么會得到兩個不同的結果:

DT[.(1, c(3, 4), c(2, 4))]
#   mpg cyl disp  hp drat    wt  qsec vs am gear carb
#1:  NA  NA   NA  NA   NA    NA    NA NA  1    3    2
#2:  21   6  160 110  3.9 2.620 16.46  0  1    4    4
#3:  21   6  160 110  3.9 2.875 17.02  0  1    4    4

DT[CJ(1, c(3, 4), c(2, 4))]
#    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#1:   NA  NA    NA  NA   NA    NA    NA NA  1    3    2
#2:   NA  NA    NA  NA   NA    NA    NA NA  1    3    4
#3: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
#4: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
#5: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#6: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4

請注意,參數nomatch = 0將刪除不匹配的行,即包含NA的行。

2.使用%in%

除了CJ()am == 1 & (gear == 3 | gear == 4) & (carb == 2 | carb == 4) ,還有第三個等效的選項,使用值匹配:

DT[am == 1 & gear %in%  c(3, 4) & carb %in% c(2, 4)]
#    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#1: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
#2: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
#3: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#4: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4

請注意, CJ()要求對data.table進行鍵控,而其他兩個變體也將與未data.table的data.table data.table

3.基准化

數據

為了測試3個選項的執行速度,我們需要的data.tabledata.table的32行要mtcars 這是通過反復將mtcars加倍直到達到100萬行(89 MB)來實現的。 然后,復制此data.table以獲取相同輸入數據的鍵控版本。

library(data.table)
# create unkeyed data.table
DT_unkey <- data.table(mtcars)
for (i in 1:15) {
  DT_unkey <- rbindlist(list(DT_unkey, DT_unkey))
  print(nrow(DT_unkey))
}

#create keyed data.table
DT_keyed <- copy(DT_unkey)
setkeyv(DT_keyed, c("am", "gear", "carb"))

# show data.tables
tables()
#     NAME          NROW NCOL MB COLS                                         KEY         
#[1,] DT_keyed 1,048,576   11 89 mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb am,gear,carb
#[2,] DT_unkey 1,048,576   11 89 mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb             
#Total: 178MB

為了獲得公平的比較,計時中包括setkey()操作。 同樣,顯式復制data.tables ,以便從data.table的引用更新中排除影響。

result <- microbenchmark::microbenchmark(
  setkey = {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb"))},
  cj_keyed = {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb")) 
    DT_keyed[CJ(1, c(3, 4), c(2, 4)), nomatch = 0]},
  or_keyed = {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb")) 
    DT_keyed[am == 1 & (gear == 3 | gear == 4) & (carb == 2 | carb == 4)]},
  or_unkey = {
    copy = DT_unkey <- copy(DT)
    DT_unkey[am == 1 & (gear == 3 | gear == 4) & (carb == 2 | carb == 4)]},
  in_keyed =  {
    DT_keyed <- copy(DT)
    setkeyv(DT_keyed, c("am", "gear", "carb")) 
    DT_keyed[am %in% c(1) & gear %in%  c(3, 4) & carb %in% c(2, 4)]},
  in_unkey = {
    copy = DT_unkey <- copy(DT)
    DT_unkey[am %in% c(1) & gear %in%  c(3, 4) & carb %in% c(2, 4)]},
  times = 10L)

我們得到

print(result)
#Unit: milliseconds
#     expr       min        lq     mean    median       uq      max neval
#   setkey 198.23972 198.80760 209.0392 203.47035 213.7455 245.8931    10
# cj_keyed 210.03574 212.46850 227.6808 216.00190 254.0678 259.5231    10
# or_keyed 244.47532 251.45227 296.7229 287.66158 291.3811 404.8678    10
# or_unkey  69.78046  75.61220 103.6113  89.32464 111.5240 231.6814    10
# in_keyed 269.82501 270.81692 302.3453 274.42716 321.2935 431.9619    10
# in_unkey  93.75537  95.86832 119.4371 100.19446 126.6605 251.4172    10

ggplot2::autoplot(result)

在此處輸入圖片說明

顯然, setkey()是一個相當昂貴的操作。 因此,對於一項一次性任務,矢量掃描操作可能比在鍵控表上使用二進制搜索更快。

該基准測試是使用R版本3.3.2(x86_64,mingw32), data.table 1.10.4, microbenchmark 1.4-2.1運行的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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