2014-04-22 8 views
10

Moim celem jest zsumowanie wszystkich wartości w kolumnach rozpoczynających się od prefiksu skill_ w data.table. Wolałbym rozwiązanie z użyciem data.table, ale nie jestem wybredny.Jaki jest najskuteczniejszy sposób na zebranie wszystkich kolumn, których nazwa zaczyna się od wzorca?

Moje rozwiązanie do tej pory:

> require(data.table) 
> DT <- data.table(x=1:4, skill_a=c(0,1,0,0), skill_b=c(0,1,1,0), skill_c=c(0,1,1,1)) 
> DT[, row_idx := 1:nrow(DT)] 
> DT[, count_skills := 
      sapply(1:nrow(DT), 
       function(id) sum(DT[row_idx == id, 
            grepl("skill_", names(DT)), with=FALSE]))] 

> DT 
    x skill_a skill_b skill_c row_idx count_skills 
1: 1  0  0  0  1   0 
2: 2  1  1  1  2   3 
3: 3  0  1  1  3   2 
4: 4  0  0  1  4   1 

Ale to staje się bardzo powolny, gdy DT jest bardzo duża. Czy istnieje skuteczniejszy sposób na zrobienie tego?

+0

Jak dużo, a ile wolniej mówisz tu aobut? – Thell

Odpowiedz

13

pytanie o efektywność i wydajność zawsze zasługuje odniesienia ...

Wielkość Państwa danych jest ważna, ponieważ tempo wzrostu sprawia, że ogromna różnica ...

Relative Times Względne poziomy odniesienia między 2^4 i 2^24.
Rozmiary wzdłuż floor(2^logb(10^(seq(4, 24, .5)), 10))

fragment odniesienia w wysokości 1 miliona wierszy ...

## Unit: milliseconds 
##    expr min  lq median uq max neval 
## dplyr.sol(DT) 21.803 50.260 51.765 52.45 73.30 100 
## rowSums.sol(DT) 20.759 50.224 51.418 52.56 96.28 100 
## SDCols.sol(DT) 7.250 8.916 37.699 38.50 52.69 100 
##  eval.sol(DT) 6.883 7.007 7.916 9.45 50.91 100 

eval.sol jest odpowiedź, że korzysta z obsługą data.table za wyrażeń, w poniższej źródła. ..

library(compiler) 
library(data.table) 
suppressMessages(library(dplyr)) 
library(microbenchmark) 

buildDT <- function(reps) { 
    data.table(x=seq_len(reps*4), 
       skill_a=rep(c(0,1,0,0),reps), 
       skill_b=rep(c(0,1,1,0),reps), 
       skill_c=rep(c(0,1,1,1),reps)) 
} 

OP.sol <- function(DT) { 
    DT[, row_idx := 1:nrow(DT)] 
    DT[, count_skills := 
      sapply(1:nrow(DT), 
       function(id) sum(DT[row_idx == id, 
            grepl("skill_", names(DT)), with=FALSE]))] 
} 

dplyr.sol <- function(DT) 
    DT %.% select(starts_with("skill_")) %.% rowSums() 

SDCols.sol <- function(DT) 
    DT[, Reduce(`+`, .SD), 
    .SDcols = grep("skill_", names(DT), value = T)] 

rowSums.sol <- function(DT) 
    rowSums(DT[,grep("skill_", names(DT)),with=FALSE]) 

eval.sol <- function(DT) { 
    cmd <- parse(text=paste(colnames(DT)[grepl("^skill_", colnames(DT))],collapse='+')) 
    DT[,eval(cmd)] 
} 

DT <- buildDT(1) 
identical(OP.sol(DT)$count_skills, dplyr.sol(DT)) 

## [1] TRUE 

identical(OP.sol(DT)$count_skills, rowSums.sol(DT)) 

## [1] TRUE 

identical(OP.sol(DT)$count_skills, SDCols.sol(DT)) 

## [1] TRUE 

identical(OP.sol(DT)$count_skills, eval.sol(DT)) 

## [1] TRUE 

DT<-buildDT(2500) 
nrow(DT) 

## [1] 10000 

microbenchmark(# OP.sol(DT), forget this method. 
       dplyr.sol(DT), 
       rowSums.sol(DT), 
       SDCols.sol(DT), 
       eval.sol(DT), 
       times=100) 

