2015-06-20 91 views
10

的方式,我想知道什麼是尋找在xts對象是同一個特定行的所有行的最快的方法最快找到匹配的行

library(xts) 

nRows <- 3 

coreData <- data.frame(a=rnorm(nRows), b=rnorm(nRows), c=rnorm(nRows)) 

testXts1 <- xts(coreData, order.by=as.Date(1:nRows)) 
testXts2 <- xts(coreData, order.by=as.Date((nRows + 1):(2*nRows))) 
testXts3 <- xts(coreData, order.by=as.Date((2*nRows + 1):(3*nRows))) 

testXts <- rbind(testXts1, testXts2, testXts3) 

> testXts 
        a   b   c 
1970-01-02 -0.3288756 1.441799 1.321608 
1970-01-03 -0.7105016 1.639239 -2.056861 
1970-01-04 0.1138675 -1.782825 -1.081799 
1970-01-05 -0.3288756 1.441799 1.321608 
1970-01-06 -0.7105016 1.639239 -2.056861 
1970-01-07 0.1138675 -1.782825 -1.081799 
1970-01-08 -0.3288756 1.441799 1.321608 
1970-01-09 -0.7105016 1.639239 -2.056861 
1970-01-10 0.1138675 -1.782825 -1.081799 

rowToSearch <- first(testXts) 

> rowToSearch 
        a  b  c 
1970-01-02 -0.3288756 1.441799 1.321608 

indicesOfMatchingRows <- unlist(apply(testXts, 1, function(row) lapply(1:NCOL(row), function(i) row[i] == coredata(rowToSearch[, i])))) 

testXts[indicesOfMatchingRows, ] 

        a   b   c 
1970-01-02 -0.3288756 1.441799 1.321608 
1970-01-05 -0.3288756 1.441799 1.321608 
1970-01-08 -0.3288756 1.441799 1.321608 

我相信這可以更優雅地完成,快速的方式。

一個更普遍的問題是,您R中是怎麼說的:「我有這樣的行矩陣[5]我怎麼能找到矩陣是相同的矩陣[5](的指標)其他行」。

如何做到這一點的data.table

回答

8

既然你說,速度是你的主要關注,你可以得到加速比甚至超過與RCPP一個data.table解決方案:

library(Rcpp) 
cppFunction(
"LogicalVector compareToRow(NumericMatrix x, NumericVector y) { 
    const int nr = x.nrow(); 
    const int nc = x.ncol(); 
    LogicalVector ret(nr, true); 
    for (int j=0; j < nr; ++j) { 
    for (int k=0; k < nc; ++k) { 
     if (x(j, k) != y[k]) { 
     ret[j] = false; 
     break; 
     } 
    } 
    } 
    return ret; 
}") 
testXts[compareToRow(testXts, rowToSearch),] 
#     a   b   c 
# 1970-01-02 1.324457 0.8485654 -1.464764 
# 1970-01-05 1.324457 0.8485654 -1.464764 
# 1970-01-08 1.324457 0.8485654 -1.464764 

這裏有一個比較一個相當大的實例(100萬行):

set.seed(144) 
bigXts <- testXts[sample(nrow(testXts), 1000000, replace=TRUE),] 
testDT <- as.data.frame(bigXts) 

josilber <- function(x, y) x[compareToRow(x, y),] 
roland.base <- function(x, y) x[colSums(t(x) != as.vector(y)) == 0L,] 
library(data.table) 
roland.dt <- function(testDT, y) { 
    setDT(testDT, keep.rownames=TRUE) 
    setkey(testDT, a, b, c) 
    testDT[setDT(as.data.frame(y))] 
} 
library(microbenchmark) 
microbenchmark(josilber(bigXts, rowToSearch), roland.base(bigXts, rowToSearch), roland.dt(testDT, rowToSearch), times=10) 
# Unit: milliseconds 
#        expr   min   lq  mean  median   uq  max 
#  josilber(bigXts, rowToSearch) 7.830986 10.24748 45.64805 14.41775 17.37049 258.4404 
# roland.base(bigXts, rowToSearch) 3530.042324 3964.72314 4288.05758 4179.64233 4534.21407 5400.5619 
# roland.dt(testDT, rowToSearch) 32.826285 34.95014 102.52362 57.30213 130.51053 267.2249 

這個基準假設調用roland.dt之前所述對象已被轉換爲一個數據幀(〜4秒開銷)並在調用josilber之前編譯了compareToRows(開銷約3秒)。 Rcpp解決方案的速度比基礎R解決方案快大約300倍,比中值運行時的data.table解決方案快大約4倍。基於digest的方法沒有競爭力,每次執行時間超過60秒。

+1

隨着摘要,你只能做一次哈希表,所以大概如果你需要多次搜索會得到回報 – jenesaisquoi

+1

我相信這個基準對於'roland.dt'來說太快了,因爲'testDT'正在通過**被引用**到在第一次試驗期間將其分類的'roland.dt',這將使後續試驗更快。你可以在這裏試試:'a < - data.table(x = 10:1);一個; foo < - function(x){setkey(x); X [data.table(X = 1:5)]}; B'-foo(a)的一個''或閱讀[這裏](https://stackoverflow.com/a/14293056/2573061) – C8H10N4O2

+1

這並不是一個巨大的差異,但我不能比兩倍的基準更好的「誠實」關閉'data.table'解決方案(下面的解決方案) – C8H10N4O2

6

這裏是一個更快的基礎R溶液:

ind <- colSums(t(testXts) != as.vector(rowToSearch)) == 0L 
testXts[ind,] 

下面是一個使用data.table的溶液加入:

library(data.table) 
testDT <- as.data.frame(testXts) 
setDT(testDT, keep.rownames=TRUE) 
setkey(testDT, a, b, c) 
testDT[setDT(as.data.frame(rowToSearch))] 

然而,我會警惕時comparing floating point numbers

2

這不使用data.table但可能是相當快的。你可以通過散列行做到這一點,

library(digest) 
hash <- apply(testXts, 1, digest) 
testXts[which(hash[1] == hash)] 

#     a   b   c 
# 1970-01-02 0.8466816 -0.7129076 -0.5742323 
# 1970-01-05 0.8466816 -0.7129076 -0.5742323 
# 1970-01-08 0.8466816 -0.7129076 -0.5742323 
+0

你能更新您的解決方案,因此它使用所提供的'rowToSearch'而不是假設它的搜索矩陣中的第一行的? – josliber

+0

@josilber我想不通的那部分,隨意編輯 – jenesaisquoi

+2

由於OP是尋找效率我會找生成密鑰一個更快的方法。 'do.call(粘貼,as.data。frame(testXts))將所有數字粘貼在一起,看起來更快(當我的基準測試中使用預先計算的testDT時,我可以與基本R解決方案競爭)。 – josliber

1

最簡單的data.table解決方案可能是:

merge(as.data.table(testXts), as.data.table(rowToSearch, keep.rownames=FALSE)) 

返回:

  a   b   c  index 
1: 1.685138 -0.3039018 -1.550871 1970-01-02 
2: 1.685138 -0.3039018 -1.550871 1970-01-05 
3: 1.685138 -0.3039018 -1.550871 1970-01-08 

爲什麼這個工程:

合併共同列=內部聯接,除非另有說明。此內部聯接僅返回具有與rowToSearch相同的(a,b,c)值的列。

keep.rownames=FALSE右側確保刪除rowToSearch(不需要)的日期索引並且不進入加入的公共列。