2011-11-15 8 views
11

Mam zestaw danych z około 3 miliony wierszy i następującą strukturę:Najszybsza droga do przekształcenia wartości zmiennych jako kolumny

PatientID| Year | PrimaryConditionGroup 
--------------------------------------- 
1  | Y1 | TRAUMA 
1  | Y1 | PREGNANCY 
2  | Y2 | SEIZURE 
3  | Y1 | TRAUMA 

Będąc całkiem nowy, R, mam pewien problem ze znalezieniem właściwej drogi do przekształcania danych w strukturę przedstawiono poniżej:

PatientID| Year | TRAUMA | PREGNANCY | SEIZURE 
---------------------------------------------- 
1  | Y1 | 1  | 1   | 0 
2  | Y2 | 0  | 0   | 1 
3  | Y1 | 1  | 0   | 1 

Moje pytanie brzmi: Co jest najszybszym/najbardziej elegancki sposób, aby utworzyć data.frame, gdzie wartości PrimaryConditionGroup stać kolumny, pogrupowanych według ID_Pacjenta i rok (licząc liczbę occurences)?

Odpowiedz

12

Istnieje prawdopodobnie bardziej zwięzłe sposoby robienia tego, ale dla czystej prędkości, trudno pokonać data.table opartych rozwiązanie:

df <- read.table(text="PatientID Year PrimaryConditionGroup 
1   Y1 TRAUMA 
1   Y1 PREGNANCY 
2   Y2 SEIZURE 
3   Y1 TRAUMA", header=T) 

library(data.table) 
dt <- data.table(df, key=c("PatientID", "Year")) 

dt[ , list(TRAUMA = sum(PrimaryConditionGroup=="TRAUMA"), 
      PREGNANCY = sum(PrimaryConditionGroup=="PREGNANCY"), 
      SEIZURE = sum(PrimaryConditionGroup=="SEIZURE")), 
    by = list(PatientID, Year)] 

#  PatientID Year TRAUMA PREGNANCY SEIZURE 
# [1,]   1 Y1  1   1  0 
# [2,]   2 Y2  0   0  1 
# [3,]   3 Y1  1   0  0 

EDIT:aggregate() dostarcza rozwiązania „zasady R”, które mogłyby lub może nie być bardziej idiomatyczny. (Jedyną komplikacją jest to, że łączna zwraca macierz, zamiast data.frame; druga linia poniżej poprawek do góry.)

out <- aggregate(PrimaryConditionGroup ~ PatientID + Year, data=df, FUN=table) 
out <- cbind(out[1:2], data.frame(out[3][[1]])) 

2-te EDIT Wreszcie zwięzłe rozwiązanie przy użyciu pakietu reshape dostaje się do to samo miejsce.

library(reshape) 
mdf <- melt(df, id=c("PatientID", "Year")) 
cast(PatientID + Year ~ value, data=j, fun.aggregate=length) 
+0

+1 'ddply' nie będzie o wiele mniej pisanie, naprawdę, i będzie oczywiście dużo wolniej. – joran

+1

Dlaczego miałbyś nawet wziąć pod uwagę ddply na ten problem? – hadley

+0

Cześć Josh, dziękuję, działa to zgodnie z oczekiwaniami i ładnie działa. Jaki byłby najbardziej zwięzły/idiomatyczny sposób przekształcania danych (jeśli wydajność nie była problemem)? – Matt

1

Istnieje szybki melt i dcast data.table specyficzne metody zaimplementowane w C, w wersji >=1.9.0. Oto porównanie z innymi znakomitymi odpowiedziami z postu @ Josha na dane z 3-milionowego wiersza (po prostu z wyłączeniem agregacji base :::, ponieważ trwało to dość długo).

Aby uzyskać więcej informacji na temat wpisu NEWS, przejdź do here.

Zakładam, że masz 1000 pacjentów i 5 lat w sumie. Można odpowiednio dopasować zmienne patients i .

require(data.table) ## >= 1.9.0 
require(reshape2) 

set.seed(1L) 
patients = 1000L 
year = 5L 
n = 3e6L 
condn = c("TRAUMA", "PREGNANCY", "SEIZURE") 

# dummy data 
DT <- data.table(PatientID = sample(patients, n, TRUE), 
       Year = sample(year, n, TRUE), 
       PrimaryConditionGroup = sample(condn, n, TRUE)) 

DT_dcast <- function(DT) { 
    dcast.data.table(DT, PatientID ~ Year, fun.aggregate=length) 
} 

reshape2_dcast <- function(DT) { 
    reshape2:::dcast(DT, PatientID ~ Year, fun.aggregate=length) 
} 

DT_raw <- function(DT) { 
    DT[ , list(TRAUMA = sum(PrimaryConditionGroup=="TRAUMA"), 
      PREGNANCY = sum(PrimaryConditionGroup=="PREGNANCY"), 
       SEIZURE = sum(PrimaryConditionGroup=="SEIZURE")), 
    by = list(PatientID, Year)] 
} 

# system.time(.) timed 3 times 
#   Method Time_rep1 Time_rep2 Time_rep3 
#  dcast_DT  0.393  0.399  0.396 
# reshape2_DT  3.784  3.457  3.605 
#   DT_raw  0.647  0.680  0.657 

dcast.data.table wynosi około 1,6x szybciej niż przy użyciu zwykłego sumowania data.table i 8.8x szybszy niż reshape2:::dcast.

Powiązane problemy