Manipulatio de données : boucles imbriquées dans R ?

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

Luca Niang
Messages : 26
Enregistré le : 01 Avr 2012, 10:41

Manipulatio de données : boucles imbriquées dans R ?

Messagepar Luca Niang » 25 Oct 2021, 14:30

Bonjour,
Je bloque actuellement sur une manipulation, compliquée pour moi, mais peut être simple pour vous, peut etre sauriez vous m'aider

J'ai cette table,
Image

dont voici le code reproductible

Code : Tout sélectionner

 z<- structure(list(V4 = c('563','563','482','482','017','017'),
                    V5 = c("mere","fille","mere","fille","mere","fille"),
                    V6=c("050134","050139","330292","330293","330132","330295"),
                  V7=c('050135','','','330294','330133',''),
                  V8=c('','','','330295','',''),
                  V9=c('','','','330296','','')),
               .Names = c("id","mereoufille",'V6','V7','V8','V9'),
               row.names = c(NA, -6L), class = "data.frame")


J'aimerais aboutir à ça
Image

Je veux au final passer à une table avec une ligne par relation.
Une mère peut avoir plusieurs filles, mais plusieurs mères peuvent aussi avoir une fille.
Quoi qu'il arrive c'est une relation de 1 à n ou de n à 1.

Mon idée etait de faire une boucle pour analyser ce qu'il se passe dans chaque id,
Puis regarder si on avait + d'éléments dans les colonnes de la ligne mère ou de la ligne fille,
Puis de créer des ignes, avec 1er nombre à mère vers nombre à fille, puis 2e nombre à mère vers 2e nombre à fille et si pas de 2e nombre à mère, alors remplacer par 1er nombre à mère ou bien si pas de 2e nombre à fille, remplacer par 2e nombre à mère...et finir la boucle lorsque l'ensemble des colonnes non vides des deux lignes ont été analysées, le tout en complétant une matrice vide que j'aurais initialisé au preaable...

L'idée de l'algo est celle-ci - mais je ne sais pas du tout coder ça en R.
Peut être que c'est ce problème est basique et que des fonctions d'un package pourraient répondre à ma question

Merci mille fois, d'avoir déjà tout lu jusque là
Merci 10 mille fois, si vous prenez le temps de me répondre ! :)

Luca

Pierre-Yves Berrard
Messages : 1029
Enregistré le : 12 Jan 2016, 23:30

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Pierre-Yves Berrard » 25 Oct 2021, 15:28

Bonjour,

Une proposition :

Code : Tout sélectionner

library(dplyr)

# trie, précaution au cas où "fille" avant "mere"
<- arrange(z, desc(mereoufille))

# transforme le data.frame en liste
liste_individus <-
  apply(
    z[-(1:2)],
    MARGIN = 1,
    function(x) x[!= ""]
  )

