Pregunta

Me gustaría tomar los datos de la forma

before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))
  attr          type
1    1   foo_and_bar
2   30 foo_and_bar_2
3    4   foo_and_bar
4    6 foo_and_bar_2

y el uso split() en el "type" columna desde arriba para obtener algo como esto:

  attr type_1 type_2
1    1    foo    bar
2   30    foo  bar_2
3    4    foo    bar
4    6    foo  bar_2

Se me ocurrió algo increíblemente complejo que implica alguna forma de apply que funcionaba, pero desde que he extraviado eso. Se parecía demasiado complicado para ser la mejor manera. Puedo usar strsplit como abajo, pero no está claro cómo conseguir que de nuevo en 2 columnas en la trama de datos.

> strsplit(as.character(before$type),'_and_')
[[1]]
[1] "foo" "bar"

[[2]]
[1] "foo"   "bar_2"

[[3]]
[1] "foo" "bar"

[[4]]
[1] "foo"   "bar_2"

Gracias por cualquier punteros. No he bastante groked listas de R por el momento.

¿Fue útil?

Solución

Uso stringr::str_split_fixed

library(stringr)
str_split_fixed(before$type, "_and_", 2)

Otros consejos

Otra opción es utilizar el nuevo paquete tidyr.

library(dplyr)
library(tidyr)

before <- data.frame(
  attr = c(1, 30 ,4 ,6 ), 
  type = c('foo_and_bar', 'foo_and_bar_2')
)

before %>%
  separate(type, c("foo", "bar"), "_and_")

##   attr foo   bar
## 1    1 foo   bar
## 2   30 foo bar_2
## 3    4 foo   bar
## 4    6 foo bar_2

5 años después de añadir la solución data.table obligatoria

library(data.table) ## v 1.9.6+ 
setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_")]
before
#    attr          type type1 type2
# 1:    1   foo_and_bar   foo   bar
# 2:   30 foo_and_bar_2   foo bar_2
# 3:    4   foo_and_bar   foo   bar
# 4:    6 foo_and_bar_2   foo bar_2

También podríamos tanto asegurarse de que las columnas resultantes tendrán tipos correctos y mejorar el rendimiento mediante la adición de type.convert y fixed argumentos (ya "_and_" no es realmente una expresión regular)

setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_", type.convert = TRUE, fixed = TRUE)]

Sin embargo, otro enfoque: el uso rbind en out:

before <- data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))  
out <- strsplit(as.character(before$type),'_and_') 
do.call(rbind, out)

     [,1]  [,2]   
[1,] "foo" "bar"  
[2,] "foo" "bar_2"
[3,] "foo" "bar"  
[4,] "foo" "bar_2"

Y para combinar:

data.frame(before$attr, do.call(rbind, out))

Observe que sapply con "[" puede ser utilizado para extraer cualquiera de los primeros o segundos elementos en esas listas así:

before$type_1 <- sapply(strsplit(as.character(before$type),'_and_'), "[", 1)
before$type_2 <- sapply(strsplit(as.character(before$type),'_and_'), "[", 2)
before$type <- NULL

Y aquí está un método gsub:

before$type_1 <- gsub("_and_.+$", "", before$type)
before$type_2 <- gsub("^.+_and_", "", before$type)
before$type <- NULL

aquí es un un trazador de líneas a lo largo de las mismas líneas que la solución de aniko, pero utilizando el paquete stringr de Hadley:

do.call(rbind, str_split(before$type, '_and_'))

Para añadir a las opciones, también se podría usar mi función splitstackshape::cSplit como esto:

library(splitstackshape)
cSplit(before, "type", "_and_")
#    attr type_1 type_2
# 1:    1    foo    bar
# 2:   30    foo  bar_2
# 3:    4    foo    bar
# 4:    6    foo  bar_2

Una forma sencilla es utilizar sapply() y la función [:

before <- data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))
out <- strsplit(as.character(before$type),'_and_')

Por ejemplo:

> data.frame(t(sapply(out, `[`)))
   X1    X2
1 foo   bar
2 foo bar_2
3 foo   bar
4 foo bar_2

resultado de sapply() es una matriz y necesita la transposición y la fundición de nuevo a una trama de datos. Es entonces algunas manipulaciones simples que producen el resultado que quería:

after <- with(before, data.frame(attr = attr))
after <- cbind(after, data.frame(t(sapply(out, `[`))))
names(after)[2:3] <- paste("type", 1:2, sep = "_")

En este punto, after es lo que quería

> after
  attr type_1 type_2
1    1    foo    bar
2   30    foo  bar_2
3    4    foo    bar
4    6    foo  bar_2

