Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

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

margot julien
Messages : 46
Enregistré le : 28 Nov 2017, 12:10

Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar margot julien » 01 Oct 2021, 12:19

Bonjour,

j'aimerais supprimer des colonnes qui apparaissent en trop dans certains de mes fichiers dans un dossier "TESTin" et ensuite écrire les fichiers propres dans un fichier "TESTout". J'ai beaucoup de fichiers et il y a des erreurs dans certains mais pas dans d'autres, donc le but est de faire cela automatiquement. Ensuite je bind ces fichiers par noms de colonnes.

Je m'explique:

dans mon exemple, j'ai 2 fichiers que je veux réunir nommés "ww97_test_40_z1.xlsx" et ""ww97_test2_40_z1.xlsx".
Pour réunir ces 2 fichiers par noms de colonnes identiques, j'ai un code qui fonctionne mais seulement si les colonnes ont des noms identiques donc ici j'ai un souci:

Code : Tout sélectionner

df_base <- read_excel(listZ1Y97[1])
df_base <- mutate_all(df_base, as.character)
for (i in c(2:length(listZ1Y97))) {
   df_join <- read_excel(listZ1Y97[i])
   df_join <- mutate_all(df_join, as.character)
   df_base <- rbind(df_base,df_join)
}
df_base
View(df_base)
write.csv2(df_base, "D:\\FinalDataModel20210820\\TESTout.csv")



Avant de faire tourner ce code, j'aimerais donc supprimer automatiquement pour tous les fichiers de la liste dessous les colonnes "colToDelete1" et "colToDelete2" lorsqu'elles sont présentes (par exemple ici dans ww97_test_40_z1.xlsx).
Ensuite j'ai un autre problème, dans certains fichiers j'ai des colonnes avec les noms correctes: "HauteurPLante", "HauteurPLanteDroite", et "HauteurPLanteGauche" comme dans "ww97_test2_40_z1.xlsx" dans cet exemple, mais dans d'autres fichiers il y a un problème, les noms des colonnes "HauteurPLanteDroite", et "HauteurPLanteGauche" ont été écrit "HauteurPlante" donc j'ai 3 fois des colonnes qui s'appellent "HauteurPlante" et j'aimerais pour tous les fichiers supprimer les colonnes qui portent les noms "HauteurPLante" ou"HauteurPLanteDroite" ou "HauteurPLanteGauche" pour solutionner ce problème (ou alors une deuxième option serait de dire à R que si il voit une colonne nommée "HauteurPlante" une fois il ne fait rien mais si il voit une deuxième fois il remplace par "HauteurPLanteDroite" et une troisième fois par "HauteurPlanteGauche" afin que je garde ces colonnes mais je crois que ça serait un peu trop compliqué).

Ensuite j'aimerais écrire les fichiers propres dans "TESTout", pour pouvoir réunir les fichier avec "bind" avec mon code présenté dessus.
Pouvez-vous m'aider à faire ça automatiquement svp?
Merci par avance.

voici les 2 fichiers et voici mon code pour le moment:
Le code:

Code : Tout sélectionner

#Import libraries
library(stringr)
library(readxl)
library(WriteXLS)
library(dataframes2xls)
library(dplyr)
library(varhandle)
library(prettyR) #describe
 
options(scipen = 999) 
 
 
setwd ("D:\\FinalDataModel20210820\\in")
 
####year 97 - list files
listZ1Y97 <- list.files("D:\\FinalDataModel20210820\\TESTin",
                        pattern = glob2rx("ww97*_z1.xlsx"))
print(listZ1Y97)
 
 
 
