2016-01-28 7 views
5

Ho una colonna riempita con altri nomi di colonna. Voglio ottenere il valore in ciascuno dei nomi delle colonne.Utilizzare la stringa per selezionare la colonna per riga in dplyr (o base R)

# three columns with values and one "key" column 
library(dplyr) 
data = data.frame(
    x = runif(10), 
    y = runif(10), 
    z = runif(10), 
    key = sample(c('x', 'y', 'z'), 10, replace=TRUE) 
) 

# now get the value named in 'key' 
data = data %>% mutate(value = VALUE_AT_COLUMN(key)) 

Sono abbastanza sicuro che la risposta ha a che fare con la versione eval pigra di mutare, ma non posso per la vita di me capire.

Qualsiasi aiuto sarebbe apprezzato.

+1

Si potrebbe anche provare 'data [c (" x "," y "," z ")] [cbind (seq_len (nrow (dati)), corrisponde (dati $ chiave, nomi (dati))) ] 'per evitare il raggruppamento per ogni riga (con il costo, probabilmente inferiore, di una conversione intermedia in" matrice "). –

+0

@alexis_laz Preferirei un approccio più estensibile che non richieda l'elencazione di ogni colonna. – sharoz

+1

A seconda di come sono ordinate le colonne, ci dovrebbero essere molti modi per estrarre programmaticamente quelli necessari; 'data [1: 3]', 'data [unique (data $ key)]', 'data [! nomi (dati)% in%" chiave "]' etc –

risposta

5

Ecco una soluzione Base R:

data$value = diag(as.matrix(data[,data$key])) 
+1

Interessante, anche se penso che questo non sia affatto efficiente in termini di memoria. –

+2

No! Potrebbe esserci un metodo di base più efficiente in termini di memoria, ma se si sta andando per prestazioni, si dovrebbe guardare a 'data.table' o' dplyr'. Se non vuoi caricare più pacchetti e i tuoi dati non sono enormi, allora funzionerà. –

6

Possiamo provare data.table. Converti 'data.frame' in 'data.table' (setDT(data)), raggruppato in base alla sequenza di righe, utilizziamo .SD per impostare sottoinsieme le colonne specificate da 'chiave'.

library(data.table) 
setDT(data)[, .SD[, key[[1L]], with=FALSE] ,1:nrow(data)] 

o un'altra opzione è get dopo la conversione della 'chiave' per character classe (come factor) dopo il raggruppamento per sequenza di righe come nel caso precedente.

setDT(data)[, get(as.character(key)), 1:nrow(data)] 

Qui è un'opzione con do

library(dplyr) 
data %>% 
    group_by(rn = row_number()) %>% 
    do(data.frame(., value= .[[.$key]])) 
+0

Posso farlo usando altre librerie, ma sto cercando di evitare di dover importare un'altra libreria di frame dati – sharoz

+0

@sharoz E 'possibile con 'dplyr' usando' do', – akrun

+2

Potresti postarla come risposta? – sharoz

4

Si sente decisamente come ci dovrebbe essere una soluzione di base R per questo, ma il meglio che potevo fare era con tidyr, alla prima di trasformare i dati in forma estesa, quindi filtrare solo per quelle osservazioni che corrispondono alla chiave desiderata.

data %>% 
    add_rownames("index") %>% 
    gather(var, value, -index, -key) %>% 
    filter(key == var) 

Una soluzione R base che quasi così:

data[cbind(seq_along(data$key), data$key)] 

per i dati, lo fa opere, ma perché utilizza una matrice, ha due gravi problemi. Uno è che l'ordine del fattore è importante, perché è solo la coercizione e la selezione delle colonne per livello di fattore, non per il nome della colonna. L'altro è che l'output risultante è un character, non uno numeric, perché nella conversione in una matrice, il tipo character viene scelto a causa della colonna key. Il problema principale è che non c'è data.frame analogico al comportamento matrice di

Quando indicizzazione array di '[' un singolo argomento 'i' può essere una matrice con tante colonne quante sono le dimensioni di 'x' ; il risultato è quindi un vettore con elementi corrispondenti all'insieme di indici in ogni riga di "i".

Dati questi problemi, probabilmente andare con la soluzione tidyr, poiché il fatto che le colonne sono mezzi variabilmente selezionabili che probabilmente rappresentano diverse osservazioni per la stessa unità osservabile.

5

per una memoria soluzione efficiente e veloce, è necessario aggiornare i dati originali.tavolo eseguendo un join come segue:

data[.(key2 = unique(key)), val := get(key2), on=c(key="key2"), by=.EACHI][] 

Per ogni key2 le righe corrispondenti nella data$key sono calcolati. Queste righe vengono aggiornate con i valori della colonna contenuta in key2. Ad esempio, key2="x" corrisponde alle righe 1,2,6,8,10. I valori corrispondenti di data$x sono data$x[c(1,2,6,8,10)]. by=.EACHI assicura che l'espressione get(key2) venga eseguita per ogni valore di key2.

Poiché questa operazione viene eseguita solo su valori univoci, dovrebbe essere considerevolmente più veloce di eseguirla in termini di righe. E dal momento che data.table è aggiornato per riferimento, dovrebbe essere abbastanza efficiente in termini di memoria (e che contribuisce anche alla velocità).