2011-11-28 14 views
20

Me gustaría dividir un vector de cadenas de caracteres (nombres de personas) en dos columnas (vectores). El problema es que algunas personas tienen un apellido de 'dos ​​palabras'. Me gustaría dividir los nombres y apellidos en dos columnas. Puedo cortar y tomar los nombres usando el siguiente código, pero el apellido me elude. (observe la obs. 29 en el ejemplo de abajo para tener una idea ya que el Ford tiene un "apellido" de Pantera L que debe mantenerse unido)División de una cadena en el primer espacio

Lo que he intentado hacer hasta ahora;

x<-rownames(mtcars) 
unlist(strsplit(x, " .*")) 

Lo que me gustaría que se vea como:

  MANUF  MAKE 
27   Porsche  914-2 
28   Lotus  Europa 
29   Ford  Pantera L 
30   Ferrari  Dino 
31   Maserati Bora 
32   Volvo  142E 

Respuesta

25

La expresión regular rexp coincide con la palabra al comienzo de la cadena, un espacio opcional, y luego el resto de la cadena. Los paréntesis son subexpresiones a las que se accede como referencias posteriores \\1 y \\2.

rexp <- "^(\\w+)\\s?(.*)$" 
y <- data.frame(MANUF=sub(rexp,"\\1",x), MAKE=sub(rexp,"\\2",x)) 
tail(y) 
#  MANUF  MAKE 
# 27 Porsche  914-2 
# 28 Lotus Europa 
# 29  Ford Pantera L 
# 30 Ferrari  Dino 
# 31 Maserati  Bora 
# 32 Volvo  142E 
+0

@ Joshua Ullrich Hermoso. Gracias por la explicación también. –

0

Si usted puede hacer el patrón y juego en grupo, me gustaría probar algo como esto (no probado):

\s+(.*)\s+(.*) 
+4

Para que lo sepas, las expresiones regulares en R funcionan de forma un poco diferente. Por lo menos, necesitarías agregar otro \ delante de cada s, solo para evitar un error. – joran

0

Creo que la búsqueda de [^\s]+ funcionaría. No probado.

17

Para mí, la función de Hadley colsplit en el paquete reshape2 es el más intuitivo para este propósito. La forma de Joshua es más general (es decir, se puede usar donde sea que se use una expresión regular) y flexible (si se quiere cambiar la especificación); pero la función colsplit se adapta perfectamente a esta configuración específica:

library(reshape2) 
y <- colsplit(x," ",c("MANUF","MAKE")) 
tail(y) 
#  MANUF  MAKE 
#27 Porsche  914-2 
#28 Lotus Europa 
#29  Ford Pantera L 
#30 Ferrari  Dino 
#31 Maserati  Bora 
#32 Volvo  142E 
+0

Muy buen uso de columna dividida. Gracias. –

+0

+1 Realmente interesante, ya que había supuesto 'colsplit' devolverá más de tres columnas en este caso. Qué equivocado estaba yo. – Andrie

7

Sin embargo, otra manera de hacerlo:

str_split de stringr se encargará de la división, pero lo devuelve en una forma diferente (una lista, al igual strsplit hace). Manipular en la forma correcta es sencillo.

library(stringr) 
split_x <- str_split(x, " ", 2) 
(y <- data.frame(
    MANUF = sapply(split_x, head, n = 1), 
    MAKE = sapply(split_x, tail, n = 1) 
)) 

O, como Hadley menciona en los comentarios, con str_split_fixed.

y <- as.data.frame(str_split_fixed(x, " ", 2)) 
colnames(y) <- c("MANUF", "MAKE") 
y 
+0

@Riche una solución más a través de un paquete de Hadley Wickham. Gracias por compartir –

+2

Sería incluso mejor usar 'str_split_fixed' – hadley

+1

Es interesante observar que esta respuesta + el comentario de hadley está relacionado con la solución' colsplit' porque 'colsplit' usa' str_split_fixed'. –

11

Aquí hay dos enfoques:

1) strsplit. Este enfoque usa solo funciones en el núcleo de R y no expresiones regulares complejas. Sustituir el primer espacio con un punto y coma (usando sub y nogsub), strsplit en el punto y coma y luego rbind en una matriz 2 de la columna:

mat <- do.call("rbind", strsplit(sub(" ", ";", x), ";")) 
colnames(mat) <- c("MANUF", "MAKE") 

2) strapply en paquete gsubfn Aquí es un uno -liner utilizando strapply en el paquete gsubfn. Las dos partes entre paréntesis de la expresión regular capturan la primera y la segunda columnas deseadas, respectivamente, y la función (que se especifica en la notación de fórmulas, es lo mismo que especificar function(x, y) c(MANUF = x, MAKE = y)) las toma y agrega nombres.El argumento simplify=rbind se usa para convertirlo en una matriz como en la solución anterior.

library(gsubfn) 
mat <- strapply(x, "(\\S+)\\s+(.*)", ~ c(MANUF = x, MAKE = y), simplify = rbind) 

Nota: En cualquiera de los casos una matriz "character", mat, se devuelve. Si se desea una trama de datos de columnas "character" luego añadir lo siguiente:

DF <- as.data.frame(mat, stringsAsFactors = FALSE) 

omite el argumento stringsAsFactors si se quieren "factor" columnas.

+0

Acabo de volver aquí. De hecho, terminé gustando tu solución n. ° 1 la mejor de todas. Gracias y perdón por el posterior regreso. –

Cuestiones relacionadas