# calcule les combinaisons meres/filles
res <-
  liste_individus %>%
  split(z$id) %>%
  lapply(
    function(x) expand.grid(parcellemere = x[[1]], parcellefille = x[[2]])
  ) %>%
  bind_rows(.id = "id"
PY

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

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Logez Maxime » 25 Oct 2021, 20:24

Bonjour,

Une autre possibilité :

Code : Tout sélectionner

library(tidyr)
library(dplyr)
test <- gather(z, key = "v1", value = "v2", -(1:2))
test <- test[test$v2!="",]
test <- spread(test, 2, 4)
test %>% select(1, 4, 3) %>% fill(mere, fille)
Cordialement,
Maxime

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

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Mickael Canouil » 27 Oct 2021, 08:20

Bonjour,

une version via {data.table} :

Code : Tout sélectionner

z<- structure(list(V4 = c('563','563','482','482','017','017'),
                    V5 = c("mere","fille","mere","fille","mere","fille"),
                    V6=c("050134","050139","330292","330293","330132","330295"),
                  V7=c('050135','','','330294','330133',''),
                  V8=c('','','','330295','',''),
                  V9=c('','','','330296','','')),
               .Names = c("id","mereoufille",'V6','V7','V8','V9'),
               row.names = c(NA, -6L), class = "data.frame"

Code : Tout sélectionner

library(data.table)
dcast(
  data = melt(setDT(z), id.vars = c("id", "mereoufille"))[!value %in% ""], 
  formula 
= id ~ mereoufille,
  value.var = "value",
  fun.aggregate = list
)[
  j = lapply(.SD, unlist),
  by = "id"
]
#>     id  fille   mere
#> 1: 017 330295 330132
#> 2: 017 330295 330133
#> 3: 482 330293 330292
#> 4: 482 330294 330292
#> 5: 482 330295 330292
#> 6: 482 330296 330292
#> 7: 563 050139 050134
#> 8: 563 050139 050135     


EDIT : un petit benchmark (à noter que les performances devrait être vraiment différente avec l'augmentation de la taille du jeux de données)
Image

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

Luca Niang
Messages : 26
Enregistré le : 01 Avr 2012, 10:41

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Luca Niang » 27 Oct 2021, 09:04

Merci beaucoup Pierre-Yves, Maxime et Mickael pour vos solutions
Autant celle de Maxime me semblait à ma portée, autant pour les deux autres, elles utilisent des fonctions que je ne connaissais pas :)
C'est fou comme les syntaxes varient selon les packages et comme il y a de nombreuses manières de faire,

Une collègue m'a aussi écrit ça, qui fonctionne aussi

Code : Tout sélectionner

# lire library zoo, celle-ci permet d attribuer la valeur de la ligne au-dessus a une cellule NA
library(zoo)
# declarer la variable d environnement et la liste de la boucle

id = unique(z$id)
list_out = list()
# debut de la boucle

for (i in 1:length(id)){              # declare que i = le nombre d id, soit le nombre de boucle a tourner
  mat = z[z$id==id[i],]               # recupere la matrice du premier id
  transpose = data.frame(t(mat))      # construit un tableau transpose, type 'pivot' de mat   
  names(transpose)[1] = "var1"        # renomme les champs x1 et x2 genere automatiquement par R - important car lors de la deuxieme boucle ces champs deviennent x3 et x4 et la boucle ne peut plus tourner par la suite
  names(transpose)[2] = "var2"        # idem
  transpose_clean = transpose[3:6,]   # permet d ecarter les 2 premieres lignes de la table transpose, elles sont superflues (id et mere/fille)
  transpose_clean$id_na = paste0(transpose_clean$var1, ' ', transpose_clean$var2)        # pretraitement pour l etape suivante : concatenation des numeros mere et filles
  transpose_without_NANA = transpose_clean[transpose_clean$id_na != 'NA NA', ]       # ecarte les lignes ou la concatenation des numeros mere et fille = NA NA, c est a dire les lignes ou le numero de mere ainsi que le numero de fille n est pas present (par exemple, pour v8 ou v9)
  transpose_zoo = zoo(transpose_without_NANA)                                       # permet d attribuer a une cellule NA la valeur de la ligne se situant juste au-dessus --> permet de recuperer les numeros mere / fille pour les cellules NA restantes
  transpose_filled = data.frame(na.locf(transpose_zoo))                             # transformation de l objet issu de l etape ci dessus en data.frame pour l exploiter par la suite
  out = data.frame(rep(id[i], nrow(transpose_filled)), rep(transpose_filled))       # contient la sortie de la boucle avec autant de ligne qu il y a dans chaque id
  colnames(out) = c("id", "parcelle_mere", "parcelle_fille", 'concat_parcelle_mf')  # renomme les champs par des noms parlants
  list_out[[i]] = out}                #
table_arbo_mere_fille = do.call(rbind,list_out)                                     # lie toutes les sorties generees par la boucle (autant de sorties que d id) en un seul tableau


Par curiosité, comment est-ce possible à mon tour de tester les vitesses sur mon jeu de données réel bien plus gros pour vos différentes requêtes ?

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

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Mickael Canouil » 27 Oct 2021, 10:26

Le code de votre collègue ne donne pas du tout le résultat attendu (d'après votre exemple) et/ou équivalent à nos propositions (sans compter l'aspect performance qu'il n'y a pas besoin de mesurer).

Code : Tout sélectionner

z<- structure(list(V4 = c('563','563','482','482','017','017'),
                    V5 = c("mere","fille","mere","fille","mere","fille"),
                    V6=c("050134","050139","330292","330293","330132","330295"),
                  V7=c('050135','','','330294','330133',''),
                  V8=c('','','','330295','',''),
                  V9=c('','','','330296','','')),
               .Names = c("id","mereoufille",'V6','V7','V8','V9'),
               row.names = c(NA, -6L), class = "data.frame") 
# lire library zoo, celle-ci permet d attribuer la valeur de la ligne au-dessus a une cellule NA
library(zoo)
#> 
#> Attaching package: 'zoo'
#> The following objects are masked from 'package:base':
#> 
#>     as.Date, as.Date.numeric
# declarer la variable d environnement et la liste de la boucle

id = unique(z$id)
list_out = list()
# debut de la boucle

for (i in 1:length(id)){              # declare que i = le nombre d id, soit le nombre de boucle a tourner
  mat = z[z$id==id[i],]               # recupere la matrice du premier id
  transpose = data.frame(t(mat))      # construit un tableau transpose, type 'pivot' de mat   
  names(transpose)[1] = "var1"        # renomme les champs x1 et x2 genere automatiquement par R - important car lors de la deuxieme boucle ces champs deviennent x3 et x4 et la boucle ne peut plus tourner par la suite
  names(transpose)[2] = "var2"        # idem
  transpose_clean = transpose[3:6,]   # permet d ecarter les 2 premieres lignes de la table transpose, elles sont superflues (id et mere/fille)
  transpose_clean$id_na = paste0(transpose_clean$var1, ' ', transpose_clean$var2)        # pretraitement pour l etape suivante : concatenation des numeros mere et filles
  transpose_without_NANA = transpose_clean[transpose_clean$id_na != 'NA NA', ]       # ecarte les lignes ou la concatenation des numeros mere et fille = NA NA, c est a dire les lignes ou le numero de mere ainsi que le numero de fille n est pas present (par exemple, pour v8 ou v9)
  transpose_zoo = zoo(transpose_without_NANA)                                       # permet d attribuer a une cellule NA la valeur de la ligne se situant juste au-dessus --> permet de recuperer les numeros mere / fille pour les cellules NA restantes
  transpose_filled = data.frame(na.locf(transpose_zoo))                             # transformation de l objet issu de l etape ci dessus en data.frame pour l exploiter par la suite
  out = data.frame(rep(id[i], nrow(transpose_filled)), rep(transpose_filled))       # contient la sortie de la boucle avec autant de ligne qu il y a dans chaque id
  colnames(out) = c("id", "parcelle_mere", "parcelle_fille", 'concat_parcelle_mf')  # renomme les champs par des noms parlants
  list_out[[i]] = out}                #
table_arbo_mere_fille = do.call(rbind,list_out)                                     # lie toutes les sorties generees par la boucle (autant de sorties que d id) en un seul tableau
table_arbo_mere_fille
#>     id parcelle_mere parcelle_fille concat_parcelle_mf
#> 1  563        050134         050139      050134 050139
#> 2  563        050135                           050135 
#> 3  563                                                
#> 4  563                                                
#> 5  482        330292         330293      330292 330293
#> 6  482                       330294             330294
#> 7  482                       330295             330295
#> 8  482                       330296             330296
#> 9  017        330132         330295      330132 330295
#> 10 017        330133                           330133 
#> 11 017                                                
#> 12 017  

Il y a fort à parier que le code {data.table} donne les meilleurs performance pour un (très) gros jeux de de données.
Le code de Pierre-yves devrait rester tout même proche en performance (sur la métrique du temps de calcul).

Dans votre cas, une simple mesure via un system.time(...) devrait être largement suffisante.
Pour un "vrai" benchmark avec plusieurs itérations du code, il y a plusieurs extensions: {bench}, {microbenchmark}, {benchr}, ...
Mickaël
mickael.canouil.fr | rlille.fr

Bastien Gamboa
Messages : 151
Enregistré le : 13 Jan 2011, 21:31

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Bastien Gamboa » 27 Oct 2021, 12:17

Bonjour,

Une autre approche utilisant reshape et merge :

Code : Tout sélectionner

tata <- reshape(z, varying=c("V6", "V7", "V8", "V9"), v.names="Valeur", idvar=c("id", "mereoufille"), direction="long")
tutu <- tata[tata$mereoufille=="mere"  & tata$Valeur!="",c("id", "Valeur")]
tete <- tata[tata$mereoufille=="fille" & tata$Valeur!="",c("id", "Valeur")]
colnames(tutu) <- c("id", "ParcelleMere")
colnames(tete) <- c("id", "ParcelleFille")
merge(tutu, tete, by="id", all=TRUE)

HTH,
Bastien

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

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Gabriel Terraz » 27 Oct 2021, 13:03

Bonjour,

Une autre possibilité en base :

Code : Tout sélectionner

  z <- z[order(z$id, z$mereoufille), ]
  exp2 <- function(x,y) expand.grid(x[x!=""], y[y!=""])
  bb <- by(z, z$id, function(x) exp2(unlist(x[2, 3:6]), unlist(x[1, 3:6])))
  do.call(rbind, bb)

Luca Niang
Messages : 26
Enregistré le : 01 Avr 2012, 10:41

Re: Manipulatio de données : boucles imbriquées dans R ?

Messagepar Luca Niang » 27 Oct 2021, 13:42

Mickael Canouil a écrit :Le code de votre collègue ne donne pas du tout le résultat attendu


Effectivement, elle avait remplacé manuellement mon exemple par des NA avant de me transmettre le code
le code était donc celui ci

Code : Tout sélectionner

# SCRIPT BOUCLE ARBORESCENCE MERE / FILLE

# pour utiliser certaines fonctionnalites, les cellules contenant des "" doivent etre remplacees par des NA

z<- structure(list(V4 = c('563','563','482','482','017','017'),
                   V5 = c("mere","fille","mere","fille","mere","fille"),
                   V6=c("050134","050139","330292","330293","330132","330295"),
                   V7=c('050135',NA,NA,'330294','330133',NA),
                   V8=c(NA,NA,NA,'330295',NA,NA),
                   V9=c(NA,NA,NA,'330296',NA,NA)),
              .Names = c("id","mereoufille",'V6','V7','V8','V9'),
              row.names = c(NA, -6L), class = "data.frame")



# lire library zoo, celle-ci permet d attribuer la valeur de la ligne au-dessus a une cellule NA

library(zoo)



# declarer la variable d environnement et la liste de la boucle

id = unique(z$id)
list_out = list()



# debut de la boucle

for (i in 1:length(id)){              # declare que i = le nombre d id, soit le nombre de boucle a tourner
  mat = z[z$id==id[i],]               # recupere la matrice du premier id
  transpose = data.frame(t(mat))      # construit un tableau transpose, type 'pivot' de mat   
  names(transpose)[1] = "var1"        # renomme les champs x1 et x2 genere automatiquement par R - important car lors de la deuxieme boucle ces champs deviennent x3 et x4 et la boucle ne peut plus tourner par la suite
  names(transpose)[2] = "var2"        # idem
  transpose_clean = transpose[3:6,]   # permet d ecarter les 2 premieres lignes de la table transpose, elles sont superflues (id et mere/fille)
  transpose_clean$id_na = paste0(transpose_clean$var1, ' ', transpose_clean$var2)        # pretraitement pour l etape suivante : concatenation des numeros mere et filles
  transpose_without_NANA = transpose_clean[transpose_clean$id_na != 'NA NA', ]       # ecarte les lignes ou la concatenation des numeros mere et fille = NA NA, c est a dire les lignes ou le numero de mere ainsi que le numero de fille n est pas present (par exemple, pour v8 ou v9)
  transpose_zoo = zoo(transpose_without_NANA)                                       # permet d attribuer a une cellule NA la valeur de la ligne se situant juste au-dessus --> permet de recuperer les numeros mere / fille pour les cellules NA restantes
  transpose_filled = data.frame(na.locf(transpose_zoo))                             # transformation de l objet issu de l etape ci dessus en data.frame pour l exploiter par la suite
  out = data.frame(rep(id[i], nrow(transpose_filled)), rep(transpose_filled))       # contient la sortie de la boucle avec autant de ligne qu il y a dans chaque id
  colnames(out) = c("id", "parcelle_mere", "parcelle_fille", 'concat_parcelle_mf')  # renomme les champs par des noms parlants
  list_out[[i]] = out}                #

table_arbo_mere_fille = do.call(rbind,list_out)                                     # lie toutes les sorties generees par la boucle (autant de sorties que d id) en un seul tableau


Merci à tous pour toutes vos réponses, j'ai pu adapter quelques uns de vos différents codes sur mon réel tableau


Retourner vers « Questions en cours »

Qui est en ligne

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