Problème boucle for

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

Lorenzo Barrail
Messages : 13
Enregistré le : 23 Sep 2020, 07:45

Problème boucle for

Messagepar Lorenzo Barrail » 23 Sep 2020, 11:54

Bonjour à tous,

J'aurais besoin d'aide au sujet d'une boucle for.
J'ai un data frame de 3 colonnes correspondant à des couleurs de pixels, vert ou noir:

pix pixmoins pixmoins4
"vert" "vert" "vert"
"vert" "noir" "vert"
"noir "noir" "vert"
... ... ...

J'aimerais rajouter une colonne "numero" assignant un numéro aux lignes selon plusieurs conditions:

Si pix= vert et pixmoins1=vert alors numéro=n
Si pix=vert et pixmoins1=noir et pixmoins4=vert alors numéro=n[i-4] (le même numéro que 4 lignes avant)
Si pix=vert et pixmoins1=noir et pixmoins4=noir alors numéro=n+1 (nouveau numéro)
Si pix=noir alors numéro=0

Pour remettre la chose dans son contexte, il s'agit de regrouper les pixels en groupes (d'où les numéros),
s'ils sont identiques. Pix est le pixel utilisé dans la boucle, Pix moins 1 est le pixel situé au dessus
de Pix et pixmoins4 est le pixel situé à gauche de pix (dans une matrice de 4 lignes).

Voici la boucle que j'ai faite, sans succès (Error in i - 1 : argument non numérique pour un opérateur binaire):

for(i in data$pix)
{n=1
data$numero=ifelse(data$pix=="vert" & data$pixmoins1=="vert",n[i-1],
ifelse(data$pix=="vert" & data$pixmoins1=="noir" & data$pixmoins4=="vert",n[i-4],
ifelse(data$pix=="vert" & data$pixmoins1=="noir" & data$pixmoins4=="noir",n[i-1]+1,
ifelse(data$pix=="noir",0,NA))))}

J'ai alors testé cette même boucle en remplaçant les couleurs par des chiffres, et j'obtiens bien une nouvelle colonne, mais avec des NA partout.
Je pense que l'erreur peut venir du fait que pour certains i(notamment le premier), i-1 ou i-4 n'existe pas.
Auriez-vous une idée?
Merci d'avance!

Lorenzo

Facundo Muñoz
Messages : 156
Enregistré le : 04 Juil 2019, 09:58
Contact :

Re: Problème boucle for

Messagepar Facundo Muñoz » 23 Sep 2020, 12:28

Bonjour,

pour faciliter la compréhension et la tâche de vous aider, veuillez

- fournir un exemple reproductible [1]. Par exemple,

Code : Tout sélectionner

dat <- read.table(
  text = '
pix pixmoins pixmoins4
vert vert vert
vert noir vert
noir noir vert',
  sep = " ",
  header = TRUE
)


- mettre du code entre les balises "Code". Pas comme texte.

- donner le résultat attendu ou souhaité

En l’occurrence, je ne comprends pas bien ce que vous souhaitez comme résultat. Dans votre première règle, quelle est la valeur de n ?.
Pour la première ligne de vos données, pix = vert, pixmoins1 = vert (j'assume pixmoins = pixmoins1). Donc, on applique la première règle. Quel numéro doit on mettre dans la nouvelle colonne ?
Je vois dans votre code que vous initialisez n = 1 (mais vous le faites dans le boucle). Donc, je suis confus. Ça aiderait si vous fournissiez le résultat attendu.

En outre, dans votre code, i prenne séquentiellement les valeurs du vecteur data$pix = vert, vert, noir. Je pense que vous vouliez peut-être utiliser les nombres de ligne ? Dans ce cas, faites plutôt

Code : Tout sélectionner

for(i in seq.int(nrow(data)))


Cordialement,

[1] viewtopic.php?f=1&t=7638
ƒacu.-

Eric Wajnberg
Messages : 776
Enregistré le : 11 Aoû 2008, 15:37
Contact :

Re: Problème boucle for

Messagepar Eric Wajnberg » 23 Sep 2020, 12:31

En complément de la réponse précédente :

1) Si vous définissez "n=1", l'instruction n[i-1] n'a aucun sens (et déclenche à lui seul le message d'erreur que vous obtenez).

2) R est un langage vectoriel. Vous n'avez pas besoin d'une boucle pour balayer toutes les valeurs d'un vecteur.

