Condition sur ligne de data frame avec utilisation de boucle

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

Marion Gaget
Messages : 10
Enregistré le : 22 Juin 2015, 09:47

Condition sur ligne de data frame avec utilisation de boucle

Messagepar Marion Gaget » 24 Juin 2015, 15:09

Bonjour,

Je dispose d'une data frame avec 90 colonnes et 1800 lignes. Je souhaite garder uniquement les lignes supérieures à une certaine valeur et donc j'ai pensé à utiliser un flag pour cela. Avec un exemple ça sera plus parlant (j'ai pris des nombres aléatoires) :
Ma data frame a cette forme :

Code : Tout sélectionner

 df1<-data.frame(masse,a,b,c,d)
    masse        a          b       c            d
1     1 -1.2557855  0.5874978 -1.02459899  0.9664616
2     2  1.3886050 -0.8129653 -0.68698018  0.5599505
3     3 -0.6273703 -0.7318345  0.58267617 -0.8576847
4     4 -0.1463911 -1.8717743  1.14981463 -0.3000195
5     5 -0.2899622 -0.4282006 -0.03105713 -1.2476672
6     6  0.4003835 -0.0788654 -0.93095767  1.0691213

Je veux insérer entre chaque colonne de ma df un flag qui me donnerait 1 si la condition est respectée (par exemple df[,i]>=0.5) sinon 0. Mon objectif final est de tracer chaque colonne "réduite" en fonction de la masse (1 ère colonne). Ma méthode pour arriver à cela n'est peut être pas la plus simple... ça fait seulement 1 mois que j'utilise R et que je fais ce genre de programmation.

J'ai créé une df vide

Code : Tout sélectionner

df2<-data.frame(matrix(NA,ncol=5,nrow=20))
   X1 X2 X3 X4 X5
1 NA NA NA NA NA
2 NA NA NA NA NA
...

Et je veux la remplir de ma df1 et des flags (les flags doivent être de préférence à coté de la colonne étudiée). J'ai fait une boucle qui ne fonctionne pas...

Code : Tout sélectionner

 j=1
for ( i in 2 :5){
df2[,j]=df1[,i]
df2[,j+1]=0
df2$df2[,j+1][df2[,j]>=0.5]<-1
j<-j+2}

Je vois certains trucs qui clochent dans ma boucle mais je ne sais pas les traduire en langage R... Et j'ai également du mal à bien comprendre la nuance entre le signe = et <- : avec le signe <- on donne une valeur à un objet mais avec le égal on fixe une valeur ??

J'ai essayé de formuler mon problème le plus synthétiquement et clairement possible mais n’hésitez pas pour les questions. Merci par avance.
Amicalement,

Marion

Gabriel Terraz
Messages : 591
Enregistré le : 26 Sep 2011, 15:11

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Gabriel Terraz » 24 Juin 2015, 15:48

Salut,
Ton problème n'est pas très clair, tu dis vouloir garder seulement les lignes supérieures à 1, tu veux dire lorsque la somme des valeurs vaut 1 ? Ou autre chose ?

Pour info :

Le signe "=" et "<-" servent à la même chose : assigner à un objet des résultats de commande. Je te conseille d'utiliser la deuxième solution pour éviter les confusions.
Si tu veux tester l'égalité il faut utiliser "==".

Marion Gaget
Messages : 10
Enregistré le : 22 Juin 2015, 09:47

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Marion Gaget » 24 Juin 2015, 16:07

Merci Gabriel pour l'explication des signes.

Je veux mettre une condition pour garder toutes les lignes supérieures à 0.5. Et j'avais pensé créé une nouvelle colonne accolée à la colonne étudiée qui me donnerait 1 si la condition est vrai sinon 0. Je travaille en anglais et ils appellent cela un "flag".
Dans ce style et pour toutes mes colonnes

Code : Tout sélectionner

   masse      a    flag1        b    flag2    c      d
1     1 -1.2557855    0   0.5874978   1  -1.02459899  0.9664616
2     2  1.3886050    1   -0.8129653  0  -0.68698018  0.5599505
3     3 -0.6273703    0  -0.7318345   0   0.58267617 -0.8576847
4     4 -0.1463911    0  -1.8717743   0   1.14981463 -0.3000195
5     5 -0.2899622    0 -0.4282006    0   -0.03105713 -1.2476672
6     6  0.4003835    0 -0.0788654    0   -0.93095767  1.0691213

Gabriel Terraz
Messages : 591
Enregistré le : 26 Sep 2011, 15:11

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Gabriel Terraz » 24 Juin 2015, 16:32

Tu parles donc de cellule et non de ligne si tu mets une colonne pour chaque colonne, non ?
Dans ce cas là, je ne vois pas trop comment tu vas "garder" que certaines cases. Un truc m'échappe.

Si tu veux tester l'égalité, tu peux directement faire :

Code : Tout sélectionner

df1[,-1] > 0.5


Ce qui va te renvoyer un logique pour chaque cellule.

Tout dépend de ce que tu veux faire en aval

Navarre Julien
Messages : 367
Enregistré le : 20 Avr 2012, 08:27

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Navarre Julien » 24 Juin 2015, 16:42

Bonjour,

si tu veux avoir une matrice de flags, avec 1 si la valeur correspondante dans d1 valide la condition 0 sinon.
La boucle d'école est la boucle suivante dans le langage R.
On boucle sur les lignes puis sur les colonnes (je pars à 2 pour ne pas vérifier la colonne masse), on regarde si la valeur [i,j] valide la condition on assigne 1 sinon 0.

Code : Tout sélectionner

datas <- read.table(text = "masse        a          b       c            d
1     1 -1.2557855  0.5874978 -1.02459899  0.9664616
2     2  1.3886050 -0.8129653 -0.68698018  0.5599505
3     3 -0.6273703 -0.7318345  0.58267617 -0.8576847
4     4 -0.1463911 -1.8717743  1.14981463 -0.3000195
5     5 -0.2899622 -0.4282006 -0.03105713 -1.2476672
6     6  0.4003835 -0.0788654 -0.93095767  1.0691213", header = TRUE)

df2 <- matrix(NA, ncol = 4, nrow = 6)

for (i in 1L:nrow(datas)) {
  for (j in 2L:length(datas)) {
    i (datas[i, j] > 0.5) { #c'est un if, mais impossible de poster sinon pour les SQL injections
      df2[i, j - 1] <- 1
    } else {
      df2[i, j - 1] <- 0
    }
  }
}

> df2
[,1] [,2] [,3] [,4]
[1,]    0    1    0    1
[2,]    1    0    0    1
[3,]    0    0    1    0
[4,]    0    0    1    0
[5,]    0    0    0    0
[6,]    0    0    0    1


Mais n'est pas tirer avantage de R. Dans R, il y a quelque chose qui peut être déroutant si tu viens d'un autre langage de programmation mais qui est très puissant, c'est la vectorisation :

Pour faire simple et très grossier, une fonction vectorisée va appliquer une fonction à chaque élément d'un vecteur et retourner un vecteur de la même longueur que le vecteur passé en paramètre. (C'est un peu grossier, car ça peut être de même longueur QU'UN de ses arguments, selon la fonction et ce que tu lui passes).

Par exemple l'addition est vectorisée :

Code : Tout sélectionner

1:10 + 1
[1]  2  3  4  5  6  7  8  9 10 11

# De même que la comparaison

datas$a > 0.5
[1] FALSE  TRUE FALSE FALSE FALSE FALSE

# La fonction ifelse permet de vectoriser les if else

ifelse(datas$a > 0.5, 1, 0)
[1] 0 1 0 0 0 0


Pour avoir quelque chose plus proche de l'esprit de R tu peux utiliser ces fonctions vectorisées dans ton cas par exemple, on n'a pas besoin d'imbriquer des boucles for :

Code : Tout sélectionner

df3 <- matrix(NA, ncol = 4, nrow = 6)

for (i in 1L:nrow(datas)) {
  df3[i, ] <- ifelse(datas[i, ][-1] > 0.5, 1, 0)
}


Bref, ça c'était pour ton histoire de flag, mais pourquoi créer des flags pour vérifier une condition si ensuite tu va vérifier si les flags remplissent eux aussi une condition (TRUE or FALSE) ..?!

Tu peux directement filtrer tes lignes sans passer par un flag, puisque les conditions t'en renvoient déjà.

Comme Gabriel, j'ai pas bien comprit la condition que tu cherches à remplir pour filtrer tes lignes. Tu veux filtrer les observations, les lignes pour les quelles la variables sont supérieures à une valeur ?

Ce code par exemple permet de filtrer les lignes qui ont au moins (any) une colonne (hormis masse) supérieure à 0.5.

Code : Tout sélectionner

datas[apply(subset(datas, select = -masse), 1, function(x) any(x > 0.5)), ]


Pas de panique pour apply, c'est de la programmation fonctionnelle et ça permet de "cacher" les boucles for, ce sont des wrappers. Dans apply le 1 (arg MARGIN) permet de signaler qu'on boucle sur les lignes.

Marion Gaget
Messages : 10
Enregistré le : 22 Juin 2015, 09:47

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Marion Gaget » 25 Juin 2015, 09:02

Bonjour,
Je vais reprendre vu que c'était pas clair (même moi en me relisant j'avais du mal !)et en plus je me suis mal exprimée comme Gabriel l'a souligné. C'est de cellule que je parlais. Désolée pour le quiproquo. Merci Julien pour ton aide et tes informations sur la vectorisation.

Bref, ça c'était pour ton histoire de flag, mais pourquoi créer des flags pour vérifier une condition si ensuite tu va vérifier si les flags remplissent eux aussi une condition (TRUE or FALSE) ..?!
Tu peux directement filtrer tes lignes sans passer par un flag, puisque les conditions t'en renvoient déjà.

Vous avez raison ma méthode avec les flags n'est pas bonne pour filtrer ! Je peux filtrer mes cellules directement.

Je reprends : j'ai 2 data frame (90 colonnes et 1800 lignes respectivement); la 1ere avec une colonne masse puis 89 colonnes intensités selon le jour de la mesure et l'autre data frame que j'ai créé comprend la même masse et les 89 colonnes "écart relatif" de l’intensité.
Mon but est de tracer l’intensité selon la masse pour chaque jour mais avec une condition sur l’intensité : l'intensité dont la variable des cellules de l'écart relatif est supérieure à 0.5 . C'est une façon de restreindre mon graphe : pour le jour 1 j'aurais seulement les intensités dont l’écart relatif est supérieur à 0.5 de même pour le jour 2,3...

Grace à vos remarques je me rends compte que ma méthode n'est pas du tout bonne mais du coup je suis un peu perdue pour arriver à mon résultat...
Si vous avez des idées je suis preneuse. Encore désolée pour les problèmes de clarté.
Amicalement,
Marion

Gabriel Terraz
Messages : 591
Enregistré le : 26 Sep 2011, 15:11

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Gabriel Terraz » 25 Juin 2015, 09:19

Salut,
Une proposition avec ton dataframe df et df_ecart celui comprenant les écarts :

Code : Tout sélectionner

df[df_ecart < 0.5] <- NA

library(lattice)
 F <- paste(paste(names(df)[-1] , collapse = "+") , "~masse")
xyplot(as.formula(F) , data = df , type = "b" , auto.key = T)

Marion Gaget
Messages : 10
Enregistré le : 22 Juin 2015, 09:47

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Marion Gaget » 25 Juin 2015, 09:50

Wahou merci Gabriel ! Ta première ligne de code est exactement ce que j'essayais de faire avec mes flags et ma boucle mais en beaucoup plus simple et plus joli.
Pour la suite je n'ai pas tout compris dans le code même après avoir regarde l'aide sur paste. Peux tu me donner quelques explications ?

Gabriel Terraz
Messages : 591
Enregistré le : 26 Sep 2011, 15:11

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Gabriel Terraz » 25 Juin 2015, 10:30

paste() sert à concaténer des chaînes de caractères

En effet la fonction xyplot permet de dessiner sur le même graphes beaucoup de variables en fonction d'une autre assez facilement.

Une possibilité est de lui donner un formule du style

Code : Tout sélectionner

a + b + c +d ~ masse

ce qui est facile à écrire, mais un peu long si tu as 89 variables, d’où l'utilisation de paste pour concaténer toutes les variables de ton tableau avec pour séparateur le signe "+".
Le deuxième paste est pour rajouter "~ masse"
Tu obtiens donc :

Code : Tout sélectionner

> F
[1] "a+b+c+d ~masse"

Cette chaîne de caractère est convertie en formule avec as.formula()

C'est mieux ?

Marion Gaget
Messages : 10
Enregistré le : 22 Juin 2015, 09:47

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Marion Gaget » 25 Juin 2015, 11:03

Merci beaucoup pour les explications. Elles sont très claires.
J'obtiens ce que je voulais et même en mieux.

Gabriel et Julien vous m'avez vraiment aidé et fait découvrir de nouveaux outils qui me seront vraiment utiles pour la suite de mon apprentissage de R; un grand merci pour votre aide, votre disponibilité et votre rapidité !!

Amicalement,
Marion

Samuel Pereira Dias
Messages : 62
Enregistré le : 07 Mar 2014, 11:09

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Samuel Pereira Dias » 11 Aoû 2022, 15:08

Bonjour,

Avant de décrire mes besoins, j'aimerais savoir s'il était possible de m'expliquer pourquoi lorsque je souhaite transmettre un extrait de mes données via dput() je me retrouve systématiquement avec le df initial comptabilisant plus de 1 000 000 de lignes? Dès que je solutionne cela, je copierai ici une partie de mes données.

J'effectue une analyse spatiale d'intensité de feu, où je souhaite compter l'intervalle moyen de temps entre chaque feu depuis les années 1975. Chaque cellule se voit renseignée par année si 1 = feu ; ou NA= pas de passage; et comme je le suggérais auparavant j'ai plus d'un million de lignes/cellules, et 46 colonnes (années).

Pour cela, je souhaitais remplacer les "1" par l'année présente dans le nom de la colonne. Ainsi, j'aurai pu, je pense plus facilement faire mes calculs ligne par ligne, avec un apply par exemple. Le code ci-dessous parait trés proche de mes besoins :

En réadaptant quelque peu le code de Julien Navarre à mes besoins, je pensais répondre à cette première étape de mon traitement.

Code : Tout sélectionner

dfm<-as.matrix(resh)# resh est mon dataframe de données que je transforme en matrix.

df2 <- matrix(NA, ncol = ncol(resh[2:ncol(resh)]), nrow = nrow(resh))
for (i in 1L:nrow(dfm)) {
  for (j in 2L:length(dfm)) {
    if (dfm[i, j] > 1) { #
      df2[i, j-1] <- 1
    } else {
      df2[i, j-1] <- 0
    }
  }
}



# J'obtiens l'erreur suivante :
Error in if (dfm[i, j] > 1) { : missing value where TRUE/FALSE needed

Le code ci-dessous ne fonctionne pas non plus :/

Code : Tout sélectionner

Ce code par exemple permet de filtrer les lignes qui ont au moins (any) une colonne (hormis masse) supérieure à 0.5.
datas[apply(subset(datas, select = -masse), 1, function(x) any(x > 0.5)), ]


D'avance, je vous remercie pour vos éclairages et votre aide, notamment en ce qui concerne le fonctionnement de dput() afin de partager un extrait de mes données.
Samuel
Samuel Pereira Dias

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

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Logez Maxime » 12 Aoû 2022, 07:54

Bonjour,

Quand tu poses une nouvelle question, il vaut mieux ouvrir un nouveau topic que d'en continuer un.

Normalement le dput ne renvoie que ce qu'il y a spécifier à l'intérieur. Donc si tu ne demandes a copier qu'une partie de ton tableau alors il n'y a pas de raison que tout te soit rendu.

Pour des questions de gestion de la mémoire, il est bien plus intéressant notamment sur un gros jeu de données comme le tien de faire des opérations sur les colonnes plutôt que sur les lignes d'une matrice.

Pour avoir une matrice avec les années à la place des 1 :

Code : Tout sélectionner

# pour 100 cellules et 10 années
mat <- matrix(rbinom(1000, 1, 0.1), 100)
mat[mat < 0.5] <- NA
colnames(mat) <- 2006:2015

# le plus simple : travailler en colonne et non en ligne
mat2 <- t(mat)
mat3 <- as.numeric(rownames(mat2)) * (!is.na(mat2)*1)


Ensuite tu peux calculer les écarts entre les observations de feu :

Code : Tout sélectionner

f <- function(x) {
 d1 <- diff(x[x>0.5])
 if (length(d1) <0.5)
   d1 <- NA
  d1
  }
res <- vector("list", ncol(mat3))
for (i in 1:ncol(mat3))
  res[[i]] <- f(mat3[,i])

Si tu as plusieurs coeurs dans ton pc tu peux paralléliser la question pour que ça te prenne moins de temps, mais peut-être que sur ce calcul ça ne sert à rien :

Code : Tout sélectionner

library(doSNOW)
library(itertools)

cl <- makeCluster(4)
registerDoSNOW(cl)

res2 <- foreach(mat = isplitCols(mat3, chunks = 4), .export = "f") %dopar% {
  res <- vector("list", ncol(mat))
  for (i in 1:ncol(mat))
    res[[i]] <- f(mat[,i])
  res
  }
stopCluster(cl)
res2 <- do.call(c, res2)
Arès tout dépend de ce que tu veux faire. Chaque élément de la liste correspond à une cellule. Tu peux faire une moyenne globale en extrayant les valeurs (mean(do.call(c, res2))) ou faire des moyennes par cellule. Si c'est la dernière solution que tu veux alors tu peux intégrer le calcul de la moyenne directement dans la fonction f :

Code : Tout sélectionner

f <- function (x)
{
    d1 <- mean(diff(x[x > 0.5]))
    if (is.nan(d1))
        d1 <- NA
    d1
}
# a ce moment la ça se simplifie un peu
cl <- makeCluster(4)
registerDoSNOW(cl)

res2 <- foreach(mat = isplitCols(mat3, chunks = 4), .export = "f") %dopar% {
  res <- numeric(ncol(mat))
  for (i in 1:ncol(mat))
    res[i] <- f(mat[,i])
  res
  }
stopCluster(cl)
res2 <- do.call(c, res2)
Les valeurs obtenues correspondent au final aux moyennes par ligne de ton tableau initial.
Sans parallélisation ça donnerait :

Code : Tout sélectionner

res2 <- numeric(ncol(mat3))
for (i in 1:ncol(mat3))
  res2[i] <- f(mat3[,i])


Cordialement,
Maxime

Samuel Pereira Dias
Messages : 62
Enregistré le : 07 Mar 2014, 11:09

Re: Condition sur ligne de data frame avec utilisation de boucle

Messagepar Samuel Pereira Dias » 12 Aoû 2022, 14:55

Bonjour Maxime,
Cela fonctionne parfaitement bien !
Un grand merci à toi,
Samuel
Samuel Pereira Dias


Retourner vers « Questions en cours »

Qui est en ligne

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