Valid XHTML     Valid CSS2    

Logiciel R, séances de perfectionnement

    en 4 demi-journées

Séance 4 : Exports, interaction utilisateur, programmation et automatisation

                     gilles.hunault "at" univ-angers.fr

 

Table des matières cliquable

  1. Notion de script R

  2. Définition des fonctions en R

  3. Interaction utilisateur et gestion de liste de fichiers

  4. Références bibliographiques pour l'ensemble des séances

 

Il est possible d'afficher toutes les solutions via ?solutions=1 et de toutes les masquer avec via ?solutions=0.

 

1. Notion de script R

Qu'est-ce qu'un script R ? Est-ce forcément un programme ? Est-ce forcément une fonction ?

Commenter le script suivant :


     data   <- read.xls("essai.xls")
     age    <- na.omit(data[,"AGE"])
     moyAge <- moy(age)
     

Est-ce un "bon" script ? Comment l'améliorer ? Peut-on en faire une fonction ? Quels en seraient les paramètres ?

Solution :  

Un script est en quelque sorte un programme puisque c'est un ensemble d'instructions que R exécute dans l'ordre des lignes. Ce n'est pas forcément une fonction, mais cela peut contenir des définitions et des appels de fonctions. Le script proposé ne définit aucune fonction.

Ce script vient lire un fichier Excel nommé essai.xls et calcule la moyenne de la colonne nommée AGE après avoir enlevé les valeurs manquantes dans cette colonne AGE.

Est-ce un "bon" script ? Oui, presque s'il s'agit juste de caculer la moyenne de l'age. Mais il y avait plus court, si c'était juste un calcul "à la volée" :


     mean( read.xls("essai.xls")$AGE, na.rm=TRUE )
     

Le "presque" vient de l'absence de l'instruction library(gdata) sans laquelle le script ne fonctionne pas car, en standard, le package gdata n'est pas chargé.

Un "vrai" script comporte en général des commentaires, un peu d'explications pour dire ce qu'on fait, qui l'a fait. On essaie en général de grouper les noms de fichiers, de colonnes en début de script, de façon à pouvoir facilement s'en servir sur d'autres données, comme par exemple :


     #  calcul de moyenne sur des données Excel avec éventuellement des données manquantes
     #  (gH) Novembre 2014
     
     # on utilise le package gdata pour lire les données
     
     library(gdata)
     
     # paramètres : nom du fichier et nom de la colonne
     
     nomFic <- "essai.xls"
     nomCol <- "AGE"
     
     # lecture des données et élimination des données manquantes
     
     data    <- read.xls(nomFic)
     colData <- na.omit(data[,nomCol])
     
     # calcul de la moyenne
     
     moyData <- moy(colData)
     

On pourrait améliorer le script en le transformant en une fonction qui prend comme paramètres le nom du fichier et le nom de la colonne à traiter, soit :


     #####################################################################################     
          
     moySansNA <- function(nomFic="",nomCol) {     
          
     #####################################################################################     
          
     #  calcul de moyenne sur des données Excel avec éventuellement des données manquantes     
     #  (gH) Novembre 2014     
          
     if (missing(nomFic) | (nomFic=="")) {     
       cat(" moySansNA : calcul de moyenne sur des données Excel \n")     
       cat("             avec éventuellement des données manquantes\n\n")     
       cat("syntaxe  : moySansNA(nomFic,nomCol)\n")     
       cat('exemple  : moySansNA("essai.cls","AGE")\n')     
       cat("remarque : ne fonctionne qu'avec une seule colonne à la fois.\n")     
       return(invisible(NULL))     
     } # fin si     
          
     # on utilise le package gdata pour lire les données     
          
     library(gdata)     
          
     # on teste si le fichier existe     
          
     if (!file.exists(nomFic)) {     
       cat("désolé, le fichier ",nomFic," n'existe pas.\n")     
       stop("fin de fonction moySansNA\n\n")     
     } # fin si     
          
     # lecture des données et élimination des données manquantes     
          
     data   <- read.xls(nomFic)     
     vals   <- na.omit(data[,nomCol])     
          
     # calcul de la moyenne     
          
     moyVals <- moy(vals)     
          
     return(invisible(moyVals))     
          
     } # fin de la fonction moySansNA     
          

Voici des exemples d'utilisation :


     > moySansNA()     
          
      moySansNA : calcul de moyenne sur des données Excel     
                  avec éventuellement des données manquantes     
          
     syntaxe  : moySansNA(nomFic,nomCol)     
     exemple  : moySansNA("essai.cls","AGE")     
     remarque : ne fonctionne qu'avec une seule colonne à la fois.     
          
     > moySansNA("essay.xls","AGE")     
          
      désolé, le fichier  essay.xls  n'existe pas.     
      Erreur dans moySansNA("essay.xls", "AGE") : fin de fonction moySansNA     
          
          
     > moySansNA("essai.xls","AGE")     
          
     > m <- moySansNA("essai.xls","AGE")     
          
     > print(m)     
     33.7013     
          

 

