web-dev-qa-db-fra.com

Emulate split () avec dplyr group_by: retourne une liste de trames de données

J'ai un grand ensemble de données qui étouffe split() dans R. Je peux utiliser dplyr group_by (ce qui est de toute façon une méthode préférée), mais je ne peux pas conserver le grouped_df résultant sous forme de liste de trames de données, format requis par mes étapes de traitement consécutives. (J'ai besoin de contraindre à SpatialDataFrames et similaire).

considérons un exemple de jeu de données:

df = as.data.frame(cbind(c("a","a","b","b","c"),c(1,2,3,4,5), c(2,3,4,2,2)))
listDf = split(df,df$V1)

résultats 

$a
   V1 V2 V3
 1  a  1  2
 2  a  2  3

$b
   V1 V2 V3
 3  b  3  4
 4  b  4  2

$c
   V1 V2 V3
 5  c  5  2

Je voudrais émuler ceci avec group_by (quelque chose comme group_by(df,V1)) mais cela retourne un, grouped_df. Je sais que do devrait pouvoir m'aider, mais je ne suis pas sûr de son utilisation (voir aussi link pour une discussion.)

Notez que chaque nom est séparé en fonction du facteur utilisé pour établir ce groupe - il s'agit d'une fonction souhaitée (en définitive, des félicitations supplémentaires pour extraire ces noms de la liste des fichiers DFS).

19
MartinT

En comparant les solutions de base, plyr et dplyr, il semble que la solution de base soit beaucoup plus rapide!

library(plyr)
library(dplyr)   

df <- data_frame(Group1=rep(LETTERS, each=1000),
             Group2=rep(rep(1:10, each=100),26), 
             Value=rnorm(26*1000))

microbenchmark(Base=df %>%
             split(list(.$Group2, .$Group1)),
           dplyr=df %>% 
             group_by(Group1, Group2) %>% 
             do(data = (.)) %>% 
             select(data) %>% 
             lapply(function(x) {(x)}) %>% .[[1]],
           plyr=dlply(df, c("Group1", "Group2"), as.tbl),
           times=50) 

Donne:

Unit: milliseconds
  expr      min        lq      mean    median        uq       max neval
  Base 12.82725  13.38087  16.21106  14.58810  17.14028  41.67266    50
  dplyr 25.59038 26.66425  29.40503  27.37226  28.85828  77.16062   50
  plyr 99.52911  102.76313 110.18234 106.82786 112.69298 140.97568    50
14
Matifou

Pour «coller» à dplyr, vous pouvez également utiliser plyr au lieu de split:

library(plyr)

dlply(df, "V1", identity)
#$a
#  V1 V2 V3
#1  a  1  2
#2  a  2  3

#$b
#  V1 V2 V3
#1  b  3  4
#2  b  4  2

#$c
#  V1 V2 V3
#1  c  5  2
13
Colonel Beauvel

Vous pouvez obtenir une liste de trames de données à partir de group_by en utilisant do à condition de nommer la nouvelle colonne dans laquelle les trames de données seront stockées, puis de canaliser cette colonne dans lapply.

listDf = df %>% group_by(V1) %>% do(vals=data.frame(.)) %>% select(vals) %>% lapply(function(x) {(x)})
listDf[[1]]
#[[1]]
#  V1 V2 V3
#1  a  1  2
#2  a  2  3

#[[2]]
#  V1 V2 V3
#1  b  3  4
#2  b  4  2

#[[3]]
#  V1 V2 V3
#1  c  5  2
8
COrtega

Depuis dplyr 0.5.0.9000, la solution la plus courte qui utilise group_by() consiste probablement à suivre do avec une pull:

df %>% group_by(V1) %>% do(data=(.)) %>% pull(data)

Notez que, contrairement à split, ceci ne nomme pas les éléments de la liste résultante. Si cela est souhaité, vous voudrez probablement quelque chose comme

df %>% group_by(V1) %>% do(data = (.)) %>% with( set_names(data, V1) )

Pour éditorialiser un peu, je suis d'accord avec les gens qui disent que split() est la meilleure option. Personnellement, j'ai toujours trouvé gênant de devoir taper le nom du bloc de données deux fois (par exemple, split( potentiallylongname, potentiallylongname$V1 )), mais le problème est facilement résolu par le tuyau

df %>% split( .$V1 )
3
Artem Sokolov

group_split dans dplyr 0.8:

La version candidate 0.8 de dplyr a implémenté group_split en tant que fonction expérimentale: https://dplyr.tidyverse.org/reference/group_split.html

Il divise une image de données en groupes et renvoie une liste d'images de données. Chacune de ces images est un sous-ensemble des images d'origine définies par les catégories de la variable de division. 

Par exemple. Divisez le jeu de données iris par la variable Species et calculez les résumés de chaque sous-jeu de données:

> iris %>% 
+     group_split(Species) %>% 
+     map(summary)
[[1]]
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200   versicolor: 0  
 Median :5.000   Median :3.400   Median :1.500   Median :0.200   virginica : 0  
 Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246                  
 3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300                  
 Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600                  

[[2]]
  Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
 1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
 Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
 Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
 3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
 Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  

[[3]]
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400   setosa    : 0  
 1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800   versicolor: 0  
 Median :6.500   Median :3.000   Median :5.550   Median :2.000   virginica :50  
 Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026                  
 3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300                  
 Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500     

C'est également très utile pour déboguer un calcul sur des images imbriquées, car c'est un moyen rapide de "voir" ce qui se passe "dans" les calculs sur des images imbriquées. Ce n'est pas encore sur Cran, mais vous pouvez l'installer en utilisant devtools:

devtools::install_github("tidyverse/dplyr@rc_0.8.0")
0
Rasmus Larsen

Depuis dplyr 0.8 vous pouvez utiliser group_split

library(dplyr)
df = as.data.frame(cbind(c("a","a","b","b","c"),c(1,2,3,4,5), c(2,3,4,2,2)))
df %>% group_by(V1) %>% group_split()
#> [[1]]
#> # A tibble: 2 x 3
#>   V1    V2    V3   
#>   <fct> <fct> <fct>
#> 1 a     1     2    
#> 2 a     2     3    
#> 
#> [[2]]
#> # A tibble: 2 x 3
#>   V1    V2    V3   
#>   <fct> <fct> <fct>
#> 1 b     3     4    
#> 2 b     4     2    
#> 
#> [[3]]
#> # A tibble: 1 x 3
#>   V1    V2    V3   
#>   <fct> <fct> <fct>
#> 1 c     5     2
0
Moody_Mudskipper