2013-03-13 12 views
19

Próbuję podzbiór ramki danych, gdzie otrzymuję wiele ramek danych na podstawie wartości wielu kolumn. Oto mój przykładR podzbiór ramki danych na wiele ramek danych na podstawie wartości wielu kolumn

>df 
    v1 v2 v3 v4 v5 
    A Z 1 10 12 
    D Y 10 12 8 
    E X 2 12 15 
    A Z 1 10 12 
    E X 2 14 16 

Oczekiwany wynik jest coś takiego, gdzie jestem dzieląc tę ​​ramkę danych do wielu ramek danych w oparciu o kolumny v1 i v2

>df1 
v3 v4 v5 
    1 10 12 
    1 10 12 
>df2 
v3 v4 v5 
10 12 8 
>df3 
v3 v4 v5 
2 12 15 
2 14 16 

Napisałem kod, który działa teraz, ale nie myśl, że to najlepszy sposób na zrobienie tego. Musi być lepszy sposób na zrobienie tego. Zakładając, że tab jest data.frame o początkowych danych. Oto mój kod:

v1Factors<-levels(factor(tab$v1)) 
v2Factors<-levels(factor(tab$v2)) 

for(i in 1:length(v1Factors)){ 
    for(j in 1:length(v2Factors)){ 
    subsetTab<-subset(tab, v1==v1Factors[i] & v2==v2Factors[j], select=c("v3", "v4", "v5")) 
    print(subsetTab) 
    } 
} 

Czy ktoś może zaproponować lepszą metodę wykonania powyższych czynności?

+0

Czy chcesz ponownie wykorzystać te ramki danych, czy też wydrukować je pogrupowane według tych kolumn? – Thilo

+0

Chcę je ponownie wykorzystać ... chcę wykreślić wykresy na tych ramkach danych. –

Odpowiedz

23

Szukasz split

split(df, with(df, interaction(v1,v2)), drop = TRUE) 
$E.X 
    v1 v2 v3 v4 v5 
3 E X 2 12 15 
5 E X 2 14 16 

$D.Y 
    v1 v2 v3 v4 v5 
2 D Y 10 12 8 

$A.Z 
    v1 v2 v3 v4 v5 
1 A Z 1 10 12 

Jak zauważył w komentarzach

jedną z następujących czynności będzie działać

library(microbenchmark) 
microbenchmark(
       split(df, list(df$v1,df$v2), drop = TRUE), 
       split(df, interaction(df$v1,df$v2), drop = TRUE), 
       split(df, with(df, interaction(v1,v2)), drop = TRUE)) 


Unit: microseconds 
                expr  min  lq median  uq  max neval 
      split(df, list(df$v1, df$v2), drop = TRUE) 1119.845 1129.3750 1145.8815 1182.119 3910.249 100 
    split(df, interaction(df$v1, df$v2), drop = TRUE) 893.749 900.5720 909.8035 936.414 3617.038 100 
split(df, with(df, interaction(v1, v2)), drop = TRUE) 895.150 902.5705 909.8505 927.128 1399.284 100 

Wydaje interaction jest nieco szybszy (prawdopodobnie ze względu na fakt, że f = list(...) są właśnie konwertowane na interakcje w ramach funkcji)


Edit

Jeśli chcesz po prostu użyć data.frames podzbioru następnie Sugerowałbym użyciu data.table dla ułatwienia kodowania

library(data.table) 

dt <- data.table(df) 
dt[, plot(v4, v5), by = list(v1, v2)] 
+2

'split' może zająć listę dla' f', zamiast używać 'interakcji'. Nie wiem, która jest bardziej wydajna. – A5C1D2H2I1M1N2O1R2T1

+0

@AnandaMahto - Dodałem benchmark. – mnel

+0

Dzięki za testy porównawcze. W takim przypadku sztuczka @ Aruna ('with (df, split (df, f = do.call (paste, df [1: 2]))) prawdopodobnie byłaby jeszcze szybsza i nie stworzy niepotrzebnych poziomów – A5C1D2H2I1M1N2O1R2T1

3

Jest teraz również nest() z tidyr co jest raczej miły .

library(tidyr) 
nestdf <- df %>% nest(v3:v5) 
nestdf$data 

> nestdf$data 
[[1]] 
# A tibble: 2 × 3 
    v3 v4 v5 
    <int> <int> <int> 
1  1 10 12 
2  1 10 12 

[[2]] 
# A tibble: 1 × 3 
    v3 v4 v5 
    <int> <int> <int> 
1 10 12  8 

[[3]] 
# A tibble: 2 × 3 
    v3 v4 v5 
    <int> <int> <int> 
1  2 12 15 
2  2 14 16 

Dostęp do poszczególnych przekąsek za pomocą nestdf$data[1] i tak dalej.