Problème de boucle for + if

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

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Problème de boucle for + if

Messagepar Diane MANZON » 22 Oct 2018, 17:47

Bonsoir à tous,

Je ne comprends pas le problème dans mon code...

J'ai une matrice (Mat_Y) qui contient 1245 lignes et 7 colonnes. Je m'intéresse aux valeurs de la 3ème colonne (Mat_Y[,3]).
Je voudrais les valeurs de cette colonne pour chaque ligne: si elle est < à 35, appliquer un calcul ; si elle est > à 35, appliquer un autre calcul.

Code : Tout sélectionner

for (i in 1:nrow(Mat_Y)) {
if (Mat_Y[i,3] < 35) { d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2 ; d1[d1 < 0] <- 0
} else { d1 <- ((Mat_Y[,3] - 45)/(35 - 45))^0.5 ; d1[d1 < 0] <- 0
   }
}


Je n'ai aucune erreur quand j'applique ce code mais quand je vérifie mon d1 pour une valeur > à 35, c'est la première condition qui a été appliquée....
Est-ce quelqu'un saurait où est le problème ?
Je m'excuse d'avance si la question vous paraît basique, je suis débutante ^^

Stéphane Adamowicz
Messages : 206
Enregistré le : 07 Mar 2012, 10:13
Contact :

Re: Problème de boucle for + if

Messagepar Stéphane Adamowicz » 23 Oct 2018, 07:04

Bonjour,

votre code semble bizarre. puisque vous bouclez sur l'indice i, mais vous oubliez par la suite d'utiliser cet indice. Par exemple, vous écrivez au lieu de

Code : Tout sélectionner

d1[i]
et

Code : Tout sélectionner

Mat_Y[,3]
au lieu de

Code : Tout sélectionner

Mat_Y[i,3]
...
Stéphane Adamowicz
INRA, UR 1115 Plantes et Systèmes de Culture Horticoles (PSH)
domaine St Paul, site agroparc
84914 Avignon, cedex 9

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Re: Problème de boucle for + if

Messagepar Diane MANZON » 23 Oct 2018, 07:23

Stéphane Adamowicz a écrit :Bonjour,

votre code semble bizarre. puisque vous bouclez sur l'indice i, mais vous oubliez par la suite d'utiliser cet indice. Par exemple, vous écrivez au lieu de

Code : Tout sélectionner

d1[i]
et

Code : Tout sélectionner

Mat_Y[,3]
au lieu de

Code : Tout sélectionner

Mat_Y[i,3]
...


Bonjour Stéphane !

En effet, erreur de débutante ! Ca fonctionne merci beaucoup !!

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

Re: Problème de boucle for + if

Messagepar Logez Maxime » 23 Oct 2018, 08:04

Bonjour,

faire des comparaison une à une avec des if et des else n'est pas très efficace sous R.
Quand tu as deux possibilités il est plus rapide de faire directement le calcul pour toutes les valeurs avec la formule pour une condition, et modifier les valeurs qui vérifient la deuxième condition :

Code : Tout sélectionner

d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2
auxi <- which(Mat_Y[,3]>=35)
d1[auxi] <- ((Mat_Y[auxi,3] - 45)/(35 - 45))^0.5
d1[d1 < 0] <- 0
Il faut privilégier le plus possible la vectorisation des calculs. De simples opérations mathématiques peuvent s'effectuer sur toutes ou parties des valeurs d'un vecteur, il n'y a pas besoin de faire ces opérations une à une via une boucle qui va considérablement ralentir le calcul.
[EDIT] J'ai rajouté le ifelse pour l'exemple :

Code : Tout sélectionner

test <- rnorm(1245, 35, 10)
microbenchmark(Vectorise = {
    d1 <- ((test - 20)/(35 - 20))^2
    auxi <- which(test>=35)
    d1[auxi] <- ((test[auxi] - 45)/(35 - 45))^0.5
    d1[d1 < 0] <- 0
  },
  Boucle = {
    d1 <- numeric(length(test))
    for (i in 1:length(test)) {
      if(test[i] < 35){d1[i] <- ((test[i] - 20)/(35 - 20))^2}else{d1 <- ((test[i] - 45)/(35 - 45))^0.5}
    }
    d1[d1<0] <- 0
    },
  IfElse = {
    d1 <- ifelse(test < 35, ((test - 20)/(35 - 20))^2, ((test - 45)/(35 - 45))^0.5)
    d1[d1<0] <- 0
  }
)
Unit: microseconds
      expr       min         lq       mean    median        uq       max neval cld
 Vectorise   116.564   122.2225   130.4122   129.012   135.803   181.824   100  a
    Boucle 15478.093 15700.6585 16373.6691 15955.854 16343.834 25166.095   100   b
    IfElse   282.923   293.4845   320.9092   310.271   322.909   616.770   100  a
