2015-06-23 13 views
5

Mam dataframe z poniższego formularza:Przegrupuj data.frame uzyskać sekwencyjną kolejność produktów

df <- data.frame(client = c("client1", "client1", "client2", "client3", "client3"), 
       product = c("A", "B", "A", "D", "A"), 
       purchase_Date = c("2010-03-22", "2010-02-02", "2009-03-02", "2011-04-05", "2012-11-01")) 
df$purchase_Date <- as.Date(df$purchase_Date, format = "%Y-%m-%d") 

który wygląda tak:

client product purchase_Date 
1 client1  A 2010-03-02 
2 client1  B 2010-02-02 
3 client2  A 2009-03-02 
4 client3  D 2011-04-05 
5 client3  A 2012-11-01 

które chciałbym zmienić tak :

client purchase1 purchase2 
1 client1   B   A 
2 client2   A  <NA> 
3 client3   D   A 

więc chciałbym dowiedzieć się, który produkt był pierwszy, drugi, trzeci i tak dalej, każdy perso n zamówiony przez datę zakupu. Mogę łatwo uzyskać każdy indywidualnie za pomocą danych.tabeli:

library(data.table) 
setDT(df)[ , .SD[order(-purchase_Date), product][1], by = client] 

dla pierwszego. ale nie mam pojęcia, jak skutecznie uzyskać pożądany wynik.

Odpowiedz

7

Oto możliwe data.table rozwiązanie (jeśli masz więcej niż 10 zakupy, to polecam unikanie korzystania paste0 i po prostu użyć indx := seq_len(.N) zamiast, jak to może potencjalnie bałagan zlecenia nabycia)

setDT(df)[order(purchase_Date), indx := paste0("purchase", seq_len(.N)), by = client] 
dcast(df, client ~ indx, value.var = "product") 
#  client purchase1 purchase2 
# 1: client1   B   A 
# 2: client2   A  NA 
# 3: client3   D   A 

Porównanie frank() i order() podejść do tworzenia indx col:

require(data.table) 
set.seed(45L); 
dt = data.table(client = sample(paste("client", 1:1e4, sep=""), 1e6, TRUE)) 
dt[, `:=`(product = sample(paste("p", 1:200, sep=""), .N, FALSE), 
      purchase_Date = as.Date(sample(14610:16586, .N, FALSE), 
      origin = "1970-01-01")), by=client] 

system.time(dt[order(purchase_Date), indx := seq_len(.N), by = client]) 
# user system elapsed 
# 0.19 0.02 0.20 
system.time(dt[, purch_rank := frank(purchase_Date, ties.method = "dense"), by=client]) 
# user system elapsed 
# 3.94 0.00 3.98 
+1

To, uważam, jest bardziej wydajne, ponieważ unika wywołania 'frank()' dla każdego 'by'. 'seq_len()' powinno być bardzo pomijalne ... Byłoby wspaniale do testowania! – Arun

+4

Tylko testy porównawcze na milionach wierszy z 10000 klientami - 'frank = 2.6s' vs' order() = 0.5s'. Dodano [FR # 1197] (https://github.com/Rdatatable/data.table/issues/1197). – Arun

4

Podejście dplyr/tidyr:

library(dplyr) 
library(tidyr) 

df %>% 
    group_by(client) %>% 
    mutate(purch_rank = dense_rank(purchase_Date)) %>% 
    select(-purchase_Date) %>% 
    spread(purch_rank, product) 
#Source: local data frame [3 x 3] 
# 
# client 1 2 
#1 client1 B A 
#2 client2 A NA 
#3 client3 D A 

I możliwe data.table podejście:

library(data.table) #v 1.9.5+ currently from GitHub for "frank" 
setDT(df)[, purch_rank := frank(purchase_Date, ties.method = "dense"), by=client] 
dcast(df, client ~ purch_rank, value.var = "product") 
# client 1 2 
#1: client1 B A 
#2: client2 A NA 
#3: client3 D A 
+0

Przykro mi, moje rozwiązanie jest bardzo podobne do twojego. Byłem zajęty, aby to zrozumieć, ale tego nie zauważyłem. – SabDeM

+0

@SabDeM, bez problemu –

+1

dziękuję za wskazanie mi na oczywistość po prostu dodanie rangi! ... oh człowiek dzień pracy był długi – grrgrrbla

0

Oto moje rozwiązanie z dplyr i tidyr:

df %>% 
    group_by(client) %>% 
    select(-purchase_Date) %>% 
    mutate(purchase = seq_along(product)) %>% 
    spread(purchase, product) 
Source: local data frame [3 x 3] 

    client 1 2 
1 client1 A B 
2 client2 A NA 
3 client3 D A 

Nieco inne podejście z inną wydajnością byłoby z reshape2 pakiet. Wystarczy skorzystać z poprzedniego kodu wyjątkiem ostatniego wiersza, który zostanie zastąpiony przez to jedno:

dcast(client ~ product) 
Using purchase as value column: use value.var to override. 
    client A B D 
1 client1 1 2 NA 
2 client2 1 NA NA 
3 client3 2 NA 1 
+0

Twoje wyniki dplyr są nieprawidłowe, ponieważ nie używasz informacji "purchase_Date". –

+0

Wiem, dlatego powiedziałem, że to inne podejście. Dane wyjściowe nie pokazują zakupionych produktów (co jest w tym przypadku zmienną), ale ranking ma. Nie odpowiadam oczywiście na pytanie, ale może być pomocne. – SabDeM

+0

Czy mówisz także o pierwszym podejściu w swojej odpowiedzi? –

Powiązane problemy