una separado separada por lista dividir convertir comas columna campo r string split r-faq

separado - convertir una columna en una lista separada por comas



Separe las cadenas separadas por comas en una columna en filas separadas (4)

Esta vieja pregunta con frecuencia se está utilizando como blanco de engaño (etiquetado con r-faq ). Hasta el día de hoy, se ha respondido tres veces ofreciendo 6 enfoques diferentes, pero carece de un punto de referencia como orientación, que de los enfoques es el más rápido 1 .

Las soluciones de referencia incluyen

  • Enfoque de base R de Matthew Lundberg, pero modificado de acuerdo con el comentario de Rich Scriven ,
  • Jaap''s dos métodos de data.table Jaap''s y dos dplyr / tidyr ,
  • La solución de splitstackshape de Ananda ,
  • y dos variantes adicionales de los métodos data.table de Jaap.

En general, se compararon 8 métodos diferentes en 6 tamaños diferentes de marcos de datos utilizando el paquete microbenchmark (ver el código a continuación).

Los datos de muestra proporcionados por el OP consisten solo en 20 filas. Para crear marcos de datos más grandes, estas 20 filas simplemente se repiten 1, 10, 100, 1000, 10000 y 100000 veces, lo que da un tamaño de problema de hasta 2 millones de filas.

Resultados de referencia

Los resultados del índice de referencia muestran que, para marcos de datos suficientemente grandes, todos los métodos data.table son más rápidos que cualquier otro método. Para marcos de datos con más de 5000 filas, el método data.table de Jaap 2 y la variante DT3 son los más rápidos, las magnitudes más rápidas que los métodos más lentos.

Sorprendentemente, los tiempos de los dos métodos tidyverse y la solución splistackshape son tan similares que es difícil distinguir las curvas en el gráfico. Son los más lentos de los métodos de referencia en todos los tamaños de marcos de datos.

Para marcos de datos más pequeños, la base R de Matt y el método 4 de data.table parecen tener menos sobrecarga que los otros métodos.

Código

director <- c("Aaron Blaise,Bob Walker", "Akira Kurosawa", "Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", "Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu", "Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock", "Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik", "Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson", "Anne Fontaine", "Anthony Harvey") AB <- c("A", "B", "A", "A", "B", "B", "B", "A", "B", "A", "B", "A", "A", "B", "B", "B", "B", "B", "B", "A") library(data.table) library(magrittr)

Definir la función para las ejecuciones de referencia del tamaño del problema n