HTH, Eric.

Sébastien Rochette
Messages : 54
Enregistré le : 03 Juil 2020, 12:43
Contact :

Re: Problème boucle for

Messagepar Sébastien Rochette » 23 Sep 2020, 12:38

Je vous recommande de ne pas faire de boucle, mais de penser “vecteur”.
De la même manière que vous avez créé les colonnes "pixmoins\*" (avec
`lag()` je suppose), vous pouvez créer les colonnes de position des
pixels précédents.

Laissez moi reconstruire votre table

Code : Tout sélectionner

  library(dplyr)


Code : Tout sélectionner

    data <- tibble(
      pixel = c("vert", "vert", "vert", "vert", "noir", "vert", "noir", "noir", "vert")
    )
    # Les valeurs des pixels précédents
    data2 <- data %>%
      mutate(
        pix_moins1 = lag(pixel, 1),
        pix_moins4 = lag(pixel, 4)
      ) %>%
      # de la meme manière les valeurs des positions
      mutate(
        position = 1:n(),
        pos_moins1 = lag(position, 1),
        pos_moins4 = lag(position, 4)
      )
    data2

Code : Tout sélectionner

 
    ## # A tibble: 9 x 6
    ##   pixel pix_moins1 pix_moins4 position pos_moins1 pos_moins4
    ##   <chr> <chr>      <chr>         <int>      <int>      <int>
    ## 1 vert  <NA>       <NA>              1         NA         NA
    ## 2 vert  vert       <NA>              2          1         NA
    ## 3 vert  vert       <NA>              3          2         NA
    ## 4 vert  vert       <NA>              4          3         NA
    ## 5 noir  vert       vert              5          4          1
    ## 6 vert  noir       vert              6          5          2
    ## 7 noir  vert       vert              7          6          3
    ## 8 noir  noir       vert              8          7          4
    ## 9 vert  noir       noir              9          8          5


A partir de là, on peut faire les tests de manière vectorielle avec
`case_when`

Code : Tout sélectionner

    data2 %>%
      mutate(
        numero = case_when(
          pixel == "vert" & pix_moins1 == "vert" ~ pos_moins1,
          pixel == "vert" & pix_moins1 == "noir" & pix_moins4 == "vert" ~ pos_moins4,
          # ...
          TRUE ~ NA_integer_
        )
      )

Code : Tout sélectionner

 
    ## # A tibble: 9 x 7
    ##   pixel pix_moins1 pix_moins4 position pos_moins1 pos_moins4 numero
    ##   <chr> <chr>      <chr>         <int>      <int>      <int>  <int>
    ## 1 vert  <NA>       <NA>              1         NA         NA     NA
    ## 2 vert  vert       <NA>              2          1         NA      1
    ## 3 vert  vert       <NA>              3          2         NA      2
    ## 4 vert  vert       <NA>              4          3         NA      3
    ## 5 noir  vert       vert              5          4          1     NA
    ## 6 vert  noir       vert              6          5          2      2
    ## 7 noir  vert       vert              7          6          3     NA
    ## 8 noir  noir       vert              8          7          4     NA
    ## 9 vert  noir       noir              9          8          5     NA


Ça tombe plutôt bien que vous parliez de boucles dans R, parce je viens
tout juste de mettre en ligne un article de blog pour se passer des
boucles dans R :
https://thinkr.fr/comment-faire-des-boucles-en-r-ou-pas/

Ceci étant dit, si vous travaillez avec des pixels, vous devez
travaillez avec des images. Peut-être qu’il serait plus judicieux de
travailler avec le package {raster} qui vous permet de réaliser des
opérations entre pixels adjacents.
Sébastien
Dev, Consult, Formateur
ThinkR