## Unit: microseconds 
##    expr min lq median uq max neval 
## dplyr.sol(DT) 760.1 809.0 848.2 951.5 2276 100 
## rowSums.sol(DT) 580.5 605.3 627.6 745.7 28481 100 
## SDCols.sol(DT) 559.8 610.5 638.8 694.0 2016 100 
##  eval.sol(DT) 636.4 677.7 692.4 740.5 2021 100 

DT<-buildDT(25000) 
nrow(DT) 

## [1] 100000 

microbenchmark(# OP.sol(DT), forget this method. 
       dplyr.sol(DT), 
       rowSums.sol(DT), 
       SDCols.sol(DT), 
       eval.sol(DT), 
       times=100) 

## Unit: milliseconds 
##    expr min lq median uq max neval 
## dplyr.sol(DT) 2.668 3.744 4.045 4.573 33.87 100 
## rowSums.sol(DT) 2.455 3.339 3.756 4.235 34.19 100 
## SDCols.sol(DT) 1.253 1.401 2.179 2.392 31.72 100 
##  eval.sol(DT) 1.294 1.427 2.116 2.484 32.02 100 

DT<-buildDT(250000) 
nrow(DT) 

## [1] 1000000 

microbenchmark(# OP.sol(DT), forget this method. 
       dplyr.sol(DT), 
       rowSums.sol(DT), 
       SDCols.sol(DT), 
       eval.sol(DT), 
       times=100) 

## Unit: milliseconds 
##    expr min  lq median uq max neval 
## dplyr.sol(DT) 21.803 50.260 51.765 52.45 73.30 100 
## rowSums.sol(DT) 20.759 50.224 51.418 52.56 96.28 100 
## SDCols.sol(DT) 7.250 8.916 37.699 38.50 52.69 100 
##  eval.sol(DT) 6.883 7.007 7.916 9.45 50.91 100 

identical(dplyr.sol(DT), rowSums.sol(DT)) 

## [1] TRUE 

identical(dplyr.sol(DT), SDCols.sol(DT)) 

## [1] TRUE 

identical(dplyr.sol(DT), eval.sol(DT)) 

## [1] TRUE 
+1

Nie widzę 'SDCols.sol' w benchmarkach – eddi

+0

@eddi, moje złe ... dodane. – Thell

+1

Dzięki za testy porównawcze! Porównanie jest nieco niesprawiedliwe wobec 'SDCols.sol'. Traci trochę czasu na dodawaniu zmiennej 'count_skills', czego nie robią inne rozwiązania. Tak więc prawidłowa wersja to 'DT [, Reduce (' + ', .SD), .SDcols = grep (" skill_ ", names (DT), value = T)]'. Czas jest nieco niestabilny, jeśli uruchomisz go tylko 10 razy. Zrobiłem ostatni test porównawczy z 'times = 100' i zaktualizowałem' SDCols.sol' i otrzymałem medianę taktowania, jak 35.2 ('SDCols.sol') i 32.4 (' eval.sol'). Różnica nie jest już tak duża. – djhurio

9

Dlaczego nie używać rowSums, to na ogół skuteczny:

rowSums(DT[,grep("skill_", names(DT)),with=FALSE]) 
+0

Dzięki, że rozwiązuje mój problem! Nie wiem, dlaczego kod nie jest powtarzalny. Brakujące wymagania (data.table)? Dodam to. – Rodrigo

+0

@RogerBill przepraszam, kod był powtarzalny (nie napisałem linku z twojego kodu) – agstudy

7

rozwiązanie wykorzystujące data.table i .SDcols.

require(data.table) 

DT <- data.table(x=1:4, skill_a=c(0,1,0,0), skill_b=c(0,1,1,0), 
       skill_c=c(0,1,1,1)) 

DT[, row_idx := 1:nrow(DT)] 

DT[, count_skills := Reduce(`+`, .SD), 
    .SDcols = grep("skill_", names(DT), value = T)] 
DT 
+0

Dzięki! W ten sposób nie musisz dodawać row_idx, którego używałem jako zmiennej pomocniczej. – Rodrigo

8

Oto rozwiązanie dplyr:

library(dplyr) 

DT %>% mutate(count = DT %>% select(starts_with("skill_")) %>% rowSums()) 
Powiązane problemy