r list split dplyr

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.