#bind the files by col names and write it in TESTOUT:
df_base <- read_excel(listZ1Y97[1])
df_base <- mutate_all(df_base, as.character)
for (i in c(2:length(listZ1Y97))) {
   df_join <- read_excel(listZ1Y97[i])
   df_join <- mutate_all(df_join, as.character)
   df_base <- rbind(df_base,df_join)
}
df_base
View(df_base)
write.csv2(df_base, "D:\\FinalDataModel20210820\\TESTout.csv"


le premier fichier:

Code : Tout sélectionner

ww97_test_40_z1<-structure(list(site = c("Utzenstorf", "Utzenstorf", "Utzenstorf",
"Utzenstorf", "Utzenstorf"), colToDelete1 = c(3, 2, 1, 2, 1),
    colToDelete2 = c("t", "t", "t", "t", "t"), siteId = c("U1",
    "U1", "U1", "U1", "U1"), weather_station = c("UTZ", "UTZ",
    "UTZ", "UTZ", "UTZ"), HauteurPLante...6 = c(2, 2, 3, 4, 5
    ), HauteurPLante...7 = c(2, 2, 3, 4, 5), HauteurPLante...8 = c(2,
    2, 3, 4, 5), plotIdForMonitoring = c(6, 50, 54, 14, 41),
    plotIdForSowing = c(1112, 2015, 2411, 1413, 1614), rep = c(1,
    2, 3, 1, 2), expectedListNumber = c(111.1001, 111.1001, 111.1001,
    192.1013, 192.1013)), row.names = c(NA, -5L), class = c("tbl_df",
"tbl", "data.frame"))


et le deuxième fichier:

Code : Tout sélectionner

ww97_test2_40_z1<-structure(list(site = c("lieu2", "lieu2", "lieu2", "lieu2", "lieu2"
), siteId = c("l2", "l2", "l2", "l2", "l2"), weather_station = c("b",
"b", "b", "b", "b"), HauteurPLante = c(2, 2, 3, 4, 5), HauteurPLanteDroite = c(2,
2, 3, 4, 5), HauteurPLanteGauche = c(2, 2, 3, 4, 5), plotIdForMonitoring = c(6,
22, 54, 14, 41), rep = c(1, 2, 3, 1, 2), expectedListNumber = c(111.1001,
111.1001, 123, 192.1013, 192.1013), plotIdForSowing = c(1112,
2015, 2411, 111, 1614)), row.names = c(NA, -5L), class = c("tbl_df",
"tbl", "data.frame"))

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

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar Pierre-Yves Berrard » 01 Oct 2021, 13:59

Bonjour,

Pour les colonnes en trop, une solution est d'ajouter :

Code : Tout sélectionner

df_join$colToDelete1 <- NULL
df_join$colToDelete2 
<- NULL

Cela devrait marcher que les colonnes à effacer soient présentes ou pas.

Pour les variables HauteurPlante :

Code : Tout sélectionner

positions_hauteurPlante <- grep("^HauteurPLante", names(df_join))
names(df_join)[positions_hauteurPlante] <- c("HauteurPLante", "HauteurPLanteDroite", "HauteurPlanteGauche"


NB : comme il faut appliquer ces traitements au premier data.frame aussi, il vaudrait mieux que i commence à 1 et initialiser un data.frame() vide.
Ou mieux utiliser lapply en lieu et place de la boucle for.
PY

margot julien
Messages : 46
Enregistré le : 28 Nov 2017, 12:10

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar margot julien » 05 Oct 2021, 09:22

Bonjour,

merci beaucoup pour le retour.
J'ai ajouté les lignes de code (voir ci-dessous) et initialisé la boucle i à 1 au lieu de 2 mais ça ne fonctionne pas, j'ai dû louper quelque chose.
J'ai le message d'erreur suivant:
New names:
* HauteurPLante -> HauteurPLante...6
* HauteurPLante -> HauteurPLante...7
* HauteurPLante -> HauteurPLante...8
Error in rbind(deparse.level, ...) :
les nombres de colonnes des arguments ne correspondent pas


Voici le code:

Code : Tout sélectionner

####year 97 - list files
listZ1Y97 <- list.files("D:\\FinalDataModel20210820\\TESTin",
                        pattern = glob2rx("ww97*_z1.xlsx"))
print(listZ1Y97)


#bind the files by col names and write it in TESTOUT:
df_base <- read_excel(listZ1Y97[1])
df_base <- mutate_all(df_base, as.character)
for (i in c(1:length(listZ1Y97))) {
   positions_hauteurPlante <- grep("^HauteurPLante", names(df_join))
   names(df_join)[positions_hauteurPlante] <- c("HauteurPLante", "HauteurPLanteDroite", "HauteurPlanteGauche")
   df_join <- read_excel(listZ1Y97[i])
   df_join <- mutate_all(df_join, as.character)
   df_join$colToDelete1 <- NULL
   df_join$colToDelete2 <- NULL
   df_base <- rbind(df_base,df_join)
}
df_base


Qu'est ce que j'ai fait de faux?
Merci par avance.
Meilleures salutations.

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

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar Mickael Canouil » 05 Oct 2021, 10:08

Bonjour,

vous n'avez pas initialiser la boucle.
De plus votre renommage de colonnes, fait l'hypothèse que le grep renvoie toujours 3 valeurs, ni plus ni moins, puisque vous remplacer X valeurs par 3.

Notez l'usage des arguments de read_excel pour définir le type des données (voir la documentation).

Voici, une boucle for correcte :

Code : Tout sélectionner

library("readxl")
library("dplyr")

(
listZ1Y97 <- list.files("D:\\FinalDataModel20210820\\TESTin", pattern = "ww97.*_z1.xlsx"))

all_df <- vector(mode = "list",  length = length(listZ1Y97))
for (ifile in seq_len(listZ1Y97)) {
  df_tmp <- read_excel(path = listZ1Y97[[ifile]], col_types = "text")
  positions_hauteurPlante <- grep("^HauteurPLante", names(df_tmp))
  if (length(positions_hauteurPlante) == 3) {
    colnames(df_tmp)[positions_hauteurPlante] <- c("HauteurPLante", "HauteurPLanteDroite", "HauteurPlanteGauche")
  }
  df_tmp$colToDelete1 <- NULL
  df_tmp$colToDelete2 
<- NULL
  all_df
[[ifile]] <- df_tmp
}

df_base <- bind_rows(all_df) # do.call("rbind", all_df) serait l'équivalent R base, mais sans la vérification de la correspondance des colonnes  


La version avec lapply :

Code : Tout sélectionner

library("readxl")
library("dplyr")

(
listZ1Y97 <- list.files("D:\\FinalDataModel20210820\\TESTin", pattern = "ww97.*_z1.xlsx"))

all_df <- lapply(
  X = listZ1Y97,
  FUN = function(x) {
    df_tmp <- read_excel(path = x, col_types = "text")
    positions_hauteurPlante <- grep("^HauteurPLante", names(df_tmp))
    if (length(positions_hauteurPlante) == 3) {
      colnames(df_tmp)[positions_hauteurPlante] <- c("HauteurPLante", "HauteurPLanteDroite", "HauteurPlanteGauche")
    }
    df_tmp$colToDelete1 <- NULL
    df_tmp$colToDelete2 
<- NULL
    df_tmp
  
}
)

df_base <- bind_rows(all_df) # do.call("rbind", all_df) serait l'équivalent R base, mais sans la vérification de la correspondance des colonnes  


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

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

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar Pierre-Yves Berrard » 05 Oct 2021, 10:10

Bonjour,

A priori, la première instruction de la boucle doit être la lecture du fichier excel, ensuite seulement on peut commencer les traitements.

Il faut aussi initialiser un df_base vide : df_base <- data.frame()
PY

margot julien
Messages : 46
Enregistré le : 28 Nov 2017, 12:10

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar margot julien » 06 Oct 2021, 07:13

Bonjour,
Merci pour les différents retours, explications et codes.
1) Avec la première solution, j’ai bien ajouté «df_base <- data.frame() » comme ceci mais j’ai encore un souci :

Code : Tout sélectionner

df_base <- data.frame()
df_base <- read_excel(listZ1Y97[1])
df_base <- mutate_all(df_base, as.character)
for (i in c(1:length(listZ1Y97))) {
   positions_hauteurPlante <- grep("^HauteurPLante", names(df_join))
   names(df_join)[positions_hauteurPlante] <- c("HauteurPLante", "HauteurPLanteDroite", "HauteurPlanteGauche")
   df_join <- read_excel(listZ1Y97[i])
   df_join <- mutate_all(df_join, as.character)
   df_join$colToDelete1 <- NULL
   df_join$colToDelete2 <- NULL
   df_base <- rbind(df_base,df_join)
}
df_base


J’ai le message d’erreur suivant :
Error in grep("^HauteurPLante", names(df_join)) :
object 'df_join' not found
> df_base
data frame with 0 columns and 0 rows


2) Avec la deuxième solution :

