2015-05-28 12 views
10

Chciałbym podsumować niektóre zmienne w mojej ramce danych na podstawie kolumny. Jednak moja ramka danych jest raczej duża (> 30 000 000 wierszy), a korzystanie z funkcji podsumowania w dplyr trwa wieki. Czy istnieje szybszy sposób w R, aby przyspieszyć proces podsumowywania?Szybszy sposób podsumowania zmiennych na podstawie kolumny w R

mam df ramki danych w formacie:

proid X1 X2 X3 X4 
1  1 zz a e a 
2  2 ff g z b 
3  1 cd s f d 
4  3 ab t e e 
5  2 ta b h k 
     .... 

pragnę złączyć zmienne X1 do X4, gdy mają taką samą wartość prodid. Połączone łańcuchy są oddzielone przecinkami. Więc powyższa tabela powinna dać mi nową tabelę:

proid  X1 X2 X3 X4 
1  1 zz,cd a,s e,f a,d 
2  2 ff,ta g,b z,h b,k 
3  3  ab t e e 
     .... 

Użyłem następujący kod dplyr:

concat <- function(x){ 
    x <- na.omit(x) 
    if(length(x)==0){ 
    return(as.character(NA)) 
    }else{ 
    return(paste(x,collapse=",")) 
    } 
} 

dg<-group_by(df,proid) 
df<-summarise(dg,proid,concat(X1),concat(X2),concat(X3),concat(X4)) 
+3

try 'setDT (DF) [, lapply (.SD, concat), by = proid] 'i zobacz, jak wyglądają czasy. – eddi

Odpowiedz

11

Edit UWAGA: usunięto oryginalne części moją odpowiedź, że nie odniósł NA leczenie & dodano punkt odniesienia.

concat2 <- function(x) if(all(is.na(x))) NA_character_ else paste(na.omit(x), collapse = ",") 

Korzystanie data.table:

setDT(df)[, lapply(.SD, concat2), by = proid, .SDcols = -c("X4")] 
# proid X1 X2 X3 
#1:  1 zz,cd a,s e,f 
#2:  2 ff,ta g,b z,h 
#3:  3 NA t e 

Korzystanie dplyr:

df %>% group_by(proid) %>% summarise_each(funs(concat2), -X4) 

Benchmark mniejsze ilości danych niż w przypadku rzeczywistego użycia i nie w pełni reprezentatywne, więc po prostu chcieliśmy dostać wrażenie, jak concat2 porównuje się z concat itd.

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

N <- 1e6 
x <- c(letters, LETTERS) 
df <- data.frame(
    proid = sample(1e4, N, TRUE), 
    X1 = sample(sample(c(x, NA), N, TRUE)), 
    X2 = sample(sample(c(x, NA), N, TRUE)), 
    X3 = sample(sample(c(x, NA), N, TRUE)), 
    X4 = sample(sample(c(x, NA), N, TRUE)) 
) 

dt <- as.data.table(df) 

concat <- function(x){ 
    x <- na.omit(x) 
    if(length(x)==0){ 
    return(as.character(NA)) 
    }else{ 
    return(paste(x,collapse=",")) 
    } 
} 

concat2 <- function(x) if(all(is.na(x))) NA_character_ else paste(na.omit(x), collapse = ",") 

concat.dplyr <- function(){ 
    df %>% group_by(proid) %>% summarise_each(funs(concat), -X4) 
} 

concat2.dplyr <- function(){ 
    df %>% group_by(proid) %>% summarise_each(funs(concat2), -X4) 
} 

concat.data.table <- function(){ 
    dt[, lapply(.SD, concat), by = proid, .SDcols = -c("X4")] 
} 

concat2.data.table <- function(){ 
    dt[, lapply(.SD, concat2), by = proid, .SDcols = -c("X4")] 
} 


microbenchmark(concat.dplyr(), 
       concat2.dplyr(), 
       concat.data.table(), 
       concat2.data.table(), 
       unit = "relative", 
       times = 10L) 
Unit: relative 
       expr  min  lq median  uq  max neval 
     concat.dplyr() 1.058839 1.058342 1.083728 1.105907 1.080883 10 
     concat2.dplyr() 1.057991 1.065566 1.109099 1.145657 1.079201 10 
    concat.data.table() 1.024101 1.018443 1.093604 1.085254 1.066560 10 
concat2.data.table() 1.000000 1.000000 1.000000 1.000000 1.000000 10 

Wynik: data.table działa nieco szybciej niż dplyr na przykładowych danych, a concat2 jest nieco szybszy niż concat. Jednak różnice w tym zbiorze danych pozostają niewielkie.

+0

Wersja data.table działa w około jednej czwartej czasu wersji dplyr na moim komputerze. Wersja dplyr/tidyr autorstwa @StevenBeaupre działa w około 1,5 razy wersji dplyr. – eipi10

+4

Żadne z tych rozwiązań nie uwzględnia 'na.omit()' w funkcji OP chociaż ... @ eipi10, różnica prędkości z 'dplyr' byłaby w dużej mierze zależna od a) liczby grup, oraz b) liczby wierszy (w tej kolejności) ... – Arun

+1

@ user22119, prawdopodobnie powinieneś się wtedy poznać: P. Zacznij od winiet HTML [tutaj] (https://github.com/Rdatatable/data.table/wiki/Getting-started). – Arun

2

na.omit robi mnóstwo niepotrzebnych kontroli i operacji. Zastępując ją prosty is.na rozmowy daje przyzwoite przyspieszenie:

concat3 = function(x) { 
    x = x[!is.na(x)] 
    if (length(x) == 0) 
    NA_character_ 
    else 
    paste(x, collapse = ",") 
} 

Wykorzystując dane docendo za (ale z ciągów zamiast czynników - czynniki powolnych Wszystkie wersje w dół):

microbenchmark(dt[, lapply(.SD, concat3), by = proid, .SDcols = -c("X4")], 
       dt[, lapply(.SD, concat2), by = proid, .SDcols = -c("X4")], 
       times = 5) 
#Unit: milliseconds 
#              expr  min  lq  mean median  uq  max neval 
# dt[, lapply(.SD, concat3), by = proid, .SDcols = -c("X4")] 960.2475 1079.177 1251.545 1342.684 1402.571 1473.045  5 
# dt[, lapply(.SD, concat2), by = proid, .SDcols = -c("X4")] 1718.8121 1892.696 2159.148 2171.772 2470.205 2542.253  5