2011-08-03 20 views
23

Esto es probablemente simple de resolver. Tengo una matriz 2D mat con 500 filas × 335 columnas y un data.frame dat con 120425 filas. El data.frame dat tiene dos columnas I y J, que son números enteros para indexar la fila, columna de mat. Me gustaría agregar los valores de mat a las filas de dat.Valores de índice de una matriz usando fila, col indicies

Aquí es mi conceptual fallan:

> dat$matval <- mat[dat$I, dat$J] 
Error: cannot allocate vector of length 1617278737 

(estoy usando R 2.13.1 en Win32). Profundizando un poco más, veo que mal uso de la indexación matricial, ya que parece que solo obtengo una submatriz de mat, y no una matriz de valores de una dimensión como esperaba, es decir:

> str(mat[dat$I[1:100], dat$J[1:100]]) 
int [1:100, 1:100] 20 1 1 1 20 1 1 1 1 1 ... 

Esperaba algo como int [1:100] 20 1 1 1 20 1 1 1 1 1 .... ¿Cuál es la forma correcta de indexar una matriz 2D usando índices de fila, columna para obtener los valores?

+0

+1 para una pregunta interesante (que plantea otra pregunta: ¿por qué no hay una opción para cambiar el comportamiento a algo un poco más como esto al pasar el '' 'operadores N vectores para una N-dimensional matriz?) –

+0

Buena pregunta - Lo edité muy ligeramente para arreglar lo que * creo * es un error tipográfico ('datI' a' dat $ I'). Si esto no es lo que quería decir, siéntase libre de deshacer ... – joran

Respuesta

37

Casi. Necesita ser ofrecido a "[" como una matriz de dos columnas:

dat$matval <- mat[ cbind(dat$I, dat$J) ] # should do it. 

hay una advertencia: Aunque esto también funciona para tramas de datos, que son obligados en primer lugar a la matriz de clase y si hay alguno no numérico, el toda la matriz se convierte en la clase de "menor denominador".

+1

+1 para encontrar la forma en que R claramente tenía la intención de hacer las cosas ;-) –

+0

Entonces, si 'I' y' J' son las únicas columnas, es solo ' mat [dat] 'suficiente? ¿O necesitas forzar a una matriz? – joran

+1

Parece que la coerción es necesaria ya que el marco de datos es realmente una lista. Entonces también puedes hacer 'as.matrix (dat)'. – joran

10

Usando una matriz de índice como sugiere Dwin es por supuesto mucho más limpio, pero por alguna extraña razón hacerlo de forma manual utilizando los índices 1-D es en realidad un poco más rápido:

# Huge sample data 
mat <- matrix(sin(1:1e7), ncol=1000) 
dat <- data.frame(I=sample.int(nrow(mat), 1e7, rep=T), 
        J=sample.int(ncol(mat), 1e7, rep=T)) 

system.time(x <- mat[cbind(dat$I, dat$J)])  # 0.51 seconds 
system.time(mat[dat$I + (dat$J-1L)*nrow(mat)]) # 0.44 seconds 

La parte dat$I + (dat$J-1L)*nrow(m) convierte el 2- D índices en uno-D. El 1L es la forma de especificar un número entero en lugar de un valor doble. Esto evita algunas coerciones.

... También probé la solución basada en la aplicación de gsk3. Es casi 500 veces más lento sin embargo:

system.time(apply(dat, 1, function(x,mat) mat[ x[1], x[2] ], mat=mat)) # 212 
1

Aquí hay una sola línea usando apply 's operaciones basados ​​en filas

> dat <- as.data.frame(matrix(rep(seq(4),4),ncol=2)) 
> colnames(dat) <- c('I','J') 
> dat 
    I J 
1 1 1 
2 2 2 
3 3 3 
4 4 4 
5 1 1 
6 2 2 
7 3 3 
8 4 4 
> mat <- matrix(seq(16),ncol=4) 
> mat 
    [,1] [,2] [,3] [,4] 
[1,] 1 5 9 13 
[2,] 2 6 10 14 
[3,] 3 7 11 15 
[4,] 4 8 12 16 

> dat$K <- apply(dat, 1, function(x,mat) mat[ x[1], x[2] ], mat=mat) 
> dat 
    I J K 
1 1 1 1 
2 2 2 6 
3 3 3 11 
4 4 4 16 
5 1 1 1 
6 2 2 6 
7 3 3 11 
8 4 4 16 
0
n <- 10 
mat <- cor(matrix(rnorm(n*n),n,n)) 
ix <- matrix(NA,n*(n-1)/2,2) 
k<-0 
for (i in 1:(n-1)){ 
    for (j in (i+1):n){ 
    k <- k+1 
    ix[k,1]<-i 
    ix[k,2]<-j 
    } 
} 
o <- rep(NA,nrow(ix)) 
o <- mat[ix] 
out <- cbind(ix,o) 
+2

Algunos comentarios serían agradable y hacer que la respuesta sea más "atractiva". –

+1

No puede responder solo a un montón de código ... Vamos ... explique un poco su respuesta :) –

Cuestiones relacionadas