Extraire données d'une matrice en fonction d'un vecteur de positions

Postez ici vos questions, réponses, commentaires ou suggestions - Les sujets seront ultérieurement répartis dans les archives par les modérateurs

Modérateur : Groupe des modérateurs

Boury frederic
Messages : 12
Enregistré le : 15 Jan 2018, 08:09

Extraire données d'une matrice en fonction d'un vecteur de positions

Messagepar Boury frederic » 15 Jan 2018, 19:21

Bonjour :

Ma question porte sur la manière d'extraire des données d'une matrice de nombres A(n,p) à partir d'un vecteur y(n) qui donne pour chaque ligne de la matrice la colonne où aller chercher l'élément.

Le traitement est facile en notation indicielle : for (k in 1:n) {z[k] = A[k, y[k]]}

Comme j'ai des matrices avec beaucoup de lignes et que le traitement doit être répété dans un Monte-Carlo, je me suis dit qu'avec des instructions vectorielles je devrais obtenir de meilleures performances qu'en mode indiciel mais ça n'est pas le cas (cf ci dessous).

Cela dit, je ne maitrise pas bien le langage R et je pense que mes expressions en notation vectorielle sont peu performantes...

Solution 1 : z <- rep(0,n); for (i in 1:p) { z= ifelse(y==i, A[, i],z) }
Solution 2 : z <- diag(A[,y])

Pourtant, je pense qu'il doit bien y avoir des instructions vectorielles simples dans R qui permettent d'obtenir directement z avec de meilleures performances que le 'traitement indiciel' ?

Peut être avec la fonction subset ? Mais je ne maitrise pas assez bien le langage pour écrire la bonne instruction.


Merci d'avance pour vos propositions à ce sujet...


EXEMPLE POUR ILLUSTRER LE SUJET (sur la base de 100.000 itérations)

> n = 500; p =10
> A <- matrix(data=runif(n*p),nrow=n, ncol=p, byrow=TRUE) # génération de la matrice A(500,10) avec des valeurs aléatoires
> y <- round((p-1)*runif(n)+1) # génération du vecteur y(500) avec la position de la valeur de A à prendre sur les colonnes
> z <- rep(0,n)
>
> tt <- Sys.time()
> for (iter in 1:1e5) {for (k in 1:n) {z[k] = A[k, y[k]]}}
> elapse <- difftime(Sys.time(),tt, units = "secs")
> print (elapse)
Time difference of 87.90625 secs
>
> tt <- Sys.time()
> for (iter in 1:1e5) {for (i in 1:p) {z= ifelse(y==i, A[, i],z) } }
> elapse <- difftime(Sys.time(),tt, units = "secs")
> print(elapse)
Time difference of 105.1406 secs[/b]
>
> tt <- Sys.time()
> for (iter in 1:1e5) {z <- diag(A[,y])}
> elapse <- difftime(Sys.time(),tt, units = "secs")
> print (elapse)
Time difference of 755.6719 secs

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Extraire données d'une matrice en fonction d'un vecteur de positions

Messagepar Logez Maxime » 15 Jan 2018, 20:01

Bonjour,

une possibilité :

Code : Tout sélectionner

auxi <- seq_along(y)
A[cbind(auxi, y)]
Cordialement,
Maxime

Boury frederic
Messages : 12
Enregistré le : 15 Jan 2018, 08:09

Re: Extraire données d'une matrice en fonction d'un vecteur de positions

Messagepar Boury frederic » 16 Jan 2018, 11:02

Bonjour Monsieur Logez :

J'ai testé votre solution : les performances sont améliorées d'un facteur de 1 à 40 par rapport à la notation indicielle !
(cf ci dessous)
Un grand merci


Sinon, j'ai compris la logique : on crée d'abord la matrice c(n,2) par l'instruction : cbind(seq(n), y), puis on l'envoie directement dans A pour obtenir z <- A[c]

Je savais sélectionner les lignes et les colonnes séparément dans A, mais j'ignorais qu'on pouvait adresser directement les valeurs de A par une matrice où étaient juxtaposées les valeurs i,j des A[i,j] attendus...
;-))


> tt <- Sys.time()
> for (iter in 1:1e4) {for (k in 1:n) {z[k] = A[k, y[k]]}}
> elapse <- difftime(Sys.time(),tt, units = "secs")
> print (elapse)
Time difference of 9.117304 secs

> auxi <- cbind(seq_along(y), y)
>
> tt <- Sys.time()
> for (iter in 1:1e4) {A[auxi]}
> elapse <- difftime(Sys.time(),tt, units = "secs")
> print(elapse)
Time difference of 0.2343779 secs

9.11/0.23 = 39.6

Boury frederic
Messages : 12
Enregistré le : 15 Jan 2018, 08:09

Re: Extraire données d'une matrice en fonction d'un vecteur de positions

Messagepar Boury frederic » 03 Fév 2018, 11:08

Bonjour :

J'ai une question complémentaire sur le même type de problème.

Dans le cas précédent, il fallait extraire des données depuis une matrice A[n,p]. A cet effet, on a passé comme argument de A une matrice pos(n,2) qui donnait pour chaque ligne de A la valeur de la colonne à sélectionner... pos <- cbind(seq(n),y)

Maintenant, j'ai un problème de même nature mais sur une Array de 3 dimensions soit A[q,n,p]...
En fait, on a désormais q matrices A distinctes (en fonction d'un critère)

La question est d'extraire du Array A une sous matrice B[n,p] en fonction d'une matrice de position P(x, y) soit pour chaque ligne(i in 1:n), le vecteur qui correspond à A[x[i],y[i],] soit A[P,], x prenant valeurs dans (1:q) et y dans (1:n)

j'ai essayé de faire pos <- cbind(x,y) puis A[pos,] mais ça ne marche pas... ce qui marche c'est de ne pas faire un Array[q,n,p] mais de placer dans une matrice A[q*n, p] les q matrices successives, puis de calculer la valeur de y' = (x-1)*q+y et de faire A[y',]

Le problème avec cette approche, outre le fait qu'elle n'est guère élégante, c'est qu'il faut gérer la variable auxiliaire y' et monter en une seule matrice empilée le tableau à 3 dimensions ... les deux contraintes (gestion de y' et empilement des q matrices) étant source d'erreurs ...

Merci d'avance pour un conseil

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Extraire données d'une matrice en fonction d'un vecteur de positions

Messagepar Logez Maxime » 05 Fév 2018, 08:37

Bonjour,

la logique est la même que précédemment sauf qu'il faut rajouter une colonne pour la "3ème dimension".
Dans l'exemple suivant on cherche à extraire les données des lignes 2 et 3 pour les colonnes 4 et 5 de la 1ere matrice soit la première dimension de l'array :

Code : Tout sélectionner

arr <- array(1:60, c(4,5,3))
# on devrait obtenir les valeurs 14 15 18 19
# on crée la matrice des indexations :
index <- as.matrix(expand.grid(2:3, 4:5, 1))
index
     Var1 Var2 Var3
[1,]    2    4    1
[2,]    3    4    1
[3,]    2    5    1
[4,]    3    5    1
arr[index]
[1] 14 15 18 19
Cordialement,
Maxime


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 1 invité