Lorenzo Barrail
Messages : 13
Enregistré le : 23 Sep 2020, 07:45

Re: Problème boucle for

Messagepar Lorenzo Barrail » 23 Sep 2020, 13:15

Merci beaucoup pour votre réponse Eric,
Autant pour moi, je vais reformuler:
Le data frame sur lequel je travaille est issu de ce code:

Code : Tout sélectionner

matpix=matrix(c(1,1,1,1,1,0.245,0.154,1,1,0.255,0.265,1,1,
                0.544,0.111,1,1,1,1,1),nrow=5,ncol=4)
matpix2=as.vector(matpix)
matpix2=as.data.frame(matpix2)
for (i in 1:20)
{output[i]<-data.frame(c(i,i+1,i-1,i+4,i-4))}
colnames(output)=c(1:20)
tab=t(output)
data=cbind(matpix2,tab)
colnames(data)=c("couleur","i","i+1","i-1","i+4","i-4")
a=data$i
b=data$`i+1`
c=data$`i-1`
c[which(c == 0)] <- NA
d=data$`i+4`
e=data$`i-4`
e[which(e <= 0)] <- NA
data$pixplus1=data[data$i[b],1]
data$pixmoins1=data[data$i[c],1]
data$pixplus4=data[data$i[d],1]
data$pixmoins4=data[data$i[e],1]
data=data[,c(1,7:10)]


Le résultat souhaité est la création d'une nouvelle colonne attribuant à chaque valeur de la colonne "pix" un numéro de groupe.
Les groupes sont définis par les conditions:

n est le numéro du groupe qui prend la valeur 1 pour le premier groupe, puis à chaque itération, prend :

- soit la même valeur que l'itération précédente (Si pix= vert et pixmoins1=vert)
- soit la même valeur que l'avant avant avant dernière itération soit i-4 (Si pix=vert et pixmoins1=noir et pixmoins4=vert)
- soit n+1 si pix=vert et pixmoins1=noir et pixmoins4=noir
- soit 0 si pix=noir
A mon avis il faut initialiser les 4 premières valeurs

