Ajout de colonne en fonction du nom du CSV

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

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

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Mickael Canouil » 10 Nov 2020, 15:54

Pour les francophones, read.csv2().

EDIT : ou la version longue de Facundo ;)
Mickaël
mickael.canouil.fr | rlille.fr

Loic SALAUN 2
Messages : 17
Enregistré le : 15 Oct 2020, 08:22

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Loic SALAUN 2 » 10 Nov 2020, 16:00

Ok, ça marche... (je vois que j'ai beaucoup de choses à apprendre...)
Encore merci,

Loic SALAUN 2
Messages : 17
Enregistré le : 15 Oct 2020, 08:22

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Loic SALAUN 2 » 10 Nov 2020, 16:03

Arffff, j'ai un nouveau problème... Il y a des points pour lesquels je n'ai pas de csv (car pas de chiroptères enregistrés... donc monscript n'a pas créer de csv). Du coup quand je modifie

Code : Tout sélectionner

ecoutes <- do.call("rbind", lapply(formatC(1:3, width =3 , flag = "0"), lire_point_ecoute))

En

Code : Tout sélectionner

ecoutes <- do.call("rbind", lapply(formatC(1:142, width =3 , flag = "0"), lire_point_ecoute))

Il me renvoie une erreur : Error in file(file, "rt") : argument 'description' incorrect
...

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

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Mickael Canouil » 10 Nov 2020, 16:07

Ajoutez une condition :

Code : Tout sélectionner

if (file.exists(...)) { ... }


Etant partisan du moindre effort (ou développeur) :

Code : Tout sélectionner

sprintf("%03d", 1:142) # = formatC(1:142, width = 3 , flag = "0")  
Mickaël
mickael.canouil.fr | rlille.fr

Loic SALAUN 2
Messages : 17
Enregistré le : 15 Oct 2020, 08:22

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Loic SALAUN 2 » 13 Nov 2020, 08:52

Bonjour,
Je ne vois pas quoi écrire dans file.exists, j'ai essayé plusieurs solutions mais aucune ne fonctionne :

Code : Tout sélectionner

 ifelse (file.exists("*.csv")== TRUE, ecoutes <- do.call("rbind", lapply(formatC(1:142, width =3 , flag = "0"), lire_point_ecoute)),print("Pas de csv"))
[1] "Pas de csv"
[1] "Pas de csv"

ifelse (file.exists("res_5_", x, "__SDOA_nuit_.*.csv")== TRUE, ecoutes <- do.call("rbind", lapply(formatC(1:142, width =3 , flag = "0"), lire_point_ecoute)),print("Pas de csv"))
Error in file.exists("res_5_", x, "__SDOA_nuit_.*.csv") :
  objet 'x' introuvable
 
  if (file.exists(".csv")== TRUE) {ecoutes <- do.call("rbind", lapply(sprintf("%03d", 1:142), lire_point_ecoute))}


Mais rien ne fonctionne...

Question à part : que signifie le %03d dans la proposition de simplification de Mickael ?

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

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Mickael Canouil » 13 Nov 2020, 09:13

Avez-vous pensé à regarder la documentation de la fonction avant de l'utiliser ?

Code : Tout sélectionner

?file.exists


Concernant l'autre question, la réponse est aussi dans la documentation de ?sprintf
03d => entier sur trois caractères avec des zéros au début.

Le but du jeu, si je puis dire, n'est pas de faire des copier-coller et de la programmation en mode essai-erreur.
Que ce soit R ou général, la documentation est le premier endroit à regarder en cas de "problème".


Avant d'écrire du code (surtout si on débute), il est bien de poser la problématique en "français" (pseudo-code).
file.exists() permet de tester l'existence d'un fichier, à quel endroit du script faut-il le faire ? Probablement pas après avoir lu et aggrégé les fichiers (dont ceux qui n'existent pas).
Mickaël
mickael.canouil.fr | rlille.fr

Loic SALAUN 2
Messages : 17
Enregistré le : 15 Oct 2020, 08:22

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Loic SALAUN 2 » 13 Nov 2020, 10:06

Désolé de débuter...
J'avais bien fait help(file.exists) mais n'y avais pas trouvé de réponse. J'ai donc cherché sur internet mais sans trouver vraiment des choses plus claires.
Pour %03d (sprintf) je n'ai effectivement pas regardé dans la doc de sprintf car j'ai cru que c'était une abréviation ''générale"...
Et j'ai bien essayé de rédigé un pseudo-code, pour moi :
Si le fichier CSV existe alors je place dans ecoutes, mes numéros de points et mes autres colonnes sinon j'imprime "pas de csv" (pour le sinon j'espérais voir les numéros pour lesquels il n'y avaient pas noms... afin de faire un contrôle).
Mais bon... Merci quand même. Je pense effectivement que ce script est trop compliqué pour moi et je vais faire la manip à la main (cela me prendra finalement moins longtemps...)
Encore merci.

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

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Mickael Canouil » 13 Nov 2020, 11:16

Nul besoin de s'excuser.

Visiblement le problème est plus sur la compréhension de la solution qui vous a été proposée.

Code : Tout sélectionner

lire_point_ecoute <- function(x, path = ".") {
  fname <- list.files(
    path = path,
    pattern = paste0("res_15", x, "SDOA_nuit_.*.csv"),
    full.names = TRUE
  
)
  ans <- read.csv(fname)
  ans$point_id <- x
  ans
[c("point_id", setdiff(names(ans), "point_id"))]
}

ecoutes <- do.call("rbind", lapply(formatC(1:3, width = 3, flag = "0"), lire_point_ecoute)) 

Avez-compris ce que fait la fonction lire_point_ecoute() ? Ce que fait list.files() ? Savez-vous ce que fait le lapply() ? le do.call() ?
J'ai l'impression, que la réponse est négative à l'ensemble de ces questions, ce qui en effet ne permet pas d'adapter le code à votre utilisation.
Mickaël
mickael.canouil.fr | rlille.fr

Loic SALAUN 2
Messages : 17
Enregistré le : 15 Oct 2020, 08:22

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Loic SALAUN 2 » 13 Nov 2020, 12:47

Il est possible que je n’aie tout saisi… Mais j’ai cherché au fur et à mesure des réponses… Car clairement mon objectif est de comprendre sinon j’aurais réellement eu plus vite fait de faire les manips à la main… Après c’est peut-être l’imbrication des fonctions les unes dans les autres qui me posent plus de problème de compréhension.
Pour la fonction lire_point_ecoute, j’ai en effet quelques difficultés à comprendre le tout mais je pensais avoir saisi le principe, en gros je créé une liste à partir de list.files en indiquant que dans cette liste x = numéro de points d’écoutes, puis je lui dit d’ajouter la colonne x à un csv. La partie que je pense avoir le plus de mal à interpréter c’est : function(x, path = ’’.’’)
List.files : (ça je l’avais déjà utilisé), si j’ai bien compris cela créé simplement une liste avec les noms de mes fichiers
Lapply : j’applique la fonction ‘’lire_point_ecoute’’ à ma liste créer par formatC (qui est une liste de 1 à 142 de largeur 3 (donc à priori 001, 010,100…)
Do.call j’avoue avoir plus de mal à saisir le sens exact. Si j’ai bien compris cela applique Rbind (donc ajout de ligne) à ma liste. Mais en effet cela me paraît un pas très limpide…
Merci de m'éclairer car il y a sûrement des erreurs dans ma compréhension,

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

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Mickael Canouil » 13 Nov 2020, 13:48

Voici un exemple reproductible que je vous invite à exécuter ligne par ligne pour vous aider à comprendre.

1. Génération de faux fichiers

Code : Tout sélectionner

unlink(file.path(tempdir(), "SDOA_nuit"), recursive = TRUE)
dir.create(file.path(tempdir(), "SDOA_nuit"))
for (i in 1:5) {
  write.csv2(
    x = sleep, 
    file 
= tempfile(
      pattern = paste0("res_15", sprintf("%03d", i), "SDOA_nuit_"), 
      tmpdir 
= file.path(tempdir(), "SDOA_nuit"), 
      fileext 
= ".csv"
    )
  )
}
cat("", file = file.path(tempdir(), "SDOA_nuit", "some_random_file.csv"))
list.
files(path = file.path(tempdir(), "SDOA_nuit"))
#> [1] "res_15001SDOA_nuit_3eb42c4a12.csv"   "res_15002SDOA_nuit_3eb46944359a.csv"
#> [3] "res_15003SDOA_nuit_3eb41c5240f3.csv" "res_15004SDOA_nuit_3eb43f6a739f.csv"
#> [5] "res_15005SDOA_nuit_3eb45dfa7d29.csv" "some_random_file.csv"    

2. Test du contenu de la fonction (Facundo faute d'exemple reproductible et comme il l'a dit n'a pas testé le code, c'est donc à vous de vérifier que le contenu est le bon)

Code : Tout sélectionner

<- 1 # parameter
path <- file.path(tempdir(), "SDOA_nuit") # parameter

fname <- list.files(# list full path of files in "path" according to the "pattern"
  path = path, 
  pattern 
= paste0("res_15", formatC(x, width = 3, flag = "0"), "SDOA_nuit_.*.csv"), 
  full
.names = TRUE
)
fname
#> [1] "D:\\Profils\\mcanouil\\AppData\\Local\\Temp\\RtmpK2F8ir/SDOA_nuit/res_15001SDOA_nuit_3eb42c4a12.csv"
ans <- read.csv2(fname) # import a french csv as a data.frame
ans$point_id <- x # add a column "point_id" with the value in "x"
ans[c("point_id", setdiff(names(ans), "point_id"))] # reorder column to have "point_id" as first column
#>    point_id  X extra group ID
#> 1         1  1   0.7     1  1
#> 2         1  2  -1.6     1  2
#> 3         1  3  -0.2     1  3
#> 4         1  4  -1.2     1  4
#> 5         1  5  -0.1     1  5    

3. Test de la fonction

Code : Tout sélectionner

lire_point_ecoute <- function(x, path) {
  fname <- list.files( 
    path 
= path,
    pattern = paste0("res_15", formatC(x, width = 3, flag = "0"), "SDOA_nuit_.*.csv"),
    full.names = TRUE
  
)
  ans <- read.csv2(fname) 
  ans$point_id 
<- formatC(x, width = 3, flag = "0")
  ans[c("point_id", setdiff(names(ans), "point_id"))] 
}
lire_point_ecoute(= 1, path = file.path(tempdir(), "SDOA_nuit"))
#>    point_id  X extra group ID
#> 1       001  1   0.7     1  1
#> 2       001  2  -1.6     1  2
#> 3       001  3  -0.2     1  3
#> 4       001  4  -1.2     1  4
#> 5       001  5  -0.1     1  5

list_df_from_csv <- lapply( #  read all files using the integer id
  X = 1:5, 
  FUN 
= lire_point_ecoute, path = file.path(tempdir(), "SDOA_nuit")
)
str(list_df_from_csv, 1)
#> List of 5
#>  $ :'data.frame':    20 obs. of  5 variables:
#>  $ :'data.frame':    20 obs. of  5 variables:
#>  $ :'data.frame':    20 obs. of  5 variables:
#>  $ :'data.frame':    20 obs. of  5 variables:
#>  $ :'data.frame':    20 obs. of  5 variables:

ecoutes <- do.call("rbind", list_df_from_csv)
str(ecoutes)
#> 'data.frame':    100 obs. of  5 variables:
#>  $ point_id: chr  "001" "001" "001" "001" ...
#>  $ X       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ extra   : num  0.7 -1.6 -0.2 -1.2 -0.1 3.4 3.7 0.8 0 2 ...
#>  $ group   : int  1 1 1 1 1 1 1 1 1 1 ...
#>  $ ID      : int  1 2 3 4 5 6 7 8 9 10 ...    

4. Supprression d'un fichier pour simuler votre "problème"

Code : Tout sélectionner

file_to_delete <- list.files( 
  path 
= file.path(tempdir(), "SDOA_nuit"),
  pattern = paste0("res_15", formatC(1, width = 3, flag = "0"), "SDOA_nuit_.*.csv"),
  full.names = TRUE
)
file_to_delete
#> [1] "D:\\Profils\\mcanouil\\AppData\\Local\\Temp\\RtmpK2F8ir/SDOA_nuit/res_15001SDOA_nuit_3eb42c4a12.csv"
file.exists(file_to_delete) # if if file exist
#> [1] TRUE
unlink( # delete file "1" to reproduce the issue at hand
  x = list.files( 
    path 
= file.path(tempdir(), "SDOA_nuit"),
    pattern = paste0("res_15", formatC(1, width = 3, flag = "0"), "SDOA_nuit_.*.csv"),
    full.names = TRUE
  
)
)

5. reutilisation de la boucle lapply() avec le fichier manquant

Code : Tout sélectionner

list_df_from_csv <- lapply( #  read all files using the integer id
  X = 1:5, 
  FUN 
= lire_point_ecoute, path = file.path(tempdir(), "SDOA_nuit")
)
#> Error in file(file, "rt"): invalid 'description' argument
lire_point_ecoute(= 1, path = file.path(tempdir(), "SDOA_nuit"))
#> Error in file(file, "rt"): invalid 'description' argument    

6. De nouveau, essai du code de la fonction en dehors de celle-ci

Code : Tout sélectionner

<- 1 # parameter
path <- file.path(tempdir(), "SDOA_nuit") # parameter
fname <- list.files(# list full path of files in "path" according to the "pattern"
  path = path, 
  pattern 
= paste0("res_15", formatC(x, width = 3, flag = "0"), "SDOA_nuit_.*.csv"), 
  full
.names = TRUE
)
fname # nothing
#> character(0)
ans <- read.csv2(fname) # error because R can't read nothing
#> Error in file(file, "rt"): invalid 'description' argument    

6. Maintenant qu'on a identifier que le problème est que fname ne contient "rien", on peut ajouter une condition

Code : Tout sélectionner

# Because the pattern uses regular expression ".*", file.exists can't be used here
# But we can check if fname contains something, i.e., a path, before doing anything else "nothing"
if (length(fname) != 0) {
  ans <- read.csv2(fname)
}

7. Modification de la fonction pour inclure la correction

Code : Tout sélectionner

lire_point_ecoute <- function(x, path) {
  fname <- list.files( 
    path 
= path,
    pattern = paste0("res_15", formatC(x, width = 3, flag = "0"), "SDOA_nuit_.*.csv"),
    full.names = TRUE
  
)
  if (length(fname) != 0) {
    ans <- read.csv2(fname) 
    ans$point_id 
<- formatC(x, width = 3, flag = "0")
    ans[c("point_id", setdiff(names(ans), "point_id"))] 
  
}
}

8. Essai de la nouvelle fonction sur UN SEUL fichier, en l'occurence celui qui n'existe plus. Notez l'absence d'erreur et aussi l'absence de résultat.

Code : Tout sélectionner

lire_point_ecoute(= 1, path = file.path(tempdir(), "SDOA_nuit"))

9. Utilisation de la fonction dans la boucle lapply()

Code : Tout sélectionner

list_df_from_csv <- lapply( #  read all files using the integer id
  X = 1:5, 
  FUN 
= lire_point_ecoute, path = file.path(tempdir(), "SDOA_nuit")
)
str(list_df_from_csv, 1)
#> List of 5
#>  $ : NULL
#>  $ :'data.frame':    20 obs. of  5 variables:
#>  $ :'data.frame':    20 obs. of  5 variables:
#>  $ :'data.frame':    20 obs. of  5 variables:
#>  $ :'data.frame':    20 obs. of  5 variables:

ecoutes <- do.call("rbind", list_df_from_csv)
str(ecoutes)
#> 'data.frame':    80 obs. of  5 variables:
#>  $ point_id: chr  "002" "002" "002" "002" ...
#>  $ X       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ extra   : num  0.7 -1.6 -0.2 -1.2 -0.1 3.4 3.7 0.8 0 2 ...
#>  $ group   : int  1 1 1 1 1 1 1 1 1 1 ...
#>  $ ID      : int  1 2 3 4 5 6 7 8 9 10 ...    
Mickaël
mickael.canouil.fr | rlille.fr

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

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Pierre-Yves Berrard » 13 Nov 2020, 14:00

Loic SALAUN 2 a écrit :Do.call j’avoue avoir plus de mal à saisir le sens exact. Si j’ai bien compris cela applique Rbind (donc ajout de ligne) à ma liste. Mais en effet cela me paraît un pas très limpide…

do.call() sert à passer une list()e d'arguments à une fonction censée prendre un nombre indéterminé d'arguments (`...`).

Par exemple :

Code : Tout sélectionner

rbind(df1, df2, df3)
est équivalent à

Code : Tout sélectionner

do.call(rbind, list(df1, df2, df3))

Ça marche pour toute fonction ayant `...` comme argument attendu.
PY

Loic SALAUN 2
Messages : 17
Enregistré le : 15 Oct 2020, 08:22

Re: Ajout de colonne en fonction du nom du CSV

Messagepar Loic SALAUN 2 » 13 Nov 2020, 14:33

Merci à tous... Et un grand merci à Mickael pour ses explications et sa patience. Je regarderai tout cela à tête reposée (j'ai passé une bonne partie de l'aprèm à lire un doc sur les listes sous R... donc je commence à saturer ;) ).
Encore merci,


Retourner vers « Questions en cours »

Qui est en ligne

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