Code : Tout sélectionner

library("readxl")
library("dplyr")

(listZ1Y97 <- list.files("F:\\FinalDataModel20210820\\TESTin", pattern = "ww97.*_z1.xlsx"))

all_df <- vector(mode = "list",  length = length(listZ1Y97))
for (ifile in seq_len(listZ1Y97)) {
   df_tmp <- read_excel(path = listZ1Y97[[ifile]], col_types = "text")
   positions_hauteurPlante <- grep("^HauteurPLante", names(df_tmp))
   if (length(positions_hauteurPlante) == 3) {
      colnames(df_tmp)[positions_hauteurPlante] <- c("HauteurPLante", "HauteurPLanteDroite", "HauteurPlanteGauche")
   }
   df_tmp$colToDelete1 <- NULL
   df_tmp$colToDelete2 <- NULL
   all_df[[ifile]] <- df_tmp
}

df_base2 <- bind_rows(all_df) # do.call("rbind", all_df) serait l'équivalent R base, mais sans la vérification de la correspondance des colonnes 
View(df_base2)

Ça ne fonctionne pas et j’ai le message d’erreur suivant :
Error in seq_len(listZ1Y97) :
argument must be coercible to non-negative integer
In addition: Warning messages:
1: In seq_len(listZ1Y97) : first element used of 'length.out' argument
2: In seq_len(listZ1Y97) : NAs introduced by coercion

3) Avec la troisième solution utilisant lapply ça fonctionne bien sur mes 2 fichiers « exemples » i.e. :

Code : Tout sélectionner

install.packages("readxl")
library("readxl")
install.packages("dplyr")
library("dplyr")
dplyr::bind_rows()

(listZ1Y97 <- list.files("F:\\FinalDataModel20210820\\TESTin", pattern = "ww97.*_z1.xlsx"))


all_df <- lapply(
   X = listZ1Y97,
   FUN = function(x) {
      df_tmp <- read_excel(path = x, col_types = "text")
      positions_hauteurPlante <- grep("^HauteurPLante", names(df_tmp))
      if (length(positions_hauteurPlante) == 3) {
         colnames(df_tmp)[positions_hauteurPlante] <- c("HauteurPLante", "HauteurPLanteDroite", "HauteurPlanteGauche")
      }
      df_tmp$colToDelete1 <- NULL
      df_tmp$colToDelete2 <- NULL
      df_tmp
   }
)

df_base <- bind_rows(all_df) # do.call("rbind", all_df) serait l'équivalent R base, mais sans la vérification de la correspondance des colonnes View(df_base)
View(df_base)

Merci pour tout, je vais utiliser la solution 3 qui fonctionne mais si on peut trouver (juste pour ma compréhension) pourquoi les solutions 1 et 2 ne fonctionnent pas ça serait super. Je préciser que dans mes « vrais » fichiers, j’ai des colonnes avec uniquement des « NA » dans certains fichiers.

Merci par avance.

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

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar Mickael Canouil » 06 Oct 2021, 08:55

Bonjour,

il faut changer seq_len par seq_along.

Cordialement,

PS : je ne traite pas les demandes en privé. Générer des fichiers fictifs (writexl) ressemblant à votre cas de figure et fournissez un code reproduisant l'erreur, de là, une aide plus précise pourra vous être apporté.
Mickaël
mickael.canouil.fr | rlille.fr

margot julien
Messages : 46
Enregistré le : 28 Nov 2017, 12:10

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar margot julien » 06 Oct 2021, 11:50

Bonjour,

parfait cela fonctionne maintenant pour les solutions 2 et 3, merci. J'ai trouvé pourquoi ça ne fonctionnait pas dans mes autres fichiers, c'était juste un problème de répertoire.

Si je veux écrire mes fichiers propres (donc avec les colonnes supprimées ou renommées) individuellement dans un dossier "out" avant de les joindre, comment puis-je faire svp?

merci par avance.
Cordialement,
Margot.

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

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar Mickael Canouil » 06 Oct 2021, 15:38

Ajoutez simplement la ligne de commande pour écrire le type de fichier que vous voulez (base::write*, readr::write*, writexl::write*) à partir de la variable/objet R que vous souhaitez.
Mickaël
mickael.canouil.fr | rlille.fr

margot julien
Messages : 46
Enregistré le : 28 Nov 2017, 12:10

Re: Faire une boucle pour supprimer des colonnes dans une liste de fichiers et écrire les fichiers

Messagepar margot julien » 08 Nov 2021, 07:46

Bonjour,

merci à tous, le problème est résolu.

Cordialement,
M.


Retourner vers « Questions en cours »

Qui est en ligne

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