Emule split() con dplyr group_by: devuelve una lista de marcos de datos
summarize count n()) (6)
Tengo un gran conjunto de datos que bloquea
split()
en R. Puedo usar
dplyr
group_by (que de todos modos es una forma preferida) pero no puedo persistir el
grouped_df
resultante como una lista de marcos de datos, un formato requerido por mi consecutivo pasos de procesamiento (necesito coaccionar a
SpatialDataFrames
y similares).
considere un conjunto de datos de muestra:
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)
devoluciones
$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
Me gustaría emular esto con
group_by
(algo así como
group_by(df,V1)
) pero esto devuelve uno,
grouped_df
.
Sé que
do
debería poder ayudarme, pero no estoy seguro sobre el uso (también vea el
link
para una discusión).
Tenga en cuenta que divide los nombres de cada lista por el nombre del factor que se ha utilizado para establecer este grupo; esta es una función deseada (en última instancia, felicitaciones adicionales para una forma de extraer estos nombres de la lista de dfs).
Comparando las soluciones base,
plyr
y
dplyr
, ¡parece que la base es mucho más rápida!
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)
Da:
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
Desde
dplyr 0.5.0.9000
, la solución más corta que usa
group_by()
es probablemente seguir con un
pull
:
df %>% group_by(V1) %>% do(data=(.)) %>% pull(data)
Tenga en cuenta que, a diferencia de la
split
, esto no nombra los elementos de la lista resultante.
Si esto se desea, entonces es probable que desee algo como
df %>% group_by(V1) %>% do(data = (.)) %>% with( set_names(data, V1) )
Para editorializar un poco, estoy de acuerdo con la gente que dice que
split()
es la mejor opción.
Personalmente, siempre me pareció molesto tener que escribir el nombre del marco de datos dos veces (p. Ej.,
split( potentiallylongname, potentiallylongname$V1 )
), pero el problema se evita fácilmente con la tubería:
df %>% split( .$V1 )
Desde
dplyr 0.8
puedes usar
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
Para ''pegar'' a dplyr, también puede usar
plyr
lugar 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
Puede obtener una lista de marcos de datos de
group_by
usando
do
siempre que nombre la nueva columna donde se almacenarán los marcos de datos y luego
lapply
esa columna en
lapply
.
> 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
group_split en dplyr 0.8:
La versión 0.8 de dplyr ha implementado
group_split
:
https://dplyr.tidyverse.org/reference/group_split.html
Divide un marco de datos por grupos, devuelve una lista de marcos de datos. Cada uno de estos marcos de datos son subconjuntos de los marcos de datos originales definidos por categorías de la variable de división.
Por ejemplo.
Divida el
iris
del conjunto de datos por la variable
Species
y calcule resúmenes de cada subconjunto de datos:
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
También es muy útil para depurar cálculos en marcos de datos anidados, porque es una forma rápida de "ver" lo que está sucediendo "dentro" de los cálculos en marcos de datos anidados.