Aquí es un forro de una base de R que se superpone una serie de soluciones anteriores, pero devuelve un data.frame con los nombres propios.

out <- setNames(data.frame(before$attr,
                  do.call(rbind, strsplit(as.character(before$type),
                                          split="_and_"))),
                  c("attr", paste0("type_", 1:2)))
out
  attr type_1 type_2
1    1    foo    bar
2   30    foo  bar_2
3    4    foo    bar
4    6    foo  bar_2

Utiliza strsplit para romper la variable, y data.frame con do.call / rbind para poner la parte posterior de datos en un hoja.de.datos. La mejora incremental adicional es el uso de setNames añadir los nombres de variables a la hoja.de.datos.

El tema es casi agotado, me gustaría pesar de ofrecer una solución a una versión ligeramente más general en el que no se conoce el número de columnas de salida, a priori. Así, por ejemplo, tiene

before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2', 'foo_and_bar_2_and_bar_3', 'foo_and_bar'))
  attr                    type
1    1             foo_and_bar
2   30           foo_and_bar_2
3    4 foo_and_bar_2_and_bar_3
4    6             foo_and_bar

No se puede utilizar dplyr separate() porque no sabemos el número de las columnas de resultados antes de la división, por lo que a continuación, he creado una función que utiliza stringr para dividir una columna, dado el patrón y un prefijo para el nombre columnas generadas. Espero que los patrones de codificación utilizado, son correctos.

split_into_multiple <- function(column, pattern = ", ", into_prefix){
  cols <- str_split_fixed(column, pattern, n = Inf)
  # Sub out the ""'s returned by filling the matrix to the right, with NAs which are useful
  cols[which(cols == "")] <- NA
  cols <- as.tibble(cols)
  # name the 'cols' tibble as 'into_prefix_1', 'into_prefix_2', ..., 'into_prefix_m' 
  # where m = # columns of 'cols'
  m <- dim(cols)[2]

  names(cols) <- paste(into_prefix, 1:m, sep = "_")
  return(cols)
}

A continuación, puede utilizar split_into_multiple en una tubería dplyr de la siguiente manera:

after <- before %>% 
  bind_cols(split_into_multiple(.$type, "_and_", "type")) %>% 
  # selecting those that start with 'type_' will remove the original 'type' column
  select(attr, starts_with("type_"))

>after
  attr type_1 type_2 type_3
1    1    foo    bar   <NA>
2   30    foo  bar_2   <NA>
3    4    foo  bar_2  bar_3
4    6    foo    bar   <NA>

Y entonces podemos utilizar gather para poner en orden ...

after %>% 
  gather(key, val, -attr, na.rm = T)

   attr    key   val
1     1 type_1   foo
2    30 type_1   foo
3     4 type_1   foo
4     6 type_1   foo
5     1 type_2   bar
6    30 type_2 bar_2
7     4 type_2 bar_2
8     6 type_2   bar
11    4 type_3 bar_3

Desde R versión 3.4.0 se puede utilizar strcapture() de la paquete utils (incluido con instalaciones de base R), de unión a la salida en la otra columna (s).

out <- strcapture(
    "(.*)_and_(.*)",
    as.character(before$type),
    data.frame(type_1 = character(), type_2 = character())
)

cbind(before["attr"], out)
#   attr type_1 type_2
# 1    1    foo    bar
# 2   30    foo  bar_2
# 3    4    foo    bar
# 4    6    foo  bar_2

Esta pregunta es bastante viejo, pero voy a añadir la solución que encontré al ser la más simple en la actualidad.

library(reshape2)
before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))
newColNames <- c("type1", "type2")
newCols <- colsplit(before$type, "_and_", newColNames)
after <- cbind(before, newCols)
after$type <- NULL
after

Otro enfoque si desea seguir con strsplit() es utilizar el comando unlist(). He aquí una solución en ese sentido.

tmp <- matrix(unlist(strsplit(as.character(before$type), '_and_')), ncol=2,
   byrow=TRUE)
after <- cbind(before$attr, as.data.frame(tmp))
names(after) <- c("attr", "type_1", "type_2")

base, pero probablemente lenta:

n <- 1
for(i in strsplit(as.character(before$type),'_and_')){
     before[n, 'type_1'] <- i[[1]]
     before[n, 'type_2'] <- i[[2]]
     n <- n + 1
}

##   attr          type type_1 type_2
## 1    1   foo_and_bar    foo    bar
## 2   30 foo_and_bar_2    foo  bar_2
## 3    4   foo_and_bar    foo    bar
## 4    6 foo_and_bar_2    foo  bar_2
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top