2015-06-20 4 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

Поскольку вы сказали, что скорость вашей главная забота, вы можете получить ускорения даже над data.table раствором с Rcpp:

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 

Вот сравнение на довольно большой экземпляр (с 1 миллиона строк):

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 

Этот тест предполагает, что объект был преобразован в кадр данных (~ 4 секунды над головой) перед вызовом roland.dt и что compareToRows был скомпилирован (~ 3 секунды накладные расходы) перед вызовом josilber. Решение Rcpp примерно в 300 раз быстрее, чем базовое R-решение, и примерно в 4 раза быстрее, чем решение data.table в срединной среде исполнения. Подход, основанный на digest, был неконкурентоспособным, и он выполнял более 60 секунд каждый раз.

+1

С помощью дайджеста вы делали бы только хэш-таблицу один раз, поэтому предположительно это окупится, если вам понадобится несколько поисков – jenesaisquoi

+1

Я считаю, что этот тест слишком быстрый для 'roland.dt', потому что' testDT' передается ** по ссылке ** на 'roland.dt', который ** сортирует ** его во время первого испытания, что сделает последующие испытания намного быстрее. Вы можете попробовать его здесь: 'a <- data.table (x = 10: 1); а; foo <- function (x) {setkey (x); х [data.table (х = 1: 5)]}; б <-foo (а); a' или читайте [здесь] (https://stackoverflow.com/a/14293056/2573061) – C8H10N4O2

+1

Это не огромная разница, но я не могу стать лучше, чем в два раза быстрее, чем на более честном, off '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)) 'вставляет все числа вместе и кажется более быстрым (я мог бы получить его конкурентоспособным с базовым R-решением при использовании предварительно вычисленного' testDT' в моем тесте). – 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 

Почему это работает:

слияние = внутреннее соединение на общих столбцов, если не указано иное. Это внутреннее соединение возвращает только столбцы с одинаковыми значениями (a, b, c) в виде rowToSearch.

keep.rownames=FALSE с правой стороны гарантирует, что индекс даты rowToSearch (который не нужен) отбрасывается и не вводит общие столбцы для соединения.

Смежные вопросы