2. Définition des fonctions en R

Application : écrire une fonction qui renvoie le nombre de valeurs NA présents dans un vecteur. Comment l'appliquer à chacune des colonnes d'une matrice ou d'un data frame ?

Compléter en renvoyant ensuite à la fois le nombre de valeurs NA et le pourcentage correspondant. Appliquer également à chacune des colonnes d'une matrice ou d'un data frame.

On pourra utiliser le fichier Excel essai.xls pour tester les fonctions.

Solution :  

Pour calculer le nombre de NA dans un vecteur V on peut se contenter de l'instruction : sum(is.na(V)). Puisque les fonctions utilisées dans ce calcul, à savoir sum() et is.na() sont vectorielles, on peut utiliser apply() pour l'appliquer aux colonnes d'une structure. Voici la fonction associée et son utilisation (fichier nbnav1.r)  :


     nbNA <- function(v) { return(sum(is.na(v))) } # nombre de NA dans v     
          
     print( nbNA( c(1,3,8,NA,2,NA,5) ) )     
          
     library(gdata)     
     age <- read.xls("essai.xls")$AGE     
          
     print( nbNA( age ))     
          
     data <- read.xls("essai.xls")     
          
     print( apply(X=data,F=nbNA,M=2) )     
          
          

     [1] 2     
     [1] 3     
       IDEN    AGE TAILLE  POIDS  POULS    SYS    DIA   CHOL     
          0      3      0      0      1      1      1      0     
          

Renvoyer le nombre de NA et le pourcentage correspondant demande un plus gros effort de rédaction (fichier nbnav2.r)  :


     #####################################################################################     
          
     nbNApct <- function(v) {     
          
     #####################################################################################     
          
     # calcule du nombre de données NA dans un vecteur et calcul du  pourcentage correspondant     
     # (on ne teste pas si le vecteur est vide)     
          
     nbna <- sum(is.na(v))     
     pct  <- 100*nbna/length(v)     
          
     # on pourrait utiliser une liste avec     
     #    return(list(nbNA=nbna,pctNA=pct))     
     # mais un vecteur nommé est "mieux en retour"     
          
     vres <- c(nbna,pct)     
     names(vres) <- c("nbNA","pctNA")     
          
     return(vres)     
          
     } # fin de fonction nbNApct     
          
     #####################################################################################     
     #     
     # exemples d'utilisation :     
     #     
     #####################################################################################     
          
     cat(" pour un vecteur quelconque : \n")     
     print( nbNApct( c(1,3,8,NA,2,NA,5) ) )     
          
     cat(" pour age : \n")     
     library(gdata)     
     age <- read.xls("essai.xls")$AGE     
     print( nbNApct( age ))     
          
     cat(" pour toutes les colonnes d'un data frame\n")     
     data <- read.xls("essai.xls")     
     print( apply(X=data,F=nbNApct,M=2) )     
          
     # et avec une matrice :     
          
     md      <- matrix(round(100*runif(15)), nrow=5,ncol=3)     
     md[4,2] <- md[1,2] <- NA     
     print(md)     
     print( apply(X=md,F=nbNApct,M=2) )     
          
          

      pour un vecteur quelconque :     
         nbNA    pctNA     
      2.00000 28.57143     
          
      pour age :     
      nbNA pctNA     
      3.00  3.75     
          
      pour toutes les colonnes d'un data frame     
           IDEN  AGE TAILLE POIDS POULS  SYS  DIA CHOL     
     nbNA     0 3.00      0     0  1.00 1.00 1.00    0     
     pctNA    0 3.75      0     0  1.25 1.25 1.25    0     
          
          [,1] [,2] [,3]     
     [1,]   63   NA   88     
     [2,]   80   53   25     
     [3,]   42   48   83     
     [4,]   59   NA   73     
     [5,]   20   30   54     
          
           [,1] [,2] [,3]     
     nbNA     0    2    0     
     pctNA    0   40    0     
          

