2012-08-02 19 views
8

Mam bardzo dużą ramkę danych w R i chciałbym podsumować dwie kolumny dla każdej odrębnej wartości w innych kolumnach, na przykład, że mieliśmy dane o ramce danych transakcji w różnych sklepach w ciągu jednego dnia następującoSuma przez odrębną wartość kolumny w R

shop <- data.frame('shop_id' = c(1, 1, 1, 2, 3, 3), 
    'shop_name' = c('Shop A', 'Shop A', 'Shop A', 'Shop B', 'Shop C', 'Shop C'), 
    'city' = c('London', 'London', 'London', 'Cardiff', 'Dublin', 'Dublin'), 
    'sale' = c(12, 5, 9, 15, 10, 18), 
    'profit' = c(3, 1, 3, 6, 5, 9)) 

który jest:

shop_id shop_name city  sale profit 
    1  Shop A  London 12 3 
    1  Shop A  London 5 1 
    1  Shop A  London 9 3 
    2  Shop B  Cardiff 15 6 
    3  Shop C  Dublin 10 5 
    3  Shop C  Dublin 18 9 

i chciałbym podsumować sprzedaż i zysk dla każdego sklepu, aby dać:

shop_id shop_name city  sale profit 
    1  Shop A  London 26 7 
    2  Shop B  Cardiff 15 6 
    3  Shop C  Dublin 28 14 

Obecnie używam następujący kod, aby to zrobić:

shop_day <-ddply(shop, "shop_id", transform, sale=sum(sale), profit=sum(profit)) 
shop_day <- subset(shop_day, !duplicated(shop_id)) 

który działa całkowicie w porządku, ale jak powiedziałem moim dataframe jest duża (140.000 wierszy, 37 kolumn i prawie 100.000 unikalnych wierszy, które chcę sumę) i mój kod trwa wieki, a potem w końcu mówi, że zabrakło mu pamięci.

Czy ktoś wie o najbardziej efektywny sposób to zrobić.

Z góry dziękuję!

+2

... Czuję, że odpowiedź "data.table" nadchodzi ... –

Odpowiedz

13

** Dane obowiązkowe Tabela odpowiedź **

> library(data.table) 
data.table 1.8.0 For help type: help("data.table") 
> shop.dt <- data.table(shop) 
> shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id'] 
    shop_id sale profit 
[1,]  1 26  7 
[2,]  2 15  6 
[3,]  3 28  14 
> 

co brzmi dobrze i dobrze, aż robi się większe ...

shop <- data.frame(shop_id = letters[1:10], profit=rnorm(1e7), sale=rnorm(1e7)) 
shop.dt <- data.table(shop) 

> system.time(ddply(shop, .(shop_id), summarise, sale=sum(sale), profit=sum(profit))) 
    user system elapsed 
    4.156 1.324 5.514 
> system.time(shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id']) 
    user system elapsed 
    0.728 0.108 0.840 
> 

uzyskać dodatkowy wzrost prędkości w przypadku tworzenia data.table z kluczem:

shop.dt <- data.table(shop, key='shop_id') 

> system.time(shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id']) 
    user system elapsed 
    0.252 0.084 0.336 
> 
+0

Zauważ, że Justin używa 'streszczenia' zamiast' transform' w swoim wywołaniu 'ddply'; ta zmiana prawdopodobnie wystarczy, aby twój kod działał bez błędu pamięci, chociaż inne rozwiązania są z pewnością szybsze. – Aaron

+0

@Aaron Dzięki! Zostawiłem to wyjaśnienie, ponieważ istniała wcześniejsza odpowiedź, która wyjaśniała to. Jednak to zostało usunięte! – Justin

+0

Dzięki Justin, o wiele szybciej. Kolejne szybkie pytanie, czy istnieje sposób na zachowanie pozostałych kolumn (np. Shop_name, city) w ostatecznej tabeli danych? Mogę połączyć się z początkową ramką danych, aby uzyskać tę informację, ale byłbym dokładniejszy, gdyby był sposób na wykonanie tego w początkowym zapytaniu. – user1165199

3

Oto jak użyć podstawy R, aby przyspieszyć operacje takie jak ta:

idx <- split(1:nrow(shop), shop$shop_id) 
a2 <- data.frame(shop_id=sapply(idx, function(i) shop$shop_id[i[1]]), 
       sale=sapply(idx, function(i) sum(shop$sale[i])), 
       profit=sapply(idx, function(i) sum(shop$profit[i]))) 

Czas zmniejsza się do 0,75 s w porównaniu z 5,70 s dla wersji podsumowania ddply w moim systemie.

+0

Jeśli mam wiele kolumn, takich jak sprzedaż i zysk w powyższym przykładzie, które chcę zsumować, czy możliwe jest wywołanie pojedynczej funkcji, aby połączyć trzecią i czwartą linię w powyższym kodzie w jedną linię. – discipulus

+1

Nie za pomocą dokładnie tej metody, ale są sposoby na to. Zacznij nowe pytanie od minimalnie powtarzalnego przykładu, a otrzymasz mnóstwo sugestii. – Aaron