Cordialement,
Maxime

Serge Rapenne
Messages : 1426
Enregistré le : 20 Aoû 2007, 15:17
Contact :

Re: Problème de boucle for + if

Messagepar Serge Rapenne » 23 Oct 2018, 08:10

Bonjour,

Dans ce contexte la boucle est inutile, Tu peux faire des choses comme ça :

Code : Tout sélectionner

set.seed(1)
Mat_Y<-data.frame(A=1:10,B=11:20,D=runif(10,0,50)) #création d'un jeu de données pour l'exemple
Mat_Y
    A  B         D
1   1 11 10.298729
2   2 12  8.827838
3   3 13 34.351142
4   4 14 19.205186
5   5 15 38.492071
6   6 16 24.884962
7   7 17 35.880925
8   8 18 49.595305
9   9 19 19.001759
10 10 20 38.872261

d1<-ifelse(Mat_Y$D>35,Mat_Y$D-40,Mat_Y$D-20) #j'ai simplifié les calculs mais l'idée est là
d1
 [1]  -9.7012713 -11.1721624  14.3511423  -0.7948141  -1.5079290   4.8849621
 [7]  -4.1190746   9.5953047  -0.9982410  -1.1277389
d1[d1<0]<-0
 d1
 [1]  0.000000  0.000000 14.351142  0.000000  0.000000  4.884962  0.000000
 [8]  9.595305  0.000000  0.000000


Remarque
Si tu tiens à garder la boucle (personnellement je trouve que c'est plus facile à lire sans), tu peux mettre d1[d1<0]<-0 en dehors de celle ci.
Serge

EDIT : En partie grillé par Maxime,

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Re: Problème de boucle for + if

Messagepar Diane MANZON » 23 Oct 2018, 08:18

Logez Maxime a écrit :Bonjour,

faire des comparaison une à une avec des if et des else n'est pas très efficace sous R.
Quand tu as deux possibilités il est plus rapide de faire directement le calcul pour toutes les valeurs avec la formule pour une condition, et modifier les valeurs qui vérifient la deuxième condition :

Code : Tout sélectionner

d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2
auxi <- which(Mat_Y[,3]>=35)
d1[auxi] <- ((Mat_Y[auxi,3] - 45)/(35 - 45))^0.5
d1[d1 < 0] <- 0
Il faut privilégier le plus possible la vectorisation des calculs. De simples opérations mathématiques peuvent s'effectuer sur toutes ou parties des valeurs d'un vecteur, il n'y a pas besoin de faire ces opérations une à une via une boucle qui va considérablement ralentir le calcul.
Un exemple :

Code : Tout sélectionner

test <- rnorm(1245, 35, 10)
microbenchmark(Vectorise = {
  d1 <- ((test - 20)/(35 - 20))^2
  auxi <- which(test>=35)
  d1[auxi] <- ((test[auxi] - 45)/(35 - 45))^0.5
  d1[d1 < 0] <- 0
  },
  Boucle = {
  d1 <- numeric(length(test))
  for (i in 1:length(test)) {
    if(test[i] < 35){d1[i] <- ((test[i] - 20)/(35 - 20))^2}else{d1 <- ((test[i] - 45)/(35 - 45))^0.5}
  }
  d1[d1<0] <- 0
  }
)
Unit: microseconds
      expr       min        lq      mean     median        uq       max neval cld
 Vectorise   115.433   121.091   130.167   132.9735   135.048   165.226   100  a
    Boucle 14959.780 15228.933 16047.400 15593.7140 17079.243 19596.305   100   b
Cordialement,
Maxime


Bonjour Maxime,
Merci beaucoup pour ton aide! En effet, j'ai exactement la même chose grâce à ton code sans faire la boucle.
En réalité, c'est plus compliqué que ça puisque j'ai plusieurs cas. Je remets la version boucle juste pour l'explication:

Code : Tout sélectionner

d1 <- NULL
for (i in 1:nrow(Mat_Y)) {
if (Mat_Y[i,3] < 20 | Mat_Y[i,3] >= 45) {
  d1[i] <- 0} else (Mat_Y[i,3] < 35)
  {d1[i] <- ((Mat_Y[i,3] - 20)/(35 - 20))^2 ; d1[d1 < 0] <- 0
} else { d1[i] <- ((Mat_Y[i,3] - 45)/(35 - 45))^0.5 ; d1[d1 < 0] <- 0}
}


Bien sûr, en appliquant ce code, je n'obtiens pas ce que je veux et j'avoue me perdre dans mes boucles --'
Je ne sais pas encore me servir de la vectorisation...

Si tu as une solution plus simple, je suis preneuse :-)

Merci!

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Re: Problème de boucle for + if

Messagepar Diane MANZON » 23 Oct 2018, 08:41

Serge Rapenne a écrit :Bonjour,

