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?
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".
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
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
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)