On a fait le choix ici d'utiliser un vecteur pour renvoyer les résultats. Il est classique d'utiliser plutôt une liste, mais cela «gâche» un peu les sorties directes de résultats sur écran avec apply() et il est conseillé d'utiliser alors sapply(). Attention : sapply() ne fonctionne bien qu'avec des listes -- donc avec des data frames -- en entrée mais pas avec des matrices (fichier nbnav3.r)  :


     #####################################################################################     
          
     nbNApct <- function(v) { # VERSION AVEC LISTE EN RETOUR !     
          
     #####################################################################################     
          
     # calcule du nombre de données NA dans un vecteur et calcul du  pourcentage correspondant     
     # (on ne teste pas si le vecteur est vide)     
          
     nbna <- sum(is.na(v))     
     pct  <- 100*nbna/length(v)     
          
     return(list(nbNA=nbna,pctNA=pct))     
          
     } # fin de fonction nbNApct     
          
     #####################################################################################     
          
     cat(" pour un vecteur quelconque : \n")     
     print( nbNApct( c(1,3,8,NA,2,NA,5) ) )     
          
     cat(" pour age : \n")     
     library(gdata)     
     age <- read.xls("essai.xls")$AGE     
     print( nbNApct( age ))     
          
     cat(" pour toutes les colonnes\n")     
     data <- read.xls("essai.xls")     
     print( apply(X=data,F=nbNApct,M=2) )     
          
     cat(" pour toutes les colonnes avec sapply :\n")     
     data <- read.xls("essai.xls")     
     print( sapply(X=data,F=nbNApct) )     
          
     # cela ne marche pas très bien avec une matrice     
          
     md <- matrix(round(100*runif(15)), nrow=5,ncol=3)     
     md[4,2] <- md[1,2] <- NA     
     print(md)     
     print( sapply(X=md,F=nbNApct) )     
          
          

      pour un vecteur quelconque :     
     $nbNA     
     [1] 2     
          
     $pctNA     
     [1] 28.57143     
          
      pour age :     
     $nbNA     
     [1] 3     
          
     $pctNA     
     [1] 3.75     
          
      pour toutes les colonnes     
     $IDEN     
     $IDEN$nbNA     
     [1] 0     
          
     $IDEN$pctNA     
     [1] 0     
          
          
     $AGE     
     $AGE$nbNA     
     [1] 3     
          
     $AGE$pctNA     
     [1] 3.75     
          
          
     $TAILLE     
     $TAILLE$nbNA     
     [1] 0     
          
     $TAILLE$pctNA     
     [1] 0     
          
          
     $POIDS     
     $POIDS$nbNA     
     [1] 0     
          
     $POIDS$pctNA     
     [1] 0     
          
          
     $POULS     
     $POULS$nbNA     
     [1] 1     
          
     $POULS$pctNA     
     [1] 1.25     
          
          
     $SYS     
     $SYS$nbNA     
     [1] 1     
          
     $SYS$pctNA     
     [1] 1.25     
          
          
     $DIA     
     $DIA$nbNA     
     [1] 1     
          
     $DIA$pctNA     
     [1] 1.25     
          
          
     $CHOL     
     $CHOL$nbNA     
     [1] 0     
          
     $CHOL$pctNA     
     [1] 0     
          
          
      pour toutes les colonnes avec sapply :     
           IDEN AGE  TAILLE POIDS POULS SYS  DIA  CHOL     
     nbNA  0    3    0      0     1     1    1    0     
     pctNA 0    3.75 0      0     1.25  1.25 1.25 0     
          
          [,1] [,2] [,3]     
     [1,]   43   NA   86     
     [2,]   92   77   80     
     [3,]   78   14   55     
     [4,]   50   NA   77     
     [5,]    1   33   65     
          
           [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15]     
     nbNA  0    0    0    0    0    1    0    0    1    0     0     0     0     0     0     
     pctNA 0    0    0    0    0    100  0    0    100  0     0     0     0     0     0     
          

 

3. Interaction utilisateur et gestion de liste de fichiers

Ecrire une fonction qui affiche le nombre de valeurs manquantes des colonnes d'un ou plusieurs fichiers Excel passés en paramètre. Si aucun paramètre, la fonction doit afficher le gestionnaire de fichiers pour qu'on puisse sélectionner le fichier. On pourra utiliser les fichier Excel essai.xls et war2.xls pour tester la fonction.

Solution :  

Un examen détaillé de la solution ci-dessous montre que nous avons utilisé une fonction anonyme pour calculer le nombre de NA et que le parcours de vecteurs de chaines de caractères est d'une grande simplicité en R (fichier nbnav4.r)  :


     #######################################################################     
          
     nbna <- function(ldf) { # ldf : liste de fichiers     
          
     #######################################################################     
          
     library(gdata)     
          
     # si pas de paramètre, on utilise le gestionnaire de fichiers     
     # pour sélectionner les fichiers à utiliser     
          
     if (missing(ldf)) { ldf <- file.choose() }     
          
     # pour chacun des fichiers présents dans ld, on affiche le nombre de NA par colonne     
          
     for (fic in ldf) {     
       cat("Fichier : ",fic,"\n")     
       dat <- read.xls(fic)     
       print( apply(X=dat,F=function(x) { return(sum(is.na(x))) },M=2) )     
     } # fin de boucle pour sur fic     
          
     } # fin de fonction nbna     
          
     # exemples d'utilisation :     
          
     nbna("essai.xls")     
     liste <- c("essai.xls","war2.xls")     
     nbna(liste)     
          
     # not run (comme ils disent) :     
     #  nbna()     
          

     Fichier :  essai.xls     
       IDEN    AGE TAILLE  POIDS  POULS    SYS    DIA   CHOL     
          0      3      0      0      1      1      1      0     
          
     Fichier :  essai.xls     
       IDEN    AGE TAILLE  POIDS  POULS    SYS    DIA   CHOL     
          0      3      0      0      1      1      1      0     
          
     Fichier :  war2.xls     
       CODE LnYCAP POLITY   INEQ   MANU    MCP REGPOL REGOIL REGREF     
          0      0      0      0      0      0      0      0      0     
          

