2012-06-05 16 views
5

Ho un vettore che mi dice, per ogni riga in un frame data, l'indice di colonna per il quale il valore di questa riga deve essere aggiornato.Scegliere una cella per riga in frame di dati

> set.seed(12008); n <- 10000; d <- data.frame(c1=1:n, c2=2*(1:n), c3=3*(1:n)) 
> i <- sample.int(3, n, replace=TRUE) 
> head(d); head(i) 
    c1 c2 c3 
1 1 2 3 
2 2 4 6 
3 3 6 9 
4 4 8 12 
5 5 10 15 
6 6 12 18 
[1] 3 2 2 3 2 1 

Ciò significa che per le righe 1 e 4, c3 deve essere aggiornato; per le righe 2, 3 e 5, c2 dovrebbe essere aggiornato (tra gli altri). Qual è il modo più pulito per ottenere questo in R usando operazioni vettorializzate, cioè senza apply e amici? EDIT: E, se possibile, senza R loop?

Ho pensato di trasformare d in una matrice e quindi di indirizzare gli elementi della matrice utilizzando un vettore unidimensionale. Ma poi non ho trovato un modo pulito per calcolare l'indirizzo unidimensionale dagli indici di riga e colonna.

risposta

3

Se siete disposti prima convertire il vostro data.frame ad una matrice, è possibile indicizzare gli elementi-to essere sostituito con una matrice a due colonne. (Cominciando con , questo sarà possibile con data.frames direttamente.) La matrice di indicizzazione dovrebbe avere indici riga nei suoi primi indici colonna e colonna nella seconda colonna.

Ecco un esempio:

## Create a subset of the your data 
set.seed(12008); n <- 6 
D <- data.frame(c1=1:n, c2=2*(1:n), c3=3*(1:n)) 
i <- seq_len(nrow(D))   # vector of row indices 
j <- sample(3, n, replace=TRUE) # vector of column indices 
ij <- cbind(i, j)    # a 2-column matrix to index a 2-D array 
           # (This extends smoothly to higher-D arrays.) 

## Convert it to a matrix  
Dmat <- as.matrix(D) 

## Replace the elements indexed by 'ij' 
Dmat[ij] <- NA 
Dmat 
#  c1 c2 c3 
# [1,] 1 2 NA 
# [2,] 2 NA 6 
# [3,] 3 NA 9 
# [4,] 4 8 NA 
# [5,] 5 NA 15 
# [6,] NA 12 18 

A cominciare da , si sarà in grado di utilizzare la stessa sintassi per dataframes (vale a dire senza dover convertire prima dataframes a matrici).

Dal file R-develNEWS:

Matrix indicizzazione di dataframes da due colonne indici numerici è ora supportata per la sostituzione così come l'estrazione.

Utilizzando l'attuale R-devel snapshot, ecco cosa che assomiglia:

D[ij] <- NA 
D 
# c1 c2 c3 
# 1 1 2 NA 
# 2 2 NA 6 
# 3 3 NA 9 
# 4 4 8 NA 
# 5 5 NA 15 
# 6 NA 12 18 
+0

È stato trasferito sul ramo 2.15.1? R-devel di solito significherebbe la prossima versione minore, cioè 2.16.x. –

+0

@GavinSimpson - Bella cattura. Grazie. Guardando ancora, vedo ora la nota importante che "lo snapshot di sviluppo r59537 di R [...] alla fine diventerà R-2.16.0". Modificherà il mio post di conseguenza. –

+0

Ci sarà anche un supporto analogo per matrici/array? – krlmlr

3

Ecco un modo:

d[which(i == 1), "c1"] <- "one" 
d[which(i == 2), "c2"] <- "two" 
d[which(i == 3), "c3"] <- "three" 

    c1 c2 c3 
1 1 2 three 
2 2 two  6 
3 3 two  9 
4 4 8 three 
5 5 two 15 
6 one 12 18 
+0

Grazie. Ciò richiede un ciclo sulle colonne, che non è male. Eppure, esiste una soluzione completamente vettoriale? – krlmlr

4

con i tuoi dati di esempio, e utilizzando solo le prime righe (D e I sotto) si può facilmente fare ciò che si vuole tramite una matrice, come si intuisce.

set.seed(12008) 
n <- 10000 
d <- data.frame(c1=1:n, c2=2*(1:n), c3=3*(1:n)) 
i <- sample.int(3, n, replace=TRUE) 
## just work with small subset 
D <- head(d) 
I <- head(i) 

Innanzitutto, convertire D in una matrice:

dmat <- data.matrix(D) 

Successivo calcolare gli indici del vettore rappresentazione della matrice corrispondente a righe e colonne indicate da I. Per questo, è facile generare gli indici di riga e l'indice di colonna (in I) utilizzando seq_along(I) che in questo semplice esempio è il vettore 1:6. Per calcolare gli indici vettore possiamo usare:

(I - 1) * nrow(D) + seq_along(I) 

dove la prima parte ((I - 1) * nrow(D)) ci dà la corretta multiplo del numero di righe (6 qui) per indicizzare l'inizio della colonna I esima. Abbiamo poi aggiungiamo l'indice di riga per ottenere l'indice per l'elemento n-esimo nella colonna I esimo.

Utilizzando questo, abbiamo appena indicizzato in dmat utilizzando "[", trattandolo come un vettore. La versione sostituzione di "[" ("[<-") ci permette di fare la sostituzione in una sola riga. Qui sostituisco gli elementi indicati con NA per rendere più facile vedere che sono stati individuati gli elementi corretti:

> dmat 
    c1 c2 c3 
1 1 2 3 
2 2 4 6 
3 3 6 9 
4 4 8 12 
5 5 10 15 
6 6 12 18 
> dmat[(I - 1) * nrow(D) + seq_along(I)] <- NA 
> dmat 
    c1 c2 c3 
1 1 2 NA 
2 2 NA 6 
3 3 NA 9 
4 4 8 NA 
5 5 NA 15 
6 NA 12 18 
+0

Grazie. Ma questo costrutto '(I - 1) * nrow (D) + seq_along (I)' è incapsulato in una funzione pubblicamente accessibile? (Più in generale, sto cercando qualcosa come 'matrix.index (m, r, c)' dove 'r' è il vettore riga e' c' è il vettore colonna.Io so come costruirlo, ma questo deve essere in R core da qualche parte, no?) Come funziona l'indirizzamento della matrice internamente? – krlmlr

+0

No, non lo è. 'I' è la colonna (' c' nella tua notazione), 'seq_along (I)' è la riga (o 'r'). Ho usato le cose che ho fatto a causa del tuo esempio, sebbene 'i' sia un vettore fintanto che il numero di righe in base al tuo esempio, quindi il mio codice funziona ancora anche per i grossi' i'. Per l'ultimo bit, studiare il codice C o le documentazioni R Internals; è tutto fatto in C, ma si noti che per quanto riguarda R, una matrice è solo un vettore con elementi impilati a colonne, cioè le colonne vengono riempite per prime, quindi quando si considera una matrice come un vettore, tutte le righe di colonna 1 vengono prima , quindi le righe della colonna 2 ecc. –

+0

@ user946850 Detto questo, non c'è nulla che ti impedisca di scrivere un 'matrixIndex()' usando l'esempio mostrato sopra. Puoi metterlo nel tuo pacchetto privato e caricarlo (o predisporre il caricamento automatico) all'inizio di ogni sessione R. –

Problemi correlati