Come evitare un loop in R: selezionare elementi da un elenco

Potrei risolvere questo usando loop, ma sto cercando di pensare in vettori, quindi il mio codice sarà più R-esque.

Ho una lista di nomi. Il formato è firstname_lastname. Voglio uscire da questa lista in una lista separata con solo i primi nomi. Non riesco a pensare a come farlo. Ecco alcuni dati di esempio:

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

che assomiglia a questo:

 > tsplit [[1]] [1] "bob" "smith" [[2]] [1] "mary" "jane" [[3]] [1] "jose" "chung" [[4]] [1] "michael" "marx" [[5]] [1] "charlie" "ivan" 

Potrei uscire da quello che voglio usando loop come questo:

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

che mi darebbe questo:

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

Quindi, come posso fare questo senza loop?

Puoi usare 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" 

Vedi: una breve introduzione a "apply" in R

E un altro approccio:

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

In parole, l'ultima riga estrae il primo elemento di ciascun componente dell'elenco e quindi lo semplifica in un vettore.

Come funziona? Bene, devi realizzare un modo alternativo di scrivere x[1] è "["(x, 1) , cioè c'è una funzione chiamata [ che fa subsettings. La chiamata sapply applica chiamate questa funzione una volta per ogni elemento dell'elenco originale, passando in due argomenti, l'elemento elenco e 1.

Il vantaggio di questo approccio rispetto agli altri è che è ansible estrarre più elementi dall'elenco senza dover ricalcolare le suddivisioni. Ad esempio, il cognome sarebbe sapply(pieces, "[", 2) . Una volta che ti abitui a questo idioma, è piuttosto facile da leggere.

Che ne dite di:

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

per l'approccio RegEx?

che dire:

 t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") sub("_.*", "", t) 

Dubito che questa sia la soluzione più elegante, ma batte il loop:

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

Convertire le liste in frame di dati è l'unico modo in cui posso convincerli a fare ciò che voglio. Non vedo l'ora di leggere le risposte di persone che effettivamente comprendono come gestire le liste.

L’hai quasi avuto. È davvero solo una questione di

  1. usando una delle funzioni *apply per passare in sapply tuo elenco esistente, spesso inizio con lapply e a volte passa a sapply
  2. aggiungi una funzione anonima che opera su uno degli elementi dell’elenco alla volta
  3. sapevi già che era strsplit(string, splitterm) e che ti serviva lo strano [[1]][1] per scegliere il primo termine della risposta
  4. metti tutto insieme, iniziando con una variabile preferita namne (manchiamo di t o c e amici)

che dà

 > 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" > 

Puoi usare 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" 

Potrebbe esserci un modo migliore per estrarre solo le voci indicizzate dispari, ma in ogni caso non avrai un ciclo.

E un altro approccio, basato sull’esempio non elencato di 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]

Vorrei usare il seguente metodo unlist ():

 > 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" 

Il grande vantaggio di questo metodo è che risolve il problema equivalente per i cognomi allo stesso tempo:

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

Lo svantaggio è che devi essere sicuro che tutti i nomi siano conformi alla struttura firstname_lastname ; in caso contrario, questo metodo si interromperà.

tsplit list tsplit originale dato all’inizio, questo comando farà:

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

estrae il primo elemento di tutti gli elementi dell’elenco, quindi trasforma un elenco in un vettore. Non elencare prima una matrice, quindi estrarre la colonna del pugno è ok, ma in questo caso si dipende dal fatto che tutti gli elementi della lista abbiano la stessa lunghezza. Ecco l’output:

 > 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"