Il aurait été possible d'écrire une solution plus fonctionnelle et sans boucle explicite avec une fonction locale (traiteFic) comme (fichier nbnav5.r)  :


     #######################################################################     
          
     nbna <- function(ldf) { # ldf : liste de fichiers     
          
     #######################################################################     
          
     library(gdata)     
          
     traiteFic <- function(nomFic) {     
        cat("Fichier : ",nomFic,"\n")     
        dat <- read.xls(nomFic)     
        print( apply(X=dat,F=function(x) { return(sum(is.na(x))) },M=2) )     
        return(invisible(NULL))     
     } # fin de fonction traiteFic     
          
     # si pas de paramètre, on utilise le gestionnaire de fichiers     
     # pour sélectionner les fichiers à utiliser     
          
     if (missing(ldf)) { ldf <- file.choose() }     
          
     # pour chacun des fichiers présents dans ld, on affiche le nombre de NA par colonne     
          
     sapply(X=ldf,F=traiteFic)     
          
     } # fin de fonction nbna     
          
     # exemples d'utilisation :     
          
     res <- nbna("essai.xls")     
     liste <- c("essai.xls","war2.xls")     
     res <- nbna(liste)     
          
     # not run (comme ils disent) :     
     #  nbna()     
          

     Fichier :  essai.xls     
       IDEN    AGE TAILLE  POIDS  POULS    SYS    DIA   CHOL     
          0      3      0      0      1      1      1      0     
     Fichier :  essai.xls     
       IDEN    AGE TAILLE  POIDS  POULS    SYS    DIA   CHOL     
          0      3      0      0      1      1      1      0     
     Fichier :  war2.xls     
       CODE LnYCAP POLITY   INEQ   MANU    MCP REGPOL REGOIL REGREF     
          0      0      0      0      0      0      0      0      0     
          

Mais un "puriste" aurait sans doute préféré la solution suivante sans fonction interne explicite (fichier nbnav6.r)  :


     #######################################################################     
          
     nbna <- function(ldf) { # ldf : liste de fichiers     
          
     #######################################################################     
          
     library(gdata)     
          
     # si pas de paramètre, on utilise le gestionnaire de fichiers     
     # pour sélectionner les fichiers à utiliser     
          
     if (missing(ldf)) { ldf <- file.choose() }     
          
     # pour chacun des fichiers présents dans ld, on affiche le nombre de NA par colonne     
          
     sapply(     
      X=ldf,     
      F=function(nomFic) {     
        cat("Fichier : ",nomFic,"\n")     
        dat <- read.xls(nomFic)     
        print( apply(X=dat,F=function(x) { return(sum(is.na(x))) },M=2) )     
        return(invisible(NULL)) }     
     ) # fin de sapply     
          
     } # fin de fonction nbna     
          
     # exemples d'utilisation :     
          
     res <- nbna("essai.xls")     
     liste <- c("essai.xls","war2.xls")     
     res <- nbna(liste)     
          
     # not run (comme ils disent) :     
     #  nbna()     
          

     Fichier :  essai.xls     
       IDEN    AGE TAILLE  POIDS  POULS    SYS    DIA   CHOL     
          0      3      0      0      1      1      1      0     
     Fichier :  essai.xls     
       IDEN    AGE TAILLE  POIDS  POULS    SYS    DIA   CHOL     
          0      3      0      0      1      1      1      0     
     Fichier :  war2.xls     
       CODE LnYCAP POLITY   INEQ   MANU    MCP REGPOL REGOIL REGREF     
          0      0      0      0      0      0      0      0      0     
          

 

4. Références bibliographiques pour l'ensemble des séances

Que lire pour approfondir tout cela ?

Solution :  

Plein de choses !

Il faut d'abord choisir si on veut approfondir le langage R, les statistiques ou la programmation. Ensuite, il n'y a plus que l'embarras du choix.

Voir par exemple ma question sur les livres pour R et sa réponse associée dans mes pages de programmation R avancée.

 

 

Code-source php de cette page. Retour à la page principale du cours.

 

 

retour gH    Retour à la page principale de   (gH)