Dans ce contexte la boucle est inutile, Tu peux faire des choses comme ça :

Code : Tout sélectionner

set.seed(1)
Mat_Y<-data.frame(A=1:10,B=11:20,D=runif(10,0,50)) #création d'un jeu de données pour l'exemple
Mat_Y
    A  B         D
1   1 11 10.298729
2   2 12  8.827838
3   3 13 34.351142
4   4 14 19.205186
5   5 15 38.492071
6   6 16 24.884962
7   7 17 35.880925
8   8 18 49.595305
9   9 19 19.001759
10 10 20 38.872261

d1<-ifelse(Mat_Y$D>35,Mat_Y$D-40,Mat_Y$D-20) #j'ai simplifié les calculs mais l'idée est là
d1
 [1]  -9.7012713 -11.1721624  14.3511423  -0.7948141  -1.5079290   4.8849621
 [7]  -4.1190746   9.5953047  -0.9982410  -1.1277389
d1[d1<0]<-0
 d1
 [1]  0.000000  0.000000 14.351142  0.000000  0.000000  4.884962  0.000000
 [8]  9.595305  0.000000  0.000000


Remarque
Si tu tiens à garder la boucle (personnellement je trouve que c'est plus facile à lire sans), tu peux mettre d1[d1<0]<-0 en dehors de celle ci.
Serge

EDIT : En partie grillé par Maxime,


Bonjour Serge,

Haha "EDIT" ;-)

tu peux m'expliquer "d1<-ifelse(Mat_Y$D>35,Mat_Y$D-40,Mat_Y$D-20)" ?

Merci beaucoup !

Serge Rapenne
Messages : 1426
Enregistré le : 20 Aoû 2007, 15:17
Contact :

Re: Problème de boucle for + if

Messagepar Serge Rapenne » 23 Oct 2018, 09:01

Une fonction incontournable de R est la fonction "?" qui permet d'obtenir l'aide.

Code : Tout sélectionner

?ifelse
donne l'aide de la fonction ifelse.
La ligne

Code : Tout sélectionner

d1<-ifelse(Mat_Y$D>35,Mat_Y$D-40,Mat_Y$D-20)
signifie pour chaque valeur de la colonne D si D>35 alors d1 vaut D-40 sinon d1 vaut D-20.

Je préfère utiliser la notation avec $D qui désigne la colonne dont le nom est D plutôt que [,3] mais

Code : Tout sélectionner

d1<-ifelse(Mat_Y[,3]>35,Mat_Y[,3]-40,Mat_Y[,3]-20)
ferait la même chose

Serge

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Re: Problème de boucle for + if

Messagepar Diane MANZON » 23 Oct 2018, 09:11

Serge Rapenne a écrit :Une fonction incontournable de R est la fonction "?" qui permet d'obtenir l'aide.

Code : Tout sélectionner

?ifelse
donne l'aide de la fonction ifelse.
La ligne

Code : Tout sélectionner

d1<-ifelse(Mat_Y$D>35,Mat_Y$D-40,Mat_Y$D-20)
signifie pour chaque valeur de la colonne D si D>35 alors d1 vaut D-40 sinon d1 vaut D-20.

Je préfère utiliser la notation avec $D qui désigne la colonne dont le nom est D plutôt que [,3] mais

Code : Tout sélectionner

d1<-ifelse(Mat_Y[,3]>35,Mat_Y[,3]-40,Mat_Y[,3]-20)
ferait la même chose

Serge



Ok super, merci pour tes explications Serge :-)

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Re: Problème de boucle for + if

Messagepar Diane MANZON » 23 Oct 2018, 09:21

Diane MANZON a écrit :
Logez Maxime a écrit :Bonjour,

faire des comparaison une à une avec des if et des else n'est pas très efficace sous R.
Quand tu as deux possibilités il est plus rapide de faire directement le calcul pour toutes les valeurs avec la formule pour une condition, et modifier les valeurs qui vérifient la deuxième condition :

Code : Tout sélectionner

d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2
auxi <- which(Mat_Y[,3]>=35)
d1[auxi] <- ((Mat_Y[auxi,3] - 45)/(35 - 45))^0.5
d1[d1 < 0] <- 0
Il faut privilégier le plus possible la vectorisation des calculs. De simples opérations mathématiques peuvent s'effectuer sur toutes ou parties des valeurs d'un vecteur, il n'y a pas besoin de faire ces opérations une à une via une boucle qui va considérablement ralentir le calcul.
Un exemple :

Code : Tout sélectionner