run_mb <- function(n) { # compute number of benchmark runs depending on problem size `n` mb_times <- scales::squish(10000L / n , c(3L, 100L)) cat(n, " ", mb_times, "/n") # create data DF <- data.frame(director = rep(director, n), AB = rep(AB, n)) DT <- as.data.table(DF) # start benchmarks microbenchmark::microbenchmark( matt_mod = { s <- strsplit(as.character(DF$director), '','') data.frame(director=unlist(s), AB=rep(DF$AB, lengths(s)))}, jaap_DT1 = { DT[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB ][!is.na(director)]}, jaap_DT2 = { DT[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director)][,.(director = V1, AB)]}, jaap_dplyr = { DF %>% dplyr::mutate(director = strsplit(as.character(director), ",")) %>% tidyr::unnest(director)}, jaap_tidyr = { tidyr::separate_rows(DF, director, sep = ",")}, cSplit = { splitstackshape::cSplit(DF, "director", ",", direction = "long")}, DT3 = { DT[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director)][, director := NULL][ , setnames(.SD, "V1", "director")]}, DT4 = { DT[, .(director = unlist(strsplit(as.character(director), ",", fixed = TRUE))), by = .(AB)]}, times = mb_times ) }

Ejecutar benchmark para diferentes tamaños de problema

# define vector of problem sizes n_rep <- 10L^(0:5) # run benchmark for different problem sizes mb <- lapply(n_rep, run_mb)

Prepare los datos para trazar

mbl <- rbindlist(mb, idcol = "N") mbl[, n_row := NROW(director) * n_rep[N]] mba <- mbl[, .(median_time = median(time), N = .N), by = .(n_row, expr)] mba[, expr := forcats::fct_reorder(expr, -median_time)]

Crear gráfico

library(ggplot2) ggplot(mba, aes(n_row, median_time*1e-6, group = expr, colour = expr)) + geom_point() + geom_smooth(se = FALSE) + scale_x_log10(breaks = NROW(director) * n_rep) + scale_y_log10() + xlab("number of rows") + ylab("median of execution time [ms]") + ggtitle("microbenchmark results") + theme_bw()

Información de la sesión y versiones del paquete (extracto)

devtools::session_info() #Session info # version R version 3.3.2 (2016-10-31) # system x86_64, mingw32 #Packages # data.table * 1.10.4 2017-02-01 CRAN (R 3.3.2) # dplyr 0.5.0 2016-06-24 CRAN (R 3.3.1) # forcats 0.2.0 2017-01-23 CRAN (R 3.3.2) # ggplot2 * 2.2.1 2016-12-30 CRAN (R 3.3.2) # magrittr * 1.5 2014-11-22 CRAN (R 3.3.0) # microbenchmark 1.4-2.1 2015-11-25 CRAN (R 3.3.3) # scales 0.4.1 2016-11-09 CRAN (R 3.3.2) # splitstackshape 1.4.2 2014-10-23 CRAN (R 3.3.3) # tidyr 0.6.1 2017-01-10 CRAN (R 3.3.2)

1 ¡ Mi curiosidad se despertó con este comentario exuberante Brilliant! Órdenes de magnitud más rápido! a una respuesta tidyverse de una pregunta que se cerró como un duplicado de esta pregunta.

Tengo un marco de datos, así:

data.frame(director = c("Aaron Blaise,Bob Walker", "Akira Kurosawa", "Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", "Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu", "Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock", "Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik", "Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson", "Anne Fontaine", "Anthony Harvey"), AB = c(''A'', ''B'', ''A'', ''A'', ''B'', ''B'', ''B'', ''A'', ''B'', ''A'', ''B'', ''A'', ''A'', ''B'', ''B'', ''B'', ''B'', ''B'', ''B'', ''A''))

Como puede ver, algunas entradas en la columna del director son nombres múltiples separados por comas. Me gustaría dividir estas entradas en filas separadas mientras mantengo los valores de la otra columna. Como ejemplo, la primera fila en el marco de datos anterior debe dividirse en dos filas, con un solo nombre en la columna del director y ''A'' en la columna AB .


Nombrando su data.frame v original, tenemos esto:

> s <- strsplit(as.character(v$director), '','') > data.frame(director=unlist(s), AB=rep(v$AB, sapply(s, FUN=length))) director AB 1 Aaron Blaise A 2 Bob Walker A 3 Akira Kurosawa B 4 Alan J. Pakula A 5 Alan Parker A 6 Alejandro Amenabar B 7 Alejandro Gonzalez Inarritu B 8 Alejandro Gonzalez Inarritu B 9 Benicio Del Toro B 10 Alejandro González Iñárritu A 11 Alex Proyas B 12 Alexander Hall A 13 Alfonso Cuaron B 14 Alfred Hitchcock A 15 Anatole Litvak A 16 Andrew Adamson B 17 Marilyn Fox B 18 Andrew Dominik B 19 Andrew Stanton B 20 Andrew Stanton B 21 Lee Unkrich B 22 Angelina Jolie B 23 John Stevenson B 24 Anne Fontaine B 25 Anthony Harvey A

Tenga en cuenta el uso de rep para construir la nueva columna AB. Aquí, sapply devuelve el número de nombres en cada una de las filas originales.


Tarde en la fiesta, pero otra alternativa generalizada es usar cSplit de mi paquete "splitstackshape" que tiene un argumento de direction . Establezca esto en "long" para obtener el resultado que especifique:

library(splitstackshape) head(cSplit(mydf, "director", ",", direction = "long")) # director AB # 1: Aaron Blaise A # 2: Bob Walker A # 3: Akira Kurosawa B # 4: Alan J. Pakula A # 5: Alan Parker A # 6: Alejandro Amenabar B


Varias alternativas:

1) dos formas con data.table :

library(data.table) # method 1 (preferred) setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB ][!is.na(director)] # method 2 setDT(v)[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director) ][,.(director = V1, AB)]

2) una combinación dplyr / tidyr : Alternativamente, también puede usar la combinación dplyr / tidyr :

library(dplyr) library(tidyr) v %>% mutate(director = strsplit(as.character(director), ",")) %>% unnest(director)

3) solo con tidyr : con tidyr 0.5.0 (y posterior), también puedes usar separate_rows :

separate_rows(v, director, sep = ",")

Puede usar el parámetro convert = TRUE para convertir números automáticamente en columnas numéricas.

4) con base R:

# if ''director'' is a character-column: stack(setNames(strsplit(df$director,'',''), df$AB)) # if ''director'' is a factor-column: stack(setNames(strsplit(as.character(df$director),'',''), df$AB))