La colonne "numero" donnerait les valeurs suivantes: 0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0 (ici un seul groupe car c'est un test...)

Merci encore!
Lorenzo

Lorenzo Barrail
Messages : 13
Enregistré le : 23 Sep 2020, 07:45

Re: Problème boucle for

Messagepar Lorenzo Barrail » 23 Sep 2020, 13:39

Merci pour votre réponse Sébastien,

C'est presque ça!
En revanche je cherche à avoir un numéro de groupe dans la colonne numéro et non la position du pixmoins1 ou pixmoins4.
Est-il possible d'intégrer un vecteur n allant de 1 à n (nombre de groupes créés) au CASE WHEN? de manière à écrire le code suivant pour qu'il fonctionne:

Code : Tout sélectionner

data2 %>%
  mutate(
    numero = case_when(
      pixel == "vert" & pix_moins1 == "vert" ~ n,
      pixel == "vert" & pix_moins1 == "noir" & pix_moins4 == "vert" ~ #n[pixel précédent],
      pixel == "vert" & pix_moins1 == "noir" & pix_moins4 == "noir" ~# n+1, soit création d'un nouveau groupe,
      TRUE ~ NA_integer_
    )
  )


Et donc initialiser le n à 1?
Merci beaucoup de votre aide.

Lorenzo

Facundo Muñoz
Messages : 156
Enregistré le : 04 Juil 2019, 09:58
Contact :

Re: Problème boucle for

Messagepar Facundo Muñoz » 23 Sep 2020, 13:42

Rebonjour,

je suis navré, mais je comprends toujours pas.

1. Votre code ne marche pas.

Code : Tout sélectionner

> for (i in 1:20)
+ {output[i]<-data.frame(c(i,i+1,i-1,i+4,i-4))}
Error: object 'output' not found


J'ai essayé cette modification pour que ça marche. Dites moi si c'est correcte.

Code : Tout sélectionner

output <- matrix(NA, 5, 20)
for (i in 1:20) {
  output[, i]<-c(i,i+1,i-1,i+4,i-4)
}


2. Cela donne un data.frame nommé data comme celui-là :

Code : Tout sélectionner

   couleur pixplus1 pixmoins1 pixplus4 pixmoins4
1    1.000    1.000        NA    1.000        NA
2    1.000    1.000     1.000    0.245        NA
3    1.000    1.000     1.000    0.154        NA
4    1.000    1.000     1.000    1.000        NA
5    1.000    0.245     1.000    1.000     1.000
6    0.245    0.154     1.000    0.255     1.000
7    0.154    1.000     0.245    0.265     1.000
8    1.000    1.000     0.154    1.000     1.000
9    1.000    0.255     1.000    1.000     1.000
10   0.255    0.265     1.000    0.544     0.245
11   0.265    1.000     0.255    0.111     0.154
12   1.000    1.000     0.265    1.000     1.000
13   1.000    0.544     1.000    1.000     1.000
14   0.544    0.111     1.000    1.000     0.255
15   0.111    1.000     0.544    1.000     0.265
16   1.000    1.000     0.111    1.000     1.000
17   1.000    1.000     1.000       NA     1.000
18   1.000    1.000     1.000       NA     0.544
19   1.000    1.000     1.000       NA     0.111
20   1.000       NA     1.000       NA     1.000


3. Ensuite vous parlez de la colonne "pix", qui n'existe pas. Est-ce "couleur" ? Dans ce cas, les valeurs dans la colonne "couleur" sont 1, 0.245, 0.154, 0.255, 0.265, 0.544 et 0.111. Certaines valeurs sont répétées sur plusieurs lignes. Vous voulez un numéro de groupe pour chaque une de ces valeurs ?

4. Vous parlez des situations où pix = vert et pixmoins1 = vert. Or, on a des nombres, pas de texte, dans les colonnes.

5. Quel est le "premier" groupe ? celui qu’apparaît en premier dans data ?
ƒacu.-

Lorenzo Barrail
Messages : 13
Enregistré le : 23 Sep 2020, 07:45

Re: Problème boucle for

Messagepar Lorenzo Barrail » 23 Sep 2020, 14:13

Facundo,

En effet, votre code fonctionne et dans la cas présent, 1 vaut "noir" et les valeurs différentes de 1 valent "vert".
Ici, couleur=pix en effet, et je souhaiterais un numéro de groupe pour chacune des valeurs pix oui, selon les conditions énoncées plus tôt.
Le premier groupe est le groupe 1, groupe unique dans cet exemple, car:
n[0.245]=1 car pixmoins1=noir et pixmoins4= noir ici c'est la première valeur de n qui est affichée
n[0.154]=1 car pixmoins1=vert
n[0.255]=1 car pixmoins4=vert
etc..

Sébastien Rochette
Messages : 54
Enregistré le : 03 Juil 2020, 12:43
Contact :

Re: Problème boucle for

Messagepar Sébastien Rochette » 23 Sep 2020, 14:54

Je ne vois pas la variable nommée "n" dans votre jeu de données, mais peu importe la manière dont elle est créée, vous remplacez ma variable nommée "position" par le contenu de votre variable "n", et ça devrait fonctionner.

Mais vu votre code, je suppose que vous ne connaissez pas la syntaxe du {tidyverse}, auquel cas, je ne peux que vous renvoyer vers un article de blog pour comprendre les fonctions de base : https://thinkr.fr/utiliser-la-grammaire-dplyr-pour-triturer-ses-donnees/

J'espère que ça ne pose pas de soucis de renvoyer vers nos articles de blog sur ce forum. L'idée n'est pas spécialement de faire de la pub, mais d'éviter de ré-écrire des choses qu'on a déjà rédigée. Surtout quand elle est en français.
Sébastien
Dev, Consult, Formateur
ThinkR

Facundo Muñoz
Messages : 156
Enregistré le : 04 Juil 2019, 09:58
Contact :

Re: Problème boucle for

Messagepar Facundo Muñoz » 23 Sep 2020, 15:19

Ok, je crois que j'ai compris.
La difficulté est qu'on ne connais pas le nombre final de groupes à l'avance. Je ne vois pas moyen de se passer des boucles for avec structures if-else.

Code : Tout sélectionner

matpix=matrix(c(1,1,1,1,1,0.245,0.154,1,1,0.255,0.265,1,1,
                0.544,0.111,1,1,1,1,1),nrow=5,ncol=4)
matpix2=as.vector(matpix)
matpix2=as.data.frame(matpix2)
output <- matrix(NA, 5, 20)
for (i in 1:20) {
  output[, i]<-c(i,i+1,i-1,i+4,i-4)
}
colnames(output)=c(1:20)
tab=t(output)
data=cbind(matpix2,tab)
colnames(data)=c("couleur","i","i+1","i-1","i+4","i-4")
a=data$i
b=data$`i+1`
c=data$`i-1`
c[which(c == 0)] <- NA
d=data$`i+4`
e=data$`i-4`
e[which(e <= 0)] <- NA
data$pixplus1=data[data$i[b],1]
data$pixmoins1=data[data$i[c],1]
data$pixplus4=data[data$i[d],1]
data$pixmoins4=data[data$i[e],1]
data=data[,c(1,7:10)]


pixel_group <- function(x) {
  g <- integer(length(x))  # group of each pixel
  g0 <- 0  # current group number
  for (i in seq.int(g)) {
    if (x[i] == 1.0 || i == 1L) {
      ## pix = noir or in first pixel, keep group at 0
      g[i] <- 0
    } else if (x[i-1] != 1.0 || i <= 4L) {
      ## pix = vert and last pixel also green, keep current group
      ## also keep current group if last pixel not green, but at the beginning
      g[i] <- g0
    } else if (x[i-4] == 1.0) {
      ## pix = vert, pix-1 = noir, pix-4 = noir: create new group and update g
      g[i] <- g0 <- g0 + 1
    } else {
      ## pix = vert, pix-1 = noir, pix-4 = vert: same group as pix-4
      g[i] <- g[i-4]
    }
  }
  return(g)
}

(data$group <- pixel_group(data$couleur))
#>  [1] 0 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 0
ƒacu.-

Lorenzo Barrail
Messages : 13
Enregistré le : 23 Sep 2020, 07:45

Re: Problème boucle for

Messagepar Lorenzo Barrail » 23 Sep 2020, 15:33

Super c'est exactement ce que je voulais!

Merci infiniment et désolé pour la faible clarté de mes explications...

Lorenzo

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: Problème boucle for

Messagepar Mickael Canouil » 24 Sep 2020, 07:59

Bonjour,

petite alternative d'écriture des multiples affectations dans les if else

Code : Tout sélectionner

pixel_group2 <- function(x) {
  g <- integer(length(x))
  g0 <- 0
  for 
(i in seq.int(g)) {
    g[i] <- 
      if 
(x[i] == 1 || i == 1L) {
        0
      
} else if (x[- 1] != 1 || i <= 4L) {
        g0
      
} else if (x[- 4] == 1) {
        g0 <- g0 + 1
      
} else {
        g[- 4]
      }
  }
  g

L'incidence est négligeable/nulle au niveau des temps d'execution.

Code : Tout sélectionner

<- c(1, 1, 1, 1, 1, 0.245, 0.154, 1, 1, 0.255, 0.265, 1, 1, 0.544, 0.111, 1, 1, 1, 1, 1)
set.seed(20200924)
bm <- bench::mark(pixel_group(x), pixel_group2(x))
bm
#> # A tibble: 2 x 6
#>   expression           min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 pixel_group(x)    5.08µs   5.69µs   166812.    4.36MB      0  
#> 2 pixel_group2(x)    5.2µs   5.84µs   167208.   91.77KB     16.7
plot(bm)
#> Loading required namespace: tidyr
 

Image

Cordialement,
Mickaël
mickael.canouil.fr | rlille.fr


Retourner vers « Questions en cours »

Qui est en ligne

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