web-dev-qa-db-fra.com

Indexer les valeurs d'une matrice en utilisant des index de lignes, de colonnes

C'est probablement simple à résoudre. J'ai une matrice 2D mat avec 500 lignes × 335 colonnes et une data.frame dat avec 120425 lignes. Le data.frame dat a deux colonnes I et J, qui sont des entiers pour indexer la ligne, colonne de mat. J'aimerais ajouter les valeurs de mat aux lignes de dat.

Voici mon échec conceptuel:

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

(J'utilise R 2.13.1 sur Win32). En creusant un peu plus loin, je vois que je me mets à mal à l'indexation matricielle, car il apparaît que je ne reçois qu'une sous-matrice de mat, et non un tableau de valeurs à une seule dimension comme prévu, c'est-à-dire:

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

Je m'attendais à quelque chose comme int [1:100] 20 1 1 1 20 1 1 1 1 1 .... Quelle est la bonne façon d'indexer une matrice 2D en utilisant des index de ligne, colonne pour obtenir les valeurs?

23
Mike T

Presque. Doit être proposé à "[" sous forme de matrice à deux colonnes:

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

Il y a une mise en garde: bien que cela fonctionne aussi pour les cadres de données, ils sont d'abord forcés dans une classe de matrice et s'ils sont non numériques, toute la matrice devient la classe du "plus petit dénominateur".

39
42-

Utiliser une matrice pour indexer, comme le suggère DWin, est bien sûr beaucoup plus propre, mais pour une raison étrange, le faire manuellement à l'aide d'indices 1-D est en réalité légèrement plus rapide:

# 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 partie dat$I + (dat$J-1L)*nrow(m) transforme les indices 2-D en indicateurs 1-D. Le 1L permet de spécifier un entier au lieu d'une valeur double. Cela évite certaines contraintes. 

... J'ai aussi essayé la solution basée sur les applications de gsk3. C'est presque 500 fois plus lent cependant:

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

Voici un one-liner utilisant les opérations basées sur les lignes de apply

> 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
1
Ari B. Friedman
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)
0
user4614201