test <- rnorm(1245, 35, 10)
microbenchmark(Vectorise = {
  d1 <- ((test - 20)/(35 - 20))^2
  auxi <- which(test>=35)
  d1[auxi] <- ((test[auxi] - 45)/(35 - 45))^0.5
  d1[d1 < 0] <- 0
  },
  Boucle = {
  d1 <- numeric(length(test))
  for (i in 1:length(test)) {
    if(test[i] < 35){d1[i] <- ((test[i] - 20)/(35 - 20))^2}else{d1 <- ((test[i] - 45)/(35 - 45))^0.5}
  }
  d1[d1<0] <- 0
  }
)
Unit: microseconds
      expr       min        lq      mean     median        uq       max neval cld
 Vectorise   115.433   121.091   130.167   132.9735   135.048   165.226   100  a
    Boucle 14959.780 15228.933 16047.400 15593.7140 17079.243 19596.305   100   b
Cordialement,
Maxime


Bonjour Maxime,
Merci beaucoup pour ton aide! En effet, j'ai exactement la même chose grâce à ton code sans faire la boucle.
En réalité, c'est plus compliqué que ça puisque j'ai plusieurs cas. Je remets la version boucle juste pour l'explication:

Code : Tout sélectionner

d1 <- NULL
for (i in 1:nrow(Mat_Y)) {
if (Mat_Y[i,3] < 20 | Mat_Y[i,3] >= 45) {
  d1[i] <- 0} else (Mat_Y[i,3] < 35)
  {d1[i] <- ((Mat_Y[i,3] - 20)/(35 - 20))^2 ; d1[d1 < 0] <- 0
} else { d1[i] <- ((Mat_Y[i,3] - 45)/(35 - 45))^0.5 ; d1[d1 < 0] <- 0}
}


Bien sûr, en appliquant ce code, je n'obtiens pas ce que je veux et j'avoue me perdre dans mes boucles --'
Je ne sais pas encore me servir de la vectorisation...

Si tu as une solution plus simple, je suis preneuse :-)

Merci!


Je pense avoir trouvé:

Code : Tout sélectionner

d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2
auxi <- which(Mat_Y[,3]>=35)
ixi <- which(Mat_Y[,3]<20 | Mat_Y[,3]>45)
d1[ixi] <- 0
d1[auxi] <- ((Mat_Y[auxi,3] - 45)/(35 - 45))^0.5
d1[d1 < 0] <- 0


Merci :-D

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

Re: Problème de boucle for + if

Messagepar Logez Maxime » 23 Oct 2018, 09:29

re,

je ne pense pas que la séquence des commandes soient bonnes parce que si tu fais d1[ixi] <- 0 avant d1[auxi] alors toutes les valeurs supérieures à 45 ne vont plus être égales à 0.

Code : Tout sélectionner

d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2
auxi <- which(Mat_Y[,3]>=35)
ixi <- which(Mat_Y[,3]<20 | Mat_Y[,3]>45)
d1[auxi] <- ((Mat_Y[auxi,3] - 45)/(35 - 45))^0.5
d1[ixi | d1 < 0] <- 0
A vérifier.
Maxime

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Re: Problème de boucle for + if

Messagepar Diane MANZON » 23 Oct 2018, 13:56

Logez Maxime a écrit :re,

je ne pense pas que la séquence des commandes soient bonnes parce que si tu fais d1[ixi] <- 0 avant d1[auxi] alors toutes les valeurs supérieures à 45 ne vont plus être égales à 0.

Code : Tout sélectionner

d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2
auxi <- which(Mat_Y[,3]>=35)
ixi <- which(Mat_Y[,3]<20 | Mat_Y[,3]>45)
d1[auxi] <- ((Mat_Y[auxi,3] - 45)/(35 - 45))^0.5
d1[ixi | d1 < 0] <- 0
A vérifier.
Maxime



Je n'ai pas de valeurs supérieures à 45 mais j'en ai des inférieures à 20 et les 2 méthodes ont l'air de marcher! Je vais appliquer la tienne quand même ! Merci !

Diane

Diane MANZON
Messages : 56
Enregistré le : 18 Juin 2018, 08:31

Re: Problème de boucle for + if

Messagepar Diane MANZON » 23 Oct 2018, 14:37

Logez Maxime a écrit :re,

je ne pense pas que la séquence des commandes soient bonnes parce que si tu fais d1[ixi] <- 0 avant d1[auxi] alors toutes les valeurs supérieures à 45 ne vont plus être égales à 0.

Code : Tout sélectionner

d1 <- ((Mat_Y[,3] - 20)/(35 - 20))^2
auxi <- which(Mat_Y[,3]>=35)
ixi <- which(Mat_Y[,3]<20 | Mat_Y[,3]>45)
d1[auxi] <- ((Mat_Y[auxi,3] - 45)/(35 - 45))^0.5
d1[ixi | d1 < 0] <- 0
A vérifier.
Maxime



Erreur: longer object is not a multiple of shorter object length.
J'ai vérifié avec le code précédent que j'avais posté, ça a l'air de marcher...

Merci!

Diane


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Google [Bot] et 1 invité