2009-08-31 11 views
31

Podría resolver esto usando bucles, pero estoy intentando pensar en vectores para que mi código sea más R-esque.Cómo evitar un bucle en R: seleccionando elementos de una lista

Tengo una lista de nombres. El formato es firstname_lastname. Quiero sacar de esta lista una lista separada con solo los nombres. Parece que no puedo entender cómo hacer esto. He aquí algunos datos de ejemplo:

t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 
tsplit <- strsplit(t,"_") 

que se parece a esto:

> tsplit 
[[1]] 
[1] "bob" "smith" 

[[2]] 
[1] "mary" "jane" 

[[3]] 
[1] "jose" "chung" 

[[4]] 
[1] "michael" "marx" 

[[5]] 
[1] "charlie" "ivan" 

que pudiera salir lo que quiero mediante bucles de este tipo:

for (i in 1:length(tsplit)){ 
    if (i==1) {t_out <- tsplit[[i]][1]} else{t_out <- append(t_out, tsplit[[i]][1])} 
} 

que me darían esto:

t_out 
[1] "bob"  "mary" "jose" "michael" "charlie" 

Entonces, ¿cómo puedo hacer esto sin bucles?

+2

Por cierto que puede ser útil si pudiera detalle cómo esto es diferente de sus preguntas anteriores sobre el mismo tema: http://stackoverflow.com/questions/439526/thinking-in -vectores-con-r http://stackoverflow.com/questions/1246244/r-using-the-apply-function-on-a-data-frame-help-me-get-my-vector-victor http://stackoverflow.com/questions/445059/vectorize-my-thinking-vector-operations-in-r –

+4

¿te refieres a mi total incapacidad para realmente aprender a aplicar funciones en R? Sí, el mismo problema, diferente matiz. Gracias por recordarme. –

Respuesta

26

Puede utilizar apply (o sapply)

t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 
f <- function(s) strsplit(s, "_")[[1]][1] 
sapply(t, f) 

bob_smith mary_jane jose_chung michael_marx charlie_ivan 

     "bob"  "mary"  "jose" "michael" "charlie" 

Ver: A brief introduction to “apply” in R

4

Casi lo tienes. Se realmente es sólo una cuestión de

  1. usando uno de los *apply funciones para recorrer la lista existente, a menudo comienzo con lapply y, a veces cambio a sapply
  2. añadir una función anónima que opera en una de los elementos de la lista a la vez
  3. que ya sabían que era strsplit(string, splitterm) y que necesita algún que otro [[1]][1] para encargarse del primer término de la respuesta
  4. sólo hay que poner todo junto, comenzando con una vari preferido namne poder (ya que mantenerse alejados de t o c y amigos)

que da

> tlist <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 
> fnames <- sapply(tlist, function(x) strsplit(x, "_")[[1]][1]) 
> fnames 
    bob_smith mary_jane jose_chung michael_marx charlie_ivan 
     "bob"  "mary"  "jose" "michael" "charlie" 
> 
+0

Realmente he tenido problemas para hacer que mi mente funcione apropiadamente usando las funciones de aplicar en R. Algunos días se siente como aprender a conducir en el lado opuesto de la carretera ... en realidad no es difícil, pero las simples vueltas resultan en una atasco de registro mental. –

+1

Lo hago de forma similar a las piernas. Usted sabía strsplit. Sabía que necesitaba una 'función anónima' de un parámetro para la familia de aplicaciones. Solo pegúelos juntos ... Finalmente, y no para elegir, publiqué esto antes de la respuesta esencialmente idéntica pero menos detallada que usted aceptó como "la" respuesta. –

+0

Typo: 'lego-alike', no 'leg -like' –

3

usted podría utilizar unlist():

> tsplit <- unlist(strsplit(t,"_")) 
> tsplit 
[1] "bob"  "smith" "mary" "jane" "jose" "chung" "michael" 
[8] "marx" "charlie" "ivan" 
> t_out <- tsplit[seq(1, length(tsplit), by = 2)] 
> t_out 
[1] "bob"  "mary" "jose" "michael" "charlie" 

Puede haber una mejor manera de sacar solo las entradas de índice impar, pero en cualquier caso no tendrá un bucle.

+0

No es ideal ya que necesita imponer 'by = 2' para elegir los elementos coincidentes. –

7

Dudo que esto es la solución más elegante, pero es mejor que un bucle:

t.df <- data.frame(tsplit) 
t.df[1, ] 

conversión de listas de tramas de datos se trata de la única manera que puedo conseguir que hagan lo que yo quiero. Estoy deseando leer las respuestas de personas que realmente entienden cómo manejar listas.

+0

Me gusta esto. Obtengo la estructura de data.frame. Y como mis datos reales tienen la misma cantidad de elementos en cada "nombre", esto no debería ser menos eficiente desde el punto de vista de la memoria. ¡Por qué no pensé en esto! –

+0

Tenga en cuenta que este enfoque lleva muchísimo tiempo con datos más grandes; consulte mi comentario sobre la respuesta de William Doane. –

9

¿Qué tal:

tlist <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
fnames <- gsub("(_.*)$", "", tlist)
# _.* matches the underscore followed by a string of characters
# the $ anchors the search at the end of the input string
# so, underscore followed by a string of characters followed by the end of the input string

para el enfoque RegEx?

+1

+1 por ser el más rápido. Con rep (t, 1e4), mi enfoque tardó 83,23 segundos (¡81,41 de los cuales se pasaron convirtiendo a un marco de datos!), David tomó 4,39 segundos y el tuyo tomó 0,81. Creo que tiene la mejor salida, también. –

+1

Gracias, Matt ... ¡Me preguntaba sobre la eficacia de cada una de estas soluciones! –

+1

eso es realmente informativo. Acababa de asumir que el bit strsplit era un hecho. Guau. Es bueno ver otra forma de hacerlo. –

2

Y otro enfoque, basado en el ejemplo no listados de brentonk ...

tlist <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
tsplit <- unlist(strsplit(tlist,"_"))
fnames <- tsplit[seq(1:length(tsplit))%%2 == 1]

43

Y una más enfoque:

t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 
pieces <- strsplit(t,"_") 
sapply(pieces, "[", 1) 

En palabras, la última línea extrae el primer elemento de cada componente de la lista y luego lo simplifica en un vector.

¿Cómo funciona esto? Bueno, debe darse cuenta de que una forma alternativa de escribir x[1] es "["(x, 1), es decir, hay una función llamada [ que hace subconjuntos. La llamada sapply aplica llamadas a esta función una vez para cada elemento de la lista original, pasando dos argumentos, el elemento de lista y 1.

La ventaja de este enfoque sobre los demás es que puede extraer múltiples elementos de la lista sin tener que volver a calcular las divisiones. Por ejemplo, el apellido sería sapply(pieces, "[", 2). Una vez que te acostumbras a este idioma, es bastante fácil de leer.

+0

Hadley, veo que esto funciona, pero no tengo la menor idea de por qué funciona. ¿Hay un "]" implícito de alguna manera? ¿Puedes elaborar un poco? Mi R-foo es claramente débil. –

+0

Estaba un poco sorprendido por esto, JD ... así que después de jugar un poco, veo que:> "[" (piezas, 1) produce [[1]] [1] "bob" "smith". ... una notación interesante, seguro, ¡y muy útil! –

+0

Como nota al margen, si va a dividir cadenas fijas en lugar de expresiones regulares, puede considerar pasar 'fixed = TRUE' a' strsplit'. Descubrí que esto puede tener un gran impacto en la velocidad de 'strsplit'. –

9

qué pasa:

t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 

sub("_.*", "", t) 
+0

que funciona totalmente! Gracias. –

1

me gustaría utilizar el siguiente no listados() - Método basado:

> t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 
> tsplit <- strsplit(t,"_") 
> 
> x <- matrix(unlist(tsplit), 2) 
> x[1,] 
[1] "bob"  "mary" "jose" "michael" "charlie" 

La gran ventaja de este método es que resuelve el problema equivalente para los apellidos en el mismo tiempo:

> x[2,] 
[1] "smith" "jane" "chung" "marx" "ivan" 

Lo malo es que tendrá que estar seguro de que todos los nombres se ajustan a la estructura firstname_lastname; si alguno no lo hace, entonces este método se romperá.

0

de la lista de objetos tsplit original dado al principio, este comando lo hará:

unlist(lapply(tsplit,function(x) x[1])) 

extrae el primer elemento de todos los elementos de la lista, a continuación, se transforma una lista a un vector. Deslisar primero en una matriz, luego extraer la primera columna también está bien, pero luego depende del hecho de que todos los elementos de la lista tengan la misma longitud.Aquí está la salida:

> tsplit 

[[1]] 
[1] "bob" "smith" 

[[2]] 
[1] "mary" "jane" 

[[3]] 
[1] "jose" "chung" 

[[4]] 
[1] "michael" "marx" 

[[5]] 
[1] "charlie" "ivan" 

> lapply(tsplit,function(x) x[1]) 

[[1]] 
[1] "bob" 

[[2]] 
[1] "mary" 

[[3]] 
[1] "jose" 

[[4]] 
[1] "michael" 

[[5]] 
[1] "charlie" 

> unlist(lapply(tsplit,function(x) x[1])) 

[1] "bob"  "mary" "jose" "michael" "charlie" 
Cuestiones relacionadas