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).
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
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
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
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 )
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")
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