Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

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

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Jean-Emmanuel Longueville » 22 Avr 2020, 08:50

Bonjour,
Avec certain appareil nous obtenons une hierarchie de fichier ainsi :
Des fichiers qui contiennent nos données pour un individus (Un fichier par individus) (oui les données sont organisées sur une seule colonne)
Ces fichiers sont dans un dossier 1 dossier par session (jour d'expérimentation) qui sont eux même mis dans un dossier d'expérience.
J'ai donc écrit les trois fonctions suivante :


  • Lecture du fichier d'un individus

    Code : Tout sélectionner


    get_subjects_data 
    = function(path_file, prog){
      #Découpage du path_file pour obtenir les informations qui y sont contenue :
      filname <- tail( unlist(stringr::str_split(path_file, "/")),1)
      date    <- stringr::str_split(filname, "_")[[1]][1]
      # Cette fonction lit un fichier csv et en modifie le contenu en fonction de l'expérience
      dataframe   <- read.csv2(path_file, header = FALSE, stringsAsFactors = FALSE)

      #We read the information of shock power
      shock_value <- dataframe[c(46:66),1]

      #We get and organize results
      tmp <- stringr::str_split(as.character(dataframe[c(121:nrow(dataframe)),1]),"\\.", simplify = TRUE)
      if (ncol(tmp) == 1) { tmp <- cbind(tmp[,1], "0") }
      res <- data.frame(Times = tmp[,1], shock_level = tmp[,2], stringsAsFactors = FALSE)
      res[which(res$shock_level == ""),"shock_level"] = 0

      res 
    <- dplyr::mutate(res, date = date,
                           subject = dataframe[7,1],
                           Times = as.numeric(as.character(Times))/10,
                           shock_level = as.character(shock_level),
                           shock_level = dplyr::if_else(shock_level == "1" | shock_level == "2", as.character(as.numeric(shock_level)*10), shock_level),
                           shock_level = as.numeric(shock_level),
                           shock_val   = as.numeric(shock_value[(shock_level + 1)]),
                           latency     = compute_latency(Times),
                           program     = prog#,
     #                      debug       = path_file
      )
      return(res)
    }
     

  • Lecture du dossier de session

    Code : Tout sélectionner


    read_session 
    <- function(pathdir){
      listefile <- dir(path = pathdir)
      info <- tail( unlist(stringr::str_split(pathdir, "/")),1)
      prog <- stringr::str_extract(info, "[tT|sS].*$")
      dfoutput <- NULL
      for 
    (i in listefile) {
        print(i)
        dfi <- get_subjects_data( normalizePath(stringr::str_c(pathdir,i, sep = "/")),
                                       prog =  prog)
        dfoutput <- dplyr::bind_rows(dfoutput, dfi)
      }
      return(dfoutput)
    }

  • Lecture du dossier d'expérimentation

    Code : Tout sélectionner

    compile_experiment <- function(pathdir){
      listesession <- list.dirs(path = pathdir, recursive = FALSE)
      output <- NULL
      
    # dfoutput <- sapply(listesession, FUN = function(x){
      #   dfi <- read_session(x)
      #   dfoutput <- dplyr::bind_rows(dfoutput, dfi)
      # })
      for (i in listesession) {
        print(i)
        dfi <- read_session(i)
        output <- dplyr::bind_rows(output, dfi)
      }
      return(output)
    }

Ma question c'est pour vous quels sont les points d'optimisation possible je présume dans un premier temps remplacer les boucles for par des apply ? mais comme vous pouvez le voir j'ai fait une tentative sans succès.
Merci
Jean-Emmanuel
Ingénieur d'étude LNEC

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

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Logez Maxime » 22 Avr 2020, 09:09

Bonjour,

La première question serait qu'est-ce qui prend le plus de temps dans ton code ?
Si tu passes ton temps à optimiser une partie du code qui ne prend elle même pas de temps alors ça ne serait pas très efficace.
Il te faut faire du profiling.
Pour ça tu peux regarder du côté de la fonction Rprof qui te donnera des indications.
Ensuite tu peux jeter un oeil ici :
https://rstudio.github.io/profvis/
ou encore là :
https://support.rstudio.com/hc/en-us/articles/218221837-Profiling-with-RStudio

Après la difficulté, c'est que le temps d’exécution dépend aussi des objets sur lesquels tu travailles. Deux fonctions peuvent avoir des temps d'execution similaire sur un objet de petite taille et avoir des temps d’exécution très différents sur des objets plus grands.

Pour comparer deux façons de faire, j'utilise la fonction microbenchmark du package éponyme, pour comparer des codes deux à deux, voir plus.

Cordialement,
Maxime

Eric Casellas
Messages : 767
Enregistré le : 06 Jan 2009, 14:59

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Eric Casellas » 23 Avr 2020, 08:10

Salut,

Moi je me pose la question de qu'est-ce que tu veux optimiser? le temps d’exécution, l'utilisation de la mémoire, autre chose?

Sinon il y a un paquet readr qui peut remplacer les fonction de lecture de fichiers de base et un paquet data.table qui peut remplacer les data.frame...

Eric
Eric

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Jean-Emmanuel Longueville » 23 Avr 2020, 09:08

Merci pour vos réponses.

Oui je cherche à optimiser le temps de calcul parce que sur ma machine qui est un équivalent machine gamer cela prends un temps insignifiant mais sur celle de mon supérieur (plus machine de bureau ++) ça prends un temps bien plus long.
Jean-Emmanuel
Ingénieur d'étude LNEC

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

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Logez Maxime » 23 Avr 2020, 09:29

Bonjour,

Combien de fichiers tu charges et quel est leur taille (nombre de lignes, colonnes, ...) ?

Cordialement,
Maxime

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Jean-Emmanuel Longueville » 23 Avr 2020, 11:52

Je charge actuellement 80 session de 24 individus avec un nombre de ligne variable entre 200 et 300 ligne avec une seule colonne.
Soit 1920 fichier de 1 colonne et entre 200 et je pense grand max 500 lignes. Autrement dit ce ne sont pas de gros fichier.
Jean-Emmanuel
Ingénieur d'étude LNEC

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

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Mickael Canouil » 23 Avr 2020, 12:08

Bonjour,

au vu des manipulations effectué, une première grosse optimisation serait de remplacer dplyr (mutate, etc.) et l'utilisation des data.frame par data.table.

Sinon, j'utiliserai plus un format "vectoriel" pour la lecture pour éviter les affectations de la boucle for, soit avec purrr soit base.
Et je m'epargnerai l'affectation à un "output" en exploitant le fait que la "dernière ligne" est renvoyée.

Code : Tout sélectionner

read_session <- function(pathdir){
  listefile <- list.files(path = pathdir) # more eplicit than dir
  info <- basename(pathdir)
  prog <- stringr::str_extract(info, "[tT|sS].*$")
 
  purrr
::map_df(
    .= listefile, 
    
.= ~ get_subjects_data(normalizePath(file.path(pathdir, .x)), prog =  prog)
  )
  # do.call("rbind", lapply(listefile, function(x) get_subjects_data(normalizePath(file.path(pathdir, .x)), prog = prog)))
}

Code : Tout sélectionner

compile_experiment <- function(pathdir){
  purrr::map_df(list.dirs(path = pathdir, recursive = FALSE), read_session)
  # do.call("rbind", lapply(list.dirs(path = pathdir, recursive = FALSE), read_session))
}


EDIT:

Code : Tout sélectionner

get_subjects_data <- function(path_file, prog) {
  date <- gsub("([^_]*)_.*", "\\1", basename(path_file))
  dataframe <- data.table::fread(path_file, header = FALSE, sep = ";")

  shock_value <- dataframe[c(46:66), 1]

  tmp <- stringr::str_split(as.character(dataframe[121:nrow(dataframe), 1]), "\\.", simplify = TRUE) # seems complicated
  if (ncol(tmp) == 1) { 
    res 
<- data.table::data.table(Times = as.numeric(tmp[, 1]) / 10, shock_level = 0)
  } else {
    res <- data.table::data.table(Times = as.numeric(tmp[, 1]) / 10, shock_level = as.numeric(tmp[, 2]))
  }
  res[shock_level == "", shock_level := 0]
  res[shock_level > 0, shock_level := shock_level * 10]
  res[, subject := dataframe[7, 1]]
  res[, shock_val := shock_value[shock_level + 1]]
  res[, latency := compute_latency(Times)]
  res[, program := prog]
  # res[, debug := path_file]

  res
}


Code non testé et non écrit dans R. Il y a donc probablement des erreurs de syntaxe ou de noms d'arguments.
SI le code fonctionne, un petit benchmark (benchr::benchmark).

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

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Jean-Emmanuel Longueville » 23 Avr 2020, 13:34

Merci Mickael je vais tester ça

Edit l'opérateur := vient de quel paquet ? merci
Jean-Emmanuel
Ingénieur d'étude LNEC

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Jean-Emmanuel Longueville » 23 Avr 2020, 23:37

@Mickael
Je me suis permis de reprendre la fonction get_subject_data ainsi :
[code= php]

get_subjects_data2 <- function(path_file, prog) {
date <- gsub("([^_]*)_.*", "\\1", basename(path_file))
dataframe <- data.table::fread(path_file, header = FALSE, sep = ";")

shock_value <- dataframe[c(46:66), 1]

res <- data.table::data.table(BaseValue = dataframe[121:nrow(dataframe),] ,
Times = trunc(dataframe[121:nrow(dataframe),]) )
names(res) <- c( "BaseValue", "Times")
res[, shock_level := (BaseValue - Times * 100) ] #BUG why ??
res$shock_level <- (res$BaseValue - res$Times)*100 #This is correct
res[, subject := dataframe[7, 1]]
res$shock_val <- shock_value[res$shock_level + 1]
res[, latency := compute_latency(Times)]
res[, program := prog]
res[, to_resp := dataframe[94,1]]
# res[, debug := path_file]

return(res)
}[/code]
Juste des comportements des data.table que je ne comprends pas pourquoi à la création il ajoute le nom de l'ancienne colonne d'où la nécessité de la ligne names.
Les assignations peuvent elle s'écrirent différemment ?
Merci les benchmarks arrivent
Jean-Emmanuel
Ingénieur d'étude LNEC

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

Re: Optimisation d'un code qui lit des fichiers organisé en dossier avec trois niveau de parcours.

Messagepar Mickael Canouil » 24 Avr 2020, 08:30

L'opérateur := est propre à data.table dans le cas présent (il existe aussi dans dplyr/rlang).

Mon écriture de data.table est non testé (d'où les erreurs probablement simple à corriger) et j'ai commencé relativement récemment à l'utiliser pour des fichiers de l'ordre du million à milliard de lignes.
Je ne suis donc pas un expert en data.table, mais le site internet contient plusieurs vignettes et exemples.
Mickaël
mickael.canouil.fr | rlille.fr


Retourner vers « Questions en cours »

Qui est en ligne

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