Valid XHTML     Valid CSS2    

 

Programmer en R

Session de formation, décembre 2013

gilles.hunault "at" univ-angers.fr

                    

 

Solutions pour la séance numéro 1 (énoncés)

  1. Programmer en R, ce n'est pas, comme en C, C++ ou en Java, écrire un programme principal, mais plutôt écrire des fonctions, c'est-à-dire des sous-programmes. Au lieu d'un programme, on utilise en général un code-source qui réalise des appels à des fonctions. Il n'y a donc pas au départ de fonction plus générale ou plus importante qu'une autre, ni de compilation à effectuer.

    Contrairement à C, C++, Java, mais comme Perl, Php, Ruby, Python, R est non typé explicitement et ne se compile pas. On peut donc changer de type (numérique, caractère...) en cours de route, on n'a pas besoin de déclarer les variables qu'on veut utiliser, ce qui peut être source d'erreurs. La notion d'environnement (session, variables...) est particulière à R. C'est ce qui permet de l'utiliser en interactif, dans une interface comme Rstudio ou Rgui, ou dans une sur-interface comme Rcmdr ou Rkward.

    Programmer en R peut servir à automatiser une série de calculs ou de transformations, à interagir avec l'utilisateur. On doit programmer dès qu'on a affaire à des actions répétitives ou longues à saisir au clavier. Tout est programmable : les calculs, les graphiques, les affichages, les opérations sur fichiers, l'interaction avec l'utilisateur...

    On peut en R redéfinir une fonction déja définie (contrairement à Php), comme en Ruby et Python, et on peut même -- ce qui peut se révéler très dangereux -- redéfinir les fonctions primitives.

    On dit que tout est fonctionnellement objet en R, y compris les fonctions parce que tout est implémenté et géré de la même façon : les données comme les fonctions, avec une syntaxe objet (ou "notation") fonctionnelle :

    
         # tout est objet, même les fonctions     
         # et tout s'écrit de la même façon     
              
         x <- 1     
         print( x )     
         y <- x     
         print( class(y) )     
              
         ca <- function(x) { return(x*x) }     
         print( ca )     
         cb <- ca     
         print( class(cb) )     
              
    

    Les notations particulières pour l'affectation et l'indexation sont une facilité d'écriture alors qu'il existe une «vraie» notation fonctionnelle associée :

    
         # en R, l'affectation est une fonction     
              
         "<-"(x,1) # équivalent à x <- 1     
              
         # une autre écriture :     
              
         assign("x",1)     
              
         # pour mémoire, l'écriture "au bout et vers la droite" est aussi autorisée     
              
         1 -> x     
              
         # l'indexation est aussi fonction     
              
         print( "["(letters,3) ) # équivalent à print( letters[3] )     
              
    

    La plus grande différence avec les autres langages de programmation est que tous les indices des structures commencent à 1.

  2. Pour définir une fonction nommée, il faut réaliser une affectation avec comme partie gauche le nom de la fonction à créer, puis utiliser le mot function, mettre des parenthèses, ajouter des paramètres s'il y en a besoin avec éventuellement des valeurs par défaut, puis écrire le corps de la fonction entre accolades et mettre return avec des parenthèses pour indiquer l'objet renvoyé, qui peut être une simple valeur ou quelque chose de plus complexe. Par exemple, pour le calcul du carré, on devrait écrire

    
         carre <- function(x) { return(x*x) }     
              
    

    Il est possible de raccourcir le corps de la fonction en omettant les accolades et en mettant comme dernière (ou comme seule) instruction le calcul à renvoyer. Ainsi, on pourrait écrire

    
         carre <- function(x) x*x     
              
    

    Une écriture aussi concise est utile dans le cas d'une fonction intégrée à un appel de fonction, mais cette écriture est dangereuse et «fainéante». Il vaut mieux lui préférer le code suivant :

    
         carre <- function(x) {     
              
          # calcule le carré de son argument     
              
          y <- x*x     
          return(y)     
              
         } # fin de fonction carre     
              
    

    Nous reviendrons sur ces écritures, mais un grand principe de la programmation robuste est qu'il faut préférer la lisibilité, la facilité de relecture et la maintenance du code à la facilité d'écriture. Il faut viser l'écriture de programmes robustes et lisibles et non pas de programmes vite tapés sur le clavier.

    Pour se servir de cette fonction carre() il suffit de l'appeler là où on utiliserait le carré des valeurs. Cela peut donc être dans le cadre d'un calcul simple ou d'un affichage interactif (en console), dans le cadre d'une instruction d'affectation. Il n'est pas obligatoire ici de nommer le paramètre (mais il vaudra mieux le faire quand il y en aura plus d'un). On peut aussi utiliser juste le nom de la fonction en tant que paramètre quand cela est permis. Le calcul effectué par notre fonction étant "vectoriel", c'est-à-dire applicable à une structure, notre fonction est elle-même vectorielle. Voici des exemples d'utilisation :

    
         ## définition de la fonction     
              
          carre <- function(x) {     
              
          # calcule le carré de son argument     
              
          y <- x*x     
              
          return(y)     
              
         } # fin de fonction carre     
              
         # ------------------------------------------------------     
              
         # calcul interactif     
              
          carre(5)  # suffisant, mais carre(x=5) est OK aussi     
              
          # affichage en interactif     
              
          cat("le carré de 8 est ",sprintf("%05d",carre(x=8)),"\n")  # noter %05d au lieu de %5d     
              
          # à l'intérieur d'une affectation     
              
          lesCarresPlusUn <- 1 + carre(1:10)     
              
          # en tant que paramètre     
              
          lapply(X=list(a=1,b=3,c=5),FUN=carre)     
              
    

    Et leurs résultats :

    
         [1] 25     
              
         le carré de 8 est  00064     
              
         # non affiché : [1]   2   5  10  17  26  37  50  65  82 101     
              
         $a     
         [1] 1     
              
         $b     
         [1] 9     
              
         $c     
         [1] 25     
              
              
    

    Pour définir la fonction pvalt() qui doit renvoyer la p-value du test t, il faut commencer par comprendre l'objet renvoyé par la fonction t.test(), puis trouver comment extraire la composante voulue pour ensuite écrire la fonction et enfin vérifier qu'elle renvoie bien ce qu'on voulait :

    
          # toutes les données iris     
              
          data(iris) # chargement "fainéant" (lazy loading) du data.frame iris     
              
          # juste les deux premières colonnes et les dix premières lignes     
              
          lesd <- iris[ (1:10) , (1:2) ] # plus "propre" que iris[1:10,1:2]     
              
          # effectuons le test et essayons de comprendre où est mémorisée     
          # la p-value     
              
          tt <- t.test(lesd)     
              
          print(tt)  # la p-value est donc  8.008e-15     
              
          # quelques essais pour comprendre ce que renvoie la fonction t.test()     
              
          print(class(tt))     
          print(is.list(tt))     
          print(names(tt))     
          print(tt$p.value) # c'est bien cela     
              
          # définissons rapidement la fonction demandée     
              
          pvalt <- function(test) { return( test$p.value ) }     
              
          # vérifions que c'est bien ce qu'il faut :     
              
          identical( tt$p.value, pvalt(tt) ) # doit renvoyer TRUE     
              
    
    
              
         >  # toutes les données iris     
         >     
         >  data(iris) # chargement "fainéant" (lazy loading) du data.frame iris     
              
         >  # juste les deux premières colonnes et les dix premières lignes     
         >     
         >  lesd <- iris[ (1:10) , (1:2) ] # plus "propre" que iris[1:10,1:2]     
              
         >  # effectuons le test et essayons de comprendre où est mémorisée     
         >  # la p-value     
         >     
         >  tt <- t.test(lesd)     
              
         >  print(tt)  # la p-value est donc  8.008e-15     
              
         	One Sample t-test     
              
         data:  lesd     
         t = 21.5729, df = 19, p-value = 8.008e-15     
         alternative hypothesis: true mean is not equal to 0     
         95 percent confidence interval:     
          3.688668 4.481332     
         sample estimates:     
         mean of x     
             4.085     
              
              
         >  print(class(tt))     
         [1] "htest"     
              
         >  print(is.list(tt))     
         [1] TRUE     
              
         >  print(names(tt))     
         [1] "statistic"   "parameter"   "p.value"     "conf.int"    "estimate"    "null.value"  "alternative" "method"      "data.name"     
              
         >  print(tt$p.value) # c'est bien cela     
         [1] 8.007948e-15     
              
         >  # définissons la fonction demandée     
         >     
         >  pvalt <- function(test) { return( test$p.value ) }     
              
         >  # vérifions que c'est bien ce qu'il faut :     
         >     
         >  identical( tt$p.value, pvalt(tt) ) # doit renvoyer TRUE     
         [1] TRUE     
              
    
  3. Une fonction anonyme correspond à la seule partie droite de définition de la fonction. Comme on l'utilise sans affectation, elle n'a pas de nom. Une telle fonction s'utilise généralement comme paramètre dans un appel de fonction. Ainsi, le dernier calcul de la question précédente, avec la fonction carre() appliquée à une liste, pourrait se réaliser par

    
          lapply(X=list(a=1,b=3,c=5),FUN=function(x) x*x)     
              
    

    Si on se rend compte qu'on utilise plusieurs fois la même fonction anonyme, il est conseillé d'en faire une fonction nommée. Cela gagne du temps et de la maintenance de code.

    Pour les données diabete.dar proposées, la fonction anonyme utilisée permet de spécifier na.rm=TRUE à la volée :

    
         # lecture de données sur Internet     
              
         url     <- "http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/diabete.dar"     
         diaTout <- read.table(url,header=TRUE,row.names=1)     
              
         # on se restreint aux colonnes 2 à 4, qui contiennent des NA     
              
         dia <- diaTout[,(2:4)]     
              
         # on ne peut pas calculer la moyenne à cause des NA     
              
         cat("Moyenne en colonne, version 1\n")     
         (res <- apply(X=dia,MARGIN=2,FUN=mean) ) # les parenthèses forcent l'affichage     
              
         # on utilise une fonction anonyme définie "juste au passage"     
              
         cat("Moyenne en colonne, version 2\n")     
         (res <- apply(X=dia,MARGIN=2,FUN=function(x) { return(mean(x,na.rm=TRUE)) } ) )     
              
         #### on aurait pu se contenter de     
         ####     
         ####   (res <- apply(X=dia,MARGIN=2,FUN=function(x) mean(x,na.rm=TRUE)) )     
         ####     
              
              
    
    
         Moyenne en colonne, version 1     
             chol stab.glu      hdl     
               NA 106.6725       NA     
         Moyenne en colonne, version 2     
              chol  stab.glu       hdl     
         207.84577 106.67246  50.44527     
              
    
  4. Les types de données sont les constantes, les objets spéciaux, les valeurs logiques, les nombres et les chaines de caractères. Les structures de données sont les vecteurs, les matrices, les listes et les "data frames" (qui sont des listes particulières). On connait la nature d'un objet grâce aux fonctions class(), typeof(), mode() et storage.mode().

    
          source("objets.r",echo=TRUE,print.eval=TRUE)     
              
          analyse <- function(x) { return( c( class(x), typeof(x), mode(x), storage.mode(x) ) ) }     
              
          mdr <- rbind( # matrice des résultats     
              analyse(obj01)  ,     
              analyse(obj02)  ,     
              analyse(obj03)  ,     
              analyse(obj04)  ,   # R autorise ce genre d'écriture lisible     
              analyse(obj05)  ,   # et compréhensible     
              analyse(obj06)  ,     
              analyse(obj07)  ,     
              analyse(obj08)  ,     
              analyse(obj09)  ,     
              analyse(obj10)  ,     
              analyse(obj11)  ,     
              analyse(obj12)  ,     
              analyse(obj13)  ,     
              analyse(obj14)  ,     
              analyse(obj15)  ,     
              analyse(obj16)  ,     
              analyse(obj17)  ,     
              analyse(obj18)  ,     
              analyse(obj19)  ,     
              analyse(obj20)     
          ) # fin de rbind     
          colnames(mdr)  <- c("class()","typeof()","mode()","storage.mode()")     
          row.names(mdr) <- paste("obj",sprintf("%02d",1:nrow(mdr)),sep="")     
          cat("\nVoici les caractéristiques des objets\n")     
          print( mdr, quote=FALSE )     
              
          cat("\nUne façon courte de voir les objets et leur type utilise ls.str() :\n")     
          ls.str(pattern="^obj[0-9]*$")     
              
    
    
              
         >  obj01  <- 1     
         >  obj02  <- 2.0     
         >  obj03  <- "Age"     
         >  obj04  <- TRUE     
         >  obj05  <- NA     
         >  obj06  <- Inf     
         >  obj07  <- NaN     
         >  obj08  <- 1:2     
         >  obj09  <- c(1,2)     
         >  obj10  <- c(obj01,obj02)     
         >  obj11  <- c(1,NA)     
         >  obj12  <- c(obj01,obj03)     
         >  obj13  <- list(obj01,obj02)     
         >  obj14  <- list(a=obj01,b=obj02)     
         >  obj15  <- function(x) { return( x*x ) }     
         >  obj16  <- vector(mode="integer",length="3")     
         >  obj17  <- array(data=1:4,dim=c(2:3))     
         >  obj18  <- matrix(data=1:6,nrow=2,ncol=3) # data=1:4 est incorrect     
         >  obj19  <- as.data.frame(obj17)     
         >  obj20  <- as.data.frame(obj18)     
              
              
         Voici les caractéristiques des objets     
               class()   typeof()  mode()    storage.mode()     
         obj01 numeric   double    numeric   double     
         obj02 numeric   double    numeric   double     
         obj03 character character character character     
         obj04 logical   logical   logical   logical     
         obj05 logical   logical   logical   logical     
         obj06 numeric   double    numeric   double     
         obj07 numeric   double    numeric   double     
         obj08 integer   integer   numeric   integer     
         obj09 numeric   double    numeric   double     
         obj10 numeric   double    numeric   double     
         obj11 numeric   double    numeric   double     
         obj12 character character character character     
         obj13 list      list      list      list     
         obj14 list      list      list      list     
         obj15 function  closure   function  function     
         obj16 integer    integer   numeric   integer     
         obj17 matrix     integer   numeric   integer     
         obj18 matrix     integer   numeric   integer     
         obj19 data.frame list      list      list     
         obj20 data.frame list      list      list     
              
         Une façon courte de voir les objets et leur type utilise ls.str() :     
         obj01 :  num 1     
         obj02 :  num 2     
         obj03 :  chr "Age"     
         obj04 :  logi TRUE     
         obj05 :  logi NA     
         obj06 :  num Inf     
         obj07 :  num NaN     
         obj08 :  int [1:2] 1 2     
         obj09 :  num [1:2] 1 2     
         obj10 :  num [1:2] 1 2     
         obj11 :  num [1:2] 1 NA     
         obj12 :  chr [1:2] "1" "Age"     
         obj13 : List of 2     
          $ : num 1     
          $ : num 2     
         obj14 : List of 2     
          $ a: num 1     
          $ b: num 2     
         obj15 : function (x)     
         obj16 :  int [1:3] 0 0 0     
         obj17 :  int [1:2, 1:3] 1 2 3 4 1 2     
         obj18 :  int [1:2, 1:3] 1 2 3 4 5 6     
         obj19 : 'data.frame':	2 obs. of  3 variables:     
          $ V1: int  1 2     
          $ V2: int  3 4     
          $ V3: int  1 2     
         obj20 : 'data.frame':	2 obs. of  3 variables:     
          $ V1: int  1 2     
          $ V2: int  3 4     
          $ V3: int  5 6     
              
    

    Pour supprimer un objet, on utilise la fonction rm(). Ainsi rm(x) supprime x. Pour supprimer un ensemble d'objets, on utilise rm( list=...). Ici, on peut donc écrire rm(list=ls(pattern="^obj*")) pour supprimer les objets nommés obj01, obj02... obj20. Pour tout supprimer, ce qui peut être très dangereux, il suffit donc d'écrire : rm(list=ls(pattern="*")).

    Il existe aussi un objet spécial nommé NULL pour lequel notre fonction analyse() renvoie 4 fois NULL.

    TRUE*TRUE vaut 1 car TRUE vaut 1 et FALSE vaut 0. C'est ce qui permet de réaliser des comptages comme sum(obj18>3).

    Les fonction de conversion s'appellent «as point quelque chose». Ecrire apropos("as") renvoie le nom de toutes les fonctions qui contiennent la chaine as, y compris xtfrm.AsIs. L'instruction apropos("^as") renvoie le nom de toutes les fonctions qui commencent par as, y compris assign ; pour avoir celles qui commencent vraiment par as"point", il faut utiliser apropos("^as\\."). Il y en a plus d'une centaine. On notera que contrairement à help(), apropos() oblige à utiliser des guillemets pour préciser ce qu'on cherche.

    Les fonctions de test commencent par is., celles d'affichage par print. et celles de résumé par summary.  ; nous définissons la fonction concatv() suivante pour afficher leurs noms en parallèle :

    
         tc1 <- apropos("^as\\.") # apropos("^as") est incorrect à cause de asc, assign, asS3...     
         tc2 <- apropos("^is\\.")     
         tc3 <- apropos("^plot\\.")     
         tc4 <- apropos("^print\\.")     
         tc5 <- apropos("^summary\\.")     
              
         ##############################################################     
              
         concatv <- function(ldtc) {     
              
         ##############################################################     
              
         lngMax  <- max(sapply(X=ldtc,FUN=length))     
         lngList <- length(ldtc)     
         mres    <- matrix("",nrow=lngMax,ncol=lngList)     
              
         for (indElt in seq(ldtc)) {     
           elt    <- ldtc[[indElt]]     
           lngElt <- length(elt)     
           mres[ (1:lngElt), indElt ] <- elt     
              
           # c'est plus lisible que     
           ##    mres[1:length(ldtc[[indElt]]),indElt] <- ldtc[[indElt]]     
              
         } # fin pour indElt     
              
         print(mres,quote=FALSE)     
              
         } # fin de fonction concatv     
              
         ##############################################################     
              
         # démonstration :     
              
         concatv( list(tc1,tc2,tc3,tc4,tc5) )     
              
              
    
    
                [,1]                          [,2]                  [,3]                [,4]                        [,5]     
           [1,] as.array                      is.array              plot.default        print.anova                 summary.aov     
           [2,] as.array.default              is.atomic             plot.density        print.AsIs                  summary.aovlist     
           [3,] as.call                       is.call               plot.design         print.by                    summary.connection     
           [4,] as.character                  is.character          plot.ecdf           print.condition             summary.data.frame     
           [5,] as.character.condition        is.complex            plot.function       print.connection            Summary.data.frame     
           [6,] as.character.Date             is.data.frame         plot.lm             print.data.frame            summary.Date     
           [7,] as.character.default          is.double             plot.mlm            print.Date                  Summary.Date     
           [8,] as.character.error            is.element            plot.new            print.default               summary.default     
           [9,] as.character.factor           is.empty.model        plot.spec           print.density               Summary.difftime     
          [10,] as.character.hexmode          is.environment        plot.spec.coherency print.difftime              summary.factor     
          [11,] as.character.numeric_version  is.expression         plot.spec.phase     print.DLLInfo               Summary.factor     
          [12,] as.character.octmode          is.factor             plot.stepfun        print.DLLInfoList           summary.glm     
          [13,] as.character.POSIXt           is.finite             plot.ts             print.DLLRegisteredRoutines summary.infl     
          [14,] as.character.srcref           is.function           plot.TukeyHSD       print.factor                summary.lm     
          [15,] as.complex                    is.infinite           plot.window         print.family                summary.manova     
          [16,] as.data.frame                 is.integer            plot.xy             print.formula               summary.matrix     
          [17,] as.data.frame.array           is.language                               print.ftable                summary.mlm     
          [18,] as.data.frame.AsIs            is.leaf                                   print.function              Summary.numeric_version     
          [19,] as.data.frame.character       is.list                                   print.glm                   Summary.ordered     
          [20,] as.data.frame.complex         is.loaded                                 print.hexmode               summary.POSIXct     
          [21,] as.data.frame.data.frame      is.logical                                print.infl                  Summary.POSIXct     
          [22,] as.data.frame.Date            is.matrix                                 print.integrate             summary.POSIXlt     
          [23,] as.data.frame.default         is.mts                                    print.libraryIQR            Summary.POSIXlt     
          [24,] as.data.frame.difftime        is.na                                     print.listof                summary.proc_time     
          [25,] as.data.frame.factor          is.na<-                                   print.lm                    summary.srcfile     
          [26,] as.data.frame.integer         is.na.data.frame                          print.logLik                summary.srcref     
          [27,] as.data.frame.list            is.na<-.default                           print.NativeRoutineList     summary.stepfun     
          [28,] as.data.frame.logical         is.na<-.factor                            print.noquote               summary.table     
          [29,] as.data.frame.matrix          is.name                                   print.numeric_version     
          [30,] as.data.frame.model.matrix    is.nan                                    print.octmode     
          [31,] as.data.frame.numeric         is.na.numeric_version                     print.packageInfo     
          [32,] as.data.frame.numeric_version is.na.POSIXlt                             print.POSIXct     
          [33,] as.data.frame.ordered         is.null                                   print.POSIXlt     
          [34,] as.data.frame.POSIXct         is.numeric                                print.proc_time     
          [35,] as.data.frame.POSIXlt         is.numeric.Date                           print.restart     
          [36,] as.data.frame.raw             is.numeric.difftime                       print.rle     
          [37,] as.data.frame.table           is.numeric.POSIXt                         print.simple.list     
          [38,] as.data.frame.ts              is.numeric_version                        print.srcfile     
          [39,] as.data.frame.vector          is.object                                 print.srcref     
          [40,] as.Date                       is.ordered                                print.summaryDefault     
          [41,] as.Date.character             is.package_version                        print.summary.table     
          [42,] as.Date.date                  is.pairlist                               print.table     
          [43,] as.Date.dates                 is.primitive                              print.terms     
          [44,] as.Date.default               is.qr                                     print.ts     
          [45,] as.Date.factor                is.R                                      print.warnings     
          [46,] as.Date.numeric               is.raster     
          [47,] as.Date.POSIXct               is.raw     
          [48,] as.Date.POSIXlt               is.recursive     
          [49,] as.dendrogram                 is.relistable     
          [50,] as.difftime                   is.single     
          [51,] as.dist                       is.stepfun     
          [52,] as.double                     is.symbol     
          [53,] as.double.difftime            is.table     
          [54,] as.double.POSIXlt             is.ts     
          [55,] as.environment                is.tskernel     
          [56,] as.expression                 is.unsorted     
          [57,] as.expression.default         is.vector     
          [58,] as.factor     
          [59,] as.formula     
          [60,] as.function     
          [61,] as.function.default     
          [62,] as.graphicsAnnot     
          [63,] as.hclust     
          [64,] as.hexmode     
          [65,] as.integer     
          [66,] as.list     
          [67,] as.list.data.frame     
          [68,] as.list.Date     
          [69,] as.list.default     
          [70,] as.list.environment     
          [71,] as.list.factor     
          [72,] as.list.function     
          [73,] as.list.numeric_version     
          [74,] as.list.POSIXct     
          [75,] as.logical     
          [76,] as.logical.factor     
          [77,] as.matrix     
          [78,] as.matrix.data.frame     
          [79,] as.matrix.default     
          [80,] as.matrix.noquote     
          [81,] as.matrix.POSIXlt     
          [82,] as.name     
          [83,] as.null     
          [84,] as.null.default     
          [85,] as.numeric     
          [86,] as.numeric_version     
          [87,] as.octmode     
          [88,] as.ordered     
          [89,] as.package_version     
          [90,] as.pairlist     
          [91,] as.person     
          [92,] as.personList     
          [93,] as.POSIXct     
          [94,] as.POSIXct.date     
          [95,] as.POSIXct.Date     
          [96,] as.POSIXct.dates     
          [97,] as.POSIXct.default     
          [98,] as.POSIXct.numeric     
          [99,] as.POSIXct.POSIXlt     
         [100,] as.POSIXlt     
         [101,] as.POSIXlt.character     
         [102,] as.POSIXlt.date     
         [103,] as.POSIXlt.Date     
         [104,] as.POSIXlt.dates     
         [105,] as.POSIXlt.default     
         [106,] as.POSIXlt.factor     
         [107,] as.POSIXlt.numeric     
         [108,] as.POSIXlt.POSIXct     
         [109,] as.qr     
         [110,] as.raster     
         [111,] as.raw     
         [112,] as.relistable     
         [113,] as.roman     
         [114,] as.sigcode     
         [115,] as.single     
         [116,] as.single.default     
         [117,] as.stepfun     
         [118,] as.symbol     
         [119,] as.table     
         [120,] as.table.default     
         [121,] as.ts     
         [122,] as.vector     
         [123,] as.vector.factor     
              
    

    c(1,2,"3",4) renvoie "1" "2" "3" "4" car R utilise un typage non explicite et "normalise" en quelque sorte le typage d'un vecteur qui doit forcément être homogène. Ce ne serait pas le cas avec list(1,2,"3",4). En conséquence, si on doit avoir un élément de type caractère dans une matrice, il faut convertir la matrice en data frame pour éviter d'obtenir une matrice de caractères :

    
         # les parenthèses forcent l'affichage     
         # l'affectation multiple simple est OK     
         # mais attention : (a <- 1 + b <- 2) est interdit     
         # on pourrait écrire (a <- 1 + (b <- 2)) mais cela devient vite fastidieux     
              
         > (m1 <- as.data.frame(m2 <- matrix(1:12,nrow=4))) # c'est m1 qui est affichée     
              
           V1 V2 V3     
         1  1  5  9     
         2  2  6 10     
         3  3  7 11     
         4  4  8 12     
              
         > m2 # remarquer le nom des lignes et des colonnes     
              
              [,1] [,2] [,3]     
         [1,]    1    5    9     
         [2,]    2    6   10     
         [3,]    3    7   11     
         [4,]    4    8   12     
              
         > m1[3,2] <- "?"     
         > m2[3,2] <- "?"     
              
         > print(m1)     
              
           V1 V2 V3     
         1  1  5  9     
         2  2  6 10     
         3  3  ? 11     
         4  4  8 12     
              
         > print(m2)     
              
              [,1] [,2] [,3]     
         [1,] "1"  "5"  "9"     
         [2,] "2"  "6"  "10"     
         [3,] "3"  "?"  "11"     
         [4,] "4"  "8"  "12"     
              
         > print(m2,quite=FALSE) # pas d'erreur détectée pour quite !     
              
              [,1] [,2] [,3]     
         [1,] "1"  "5"  "9"     
         [2,] "2"  "6"  "10"     
         [3,] "3"  "?"  "11"     
         [4,] "4"  "8"  "12"     
              
         > print(m2,quote=FALSE)     
              
              [,1] [,2] [,3]     
         [1,] 1    5    9     
         [2,] 2    6    10     
         [3,] 3    ?    11     
         [4,] 4    8    12     
              
              
    

    Une lecture attentive des fonctions summary.* précédentes montre des noms de fonctions presque identiques, comme summary.Date() et Summary.Date(). En fait, certaines fonctions sont génériques (modèles objets de fonctions), d'autres sont les "vraies" fonctions à utiliser :

    
              
         >  da <- date()     
              
         >  print( summary.Date(da) )     
            Length     Class      Mode     
                 1 character character     
              
         >  # non exécuté : print( Summary.Date(da) )     
         >  ##  Erreur dans Summary.Date(da) : objet '.Generic' introuvable     
              
         ##############################################################     
              
         >  # code-source des fonctions     
              
         ##############################################################     
              
         >  print( summary.Date )     
              
         # ------------------------------------------------------------     
              
         function (object, digits = 12L, ...)     
         {     
             x <- summary.default(unclass(object), digits = digits, ...)     
             if (m <- match("NA's", names(x), 0)) {     
                 NAs <- as.integer(x[m])     
                 x <- x[-m]     
                 attr(x, "NAs") <- NAs     
             }     
             class(x) <- c("summaryDefault", "table", oldClass(object))     
             x     
         }     
         <bytecode: 0x3cdff90>     
         <environment: namespace:base>     
              
         ##############################################################     
              
         >  print( Summary.Date )     
              
         # ------------------------------------------------------------     
              
         function (..., na.rm)     
         {     
             ok <- switch(.Generic, max = , min = , range = TRUE, FALSE)     
             if (!ok)     
                 stop(gettextf("%s not defined for \"Date\" objects",     
                     .Generic), domain = NA)     
             val <- NextMethod(.Generic)     
             class(val) <- oldClass(list(...)[[1L]])     
             val     
         }     
         <bytecode: 0x3602378>     
         <environment: namespace:base>     
              
    
  5. Comme on peut le voir sur l'affichage de la solution pour l'exercice 4, (1:2) est de classe integer, de type integer et de mode numeric alors que c(1,2) est de classe numeric, de type double et de mode numeric. La simple indexation par crochet pour une liste renvoie toujours une liste alors que la double indexation par crochets pour une liste peut renvoyer un vecteur. Avec un simple crochet, on peut accéder à plusieurs éléments, comme par exemple demo[c("a","b")] alors que demo[[c("a","b")]] renvoie une erreur indiquant indice hors limites si demo est la variable définie ci-dessous :

    
         demo <- list(a=1:5,b=list(3.1,12),c="un exemple")     
              
         cat("\nAvec un crochet\n")     
              
         demo[1]     
         demo[2]     
         demo[3]     
         class(demo[1])     
         class(demo[2])     
              
         cat("\nAvec deux crochets\n")     
              
         demo[[1]]     
         demo[[2]]     
         demo[[3]]     
         class(demo[[1]])     
         class(demo[[2]])     
              
         cat("\nComposants nommés\n")     
              
         demo["a"]     
         demo["b"]     
         demo["c"]     
         class(demo["a"])     
         class(demo["b"])     
              
         # not run: demo[["a"]]     
         # not run: demo[["b"]]     
         # not run: demo[["c"]]     
              
         cat("\nNature selon les crochets\n")     
              
         cat(" demo[1]   est une liste ? : ",is.list(demo[ 1 ]),"\n")  # TRUE     
         cat(" demo[[1]] est une liste ? : ",is.list(demo[[1]]),"\n")  # FALSE     
              
         identical(demo[1],demo[[1]])     # FALSE     
         identical(demo[1],demo$a)        # FALSE     
         identical(demo[[1]],demo$a)      # TRUE     
         identical(demo["a"],demo$a)      # FALSE     
         identical(demo[1][[1]],demo$a)   # TRUE     
              
    
    
              
         Avec un crochet     
         $a     
         [1] 1 2 3 4 5     
              
         $b     
         $b[[1]]     
         [1] 3.1     
              
         $b[[2]]     
         [1] 12     
              
              
         $c     
         [1] "un exemple"     
              
         [1] "list"     
         [1] "list"     
              
         Avec deux crochets     
         [1] 1 2 3 4 5     
         [[1]]     
         [1] 3.1     
              
         [[2]]     
         [1] 12     
              
         [1] "un exemple"     
         [1] "integer"     
         [1] "list"     
              
         Composants nommés     
         $a     
         [1] 1 2 3 4 5     
              
         $b     
         $b[[1]]     
         [1] 3.1     
              
         $b[[2]]     
         [1] 12     
              
              
         $c     
         [1] "un exemple"     
              
         [1] "list"     
         [1] "list"     
              
         Nature selon les crochets     
          demo[1]   est une liste ? :  TRUE     
          demo[[1]] est une liste ? :  FALSE     
         [1] FALSE     
         [1] FALSE     
         [1] TRUE     
         [1] FALSE     
         [1] TRUE     
              
    

    Il y a un air de famille mais aussi des différences entre les fonctions qui contiennent le mot apply.

    apply() s'applique aux marges (lignes ou colonnes, spécifiées respectviement par MARGIN=1 et MARGIN=2) d'un vecteur ou d'une matrice. lapply(), comme son nom l'indique, s'applique à une liste et renvoie une liste. sapply() simplifie les résultats de lapply et renvoie un vecteur.

    On peut noter qu'il existe aussi rapply() et eapply() dont l'aide et les exemples doivent être suffisants pour comprendre que rapply applique récursivement une fonction à une liste et que eapply applique une fonction à tous les éléments d'un environnement.

    Enfin, R dispose aussi de tapply() et vapply() qui permettent respectivement d'appliquer une fonction à un «tableau irrégulier» (ragged array) et d'utiliser sapply avec une sortie prédéfinie. Voir example(tapply) et example(vapply). Nous les utiliserons un peu plus tard.

    Si on exécute la commande apropos("apply"), R indique aussi qu'il existe dendrapply() et kernapply().

    
         # une matrice de données numériques     
              
         nbl <- 4 # lignes     
         nbc <- 3 # colonnes     
         mdata <- matrix(data=1:12,nrow=nbl)     
         row.names(mdata) <- paste("Lig",1:nbl,sep="")     
         colnames(mdata)  <- paste("Col",1:nbc,sep="_")     
         print(mdata)     
              
         # moyenne en ligne avec apply     
              
         cat(" apply : moyenne en ligne \n")     
         print(apply(X=mdata,MARGIN=1,FUN=mean)) # apply(mdata,1,mean) est moins lisible, sans doute     
         # il existe une fonction pour cela : rowMeans     
              
         # moyenne en colonne avec apply     
              
         cat(" apply : moyenne en colonne \n")     
         print(apply(X=mdata,MARGIN=2,FUN=mean))     
         # il existe une fonction pour cela : colMeans     
              
         # une liste avec des données numériques et une valeur NA     
              
         str(ldata <- list(a=5,b=c(3,8,2),c=8,d=5,e=c(6,7,NA)))  # str est mieux que ls.str dans certains cas     
              
         # lapply de base : moyenne de chaque composante de la liste     
              
         cat("lapply de mean\n")     
         print(lapply(X=ldata,FUN=mean))     
              
         # lapply avec fonction anonyme pour gérer les NA     
              
         cat("lapply de mean avec na.rm=TRUE (solution 1)\n")     
         print(lapply(X=ldata,FUN=function(v) mean(v,na.rm=TRUE) ))     
              
         # mieux : lapply avec paramètre supplémentaire pour la fonction     
              
         cat("lapply de mean avec na.rm=TRUE (solution 2)\n")     
         print(lapply(X=ldata,FUN=mean,na.rm=TRUE)) # objet de classe "list"     
              
         # sapply     
              
         cat(" le s de sapply signifie : simplification\n")     
         print(sapply(X=ldata,FUN=mean,na.rm=TRUE)) # objet de classe "numeric"     
              
         # mapply     
              
         cat(" le m de mapply signifie : multivarié\n")     
         print(mapply(rep,1:4,8:11))     
         serie1 <- 1:3     
         serie2 <- serie1*10     
         serie3 <- serie1**2     
         print(rbind(serie1,serie2,serie3))     
         cat(" mapply avec 2 arguments\n")     
         print(mapply(FUN=function(x,y)   x+y  ,serie1,serie2))     
         cat(" mapply avec 3 arguments\n")     
         print(mapply(FUN=function(x,y,z) x+y+z,serie1,serie2,serie3))     
              
         #     
         # non présentés pour l'instant : tapply, vapply, rapply, eapply     
         #     
              
              
    
    
              Col_1 Col_2 Col_3     
         Lig1     1     5     9     
         Lig2     2     6    10     
         Lig3     3     7    11     
         Lig4     4     8    12     
          apply : moyenne en ligne     
         Lig1 Lig2 Lig3 Lig4     
            5    6    7    8     
          apply : moyenne en colonne     
         Col_1 Col_2 Col_3     
           2.5   6.5  10.5     
         List of 5     
          $ a: num 5     
          $ b: num [1:3] 3 8 2     
          $ c: num 8     
          $ d: num 5     
          $ e: num [1:3] 6 7 NA     
         lapply de mean     
         $a     
         [1] 5     
              
         $b     
         [1] 4.333333     
              
         $c     
         [1] 8     
              
         $d     
         [1] 5     
              
         $e     
         [1] NA     
              
         lapply de mean avec na.rm=TRUE (solution 1)     
         $a     
         [1] 5     
              
         $b     
         [1] 4.333333     
              
         $c     
         [1] 8     
              
         $d     
         [1] 5     
              
         $e     
         [1] 6.5     
              
         lapply de mean avec na.rm=TRUE (solution 2)     
         $a     
         [1] 5     
              
         $b     
         [1] 4.333333     
              
         $c     
         [1] 8     
              
         $d     
         [1] 5     
              
         $e     
         [1] 6.5     
              
          le s de sapply signifie : simplification     
                a        b        c        d        e     
         5.000000 4.333333 8.000000 5.000000 6.500000     
          le m de mapply signifie : multivarié     
         [[1]]     
         [1] 1 1 1 1 1 1 1 1     
              
         [[2]]     
         [1] 2 2 2 2 2 2 2 2 2     
              
         [[3]]     
          [1] 3 3 3 3 3 3 3 3 3 3     
              
         [[4]]     
          [1] 4 4 4 4 4 4 4 4 4 4 4     
              
                [,1] [,2] [,3]     
         serie1    1    2    3     
         serie2   10   20   30     
         serie3    1    4    9     
          mapply avec 2 arguments     
         [1] 11 22 33     
          mapply avec 3 arguments     
         [1] 12 26 42     
              
    

    Les instructions proposées à la question 5 viennent créer la liste des mots d'une phrase et comptent le nombre de lettres de chaque mot. La fonction nchar() est vectorielle, mais avec sapply, les calculs sont plus explicites. Les deux dernières instructions sont équivalentes.

    
              
         >  # les mots     
         >     
         >  ldm <-  unlist( strsplit(x="le chat mange la souris",split=" "))     
              
         >  # version courte     
         >     
         >  nchar(ldm)     
         [1] 2 4 5 2 6     
              
         >  # un peu mieux avec sapply ?     
         >     
         >  sapply(FUN=function(x) { nchar(x) },X=ldm)     
             le   chat  mange     la souris     
              2      4      5      2      6     
              
         >  # on n'est pas obligé de tout détailler     
         >     
         >  sapply(FUN=nchar,X=ldm)     
             le   chat  mange     la souris     
              2      4      5      2      6     
              
    

    tapply() est un peu la composition de lapply() et split(), mais en mieux. En voici un exemple : on dispose de 30 personnes, 20 hommes et 10 femmes (variable SEXE) et on veut connaitre la moyenne de l'age par sexe, par niveau d'études et par croisement (sexe,étude). Les données sont dans le fichier personnes.dar.

    
         NUM  SEXE  AGE ETUD
         M001 Femme 62  Secondaire
         M002 Homme 60  Niveau_bac
         M003 Femme 31  Secondaire
         M004 Femme 27  Secondaire
         M005 Homme 22  Supérieur
         M006 Femme 07  Niveau_bac
         M007 Femme 19  Supérieur
         M008 Femme 53  Niveau_bac
         M009 Homme 62  Secondaire
         M010 Femme 63  Secondaire
         M011 Homme 65  Secondaire
         M012 Femme 11  Niveau_bac
         M013 Homme 78  Secondaire
         M014 Homme 20  Supérieur
         M015 Femme 48  Niveau_bac
         M016 Femme  5  Secondaire
         M017 Homme 39  Niveau_bac
         M018 Homme 28  Niveau_bac
         M019 Homme 14  Niveau_bac
         M020 Homme 11  Secondaire
         M021 Homme 17  Supérieur
         M022 Homme 25  Niveau_bac
         M023 Homme 39  Niveau_bac
         M024 Homme  8  Secondaire
         M025 Homme 31  Secondaire
         M026 Homme 22  Secondaire
         M027 Homme 47  Secondaire
         M028 Homme 68  Secondaire
         M029 Homme 38  Secondaire
         M030 Homme 47  Secondaire
         
    
    
         # lecture des données     
              
         pers <- lit.dar("personnes.dar")     
              
         # tris à plat élémentaires     
              
         print(table(pers$SEXE))     
         print(table(pers$ETUD))     
              
         # moyenne par sexe "à la main"     
              
         attach(pers)     
         cats("Moyenne des ages pour les hommes et les femmes","-")     
         print(mean(pers[SEXE=="Femme","AGE"]))     
         print(mean(pers[SEXE=="Homme","AGE"]))     
         detach(pers)     
              
         # split permet de découper et renvoie une liste     
              
         cats("Utilisation de split")     
         decoupe <- split(x=pers$AGE,f=pers$SEXE)     
         print(decoupe)     
              
         # on peut alors utiliser lapply :     
              
         cats("Utilisation de lapply")     
         moyAges <- lapply(X=decoupe,FUN=mean)     
         print(moyAges)     
              
         # le tout en une seule fois avec les deux facteurs ensemble :     
              
         cats("Tableau résumé")     
         moys <- matrix( lapply( X=split(x=pers$AGE,f=list(pers$SEXE,pers$ETUD)), FUN=mean ),     
                         ncol=length(levels(pers$ETUD)))     
         print(moys)     
         # attention : on ne peut pas exécuter     
         #   round(moys,2)     
              
         # avec tapply, le tout en plus simple :     
              
         cats("Utilisation de tapply")     
         moys <- tapply( X=pers$AGE,INDEX=list(pers$SEXE,pers$ETUD), FUN=mean )     
              
         print(moys)     
              
         cats("Tableau des moyennes arrondies en années :")     
         print(round(moys,2))     
              
              
    
    
              
         Femme Homme     
            10    20     
              
         Niveau_bac Secondaire  Supérieur     
                 10         16          4     
              
         Moyenne des ages pour les hommes et les femmes     
         ----------------------------------------------     
              
         [1] 32.6     
         [1] 37.05     
              
         Utilisation de split     
         ====================     
              
         $Femme     
          [1] 62 31 27  7 19 53 63 11 48  5     
              
         $Homme     
          [1] 60 22 62 65 78 20 39 28 14 11 17 25 39  8 31 22 47 68 38 47     
              
              
         Utilisation de lapply     
         =====================     
              
         $Femme     
         [1] 32.6     
              
         $Homme     
         [1] 37.05     
              
              
         Tableau résumé     
         ==============     
              
              [,1]     [,2]     [,3]     
         [1,] 29.75    37.6     19     
         [2,] 34.16667 43.36364 19.66667     
              
         Utilisation de tapply     
         =====================     
              
               Niveau_bac Secondaire Supérieur     
         Femme   29.75000   37.60000  19.00000     
         Homme   34.16667   43.36364  19.66667     
              
         Tableau des moyennes arrondies en années :     
         ==========================================     
              
               Niveau_bac Secondaire Supérieur     
         Femme      29.75      37.60     19.00     
         Homme      34.17      43.36     19.67     
              
    

    La fonction ls() permet d'avoir une liste (caractère) des objets correspondant à pattern. Avec ^mdata on obtient les objets dont le nom commence par mdata. Pour utiliser dim() sur chacun des objets, il faut passer de caractère à expression à l'aide de eval() et parse() :

    
         # les données     
              
         aleaEntier <- function(x) { return(round(runif(n=1,min=5,max=10))) }     
              
         # pour générer toujours les mêmes données, décommenter la ligne suivante     
         # set.seed(1234)     
              
         mdata1 <- matrix(nrow=aleaEntier(), ncol=aleaEntier() )     
         mdata2 <- matrix(nrow=aleaEntier(), ncol=aleaEntier() )     
         mdata3 <- matrix(nrow=aleaEntier(), ncol=aleaEntier() )     
         mdata4 <- matrix(nrow=aleaEntier(), ncol=aleaEntier() )     
              
         dim(mdata1)     
         dim(mdata2)     
         dim(mdata3)     
         dim(mdata4)     
              
         autremdata <- mdata1 # ne commence pas par mdata     
              
         # dim("mdata1") est incorrect, il faut exécuter     
         # dim(eval(parse(text="mdata1"))) donc pour la liste :     
              
         t(sapply(FUN=function(x) { dim(eval(parse(text=x))) },X=ls(pattern="^mdata")))     
              
    
    
         [1] 7 8     
         [1] 6 6     
         [1] 10  5     
         [1] 9 5     
                [,1] [,2]     
         mdata1    7    8     
         mdata2    6    6     
         mdata3   10    5     
         mdata4    9    5     
              
    
  6. La partie 1 et la partie 2 du code proposé viennent permuter les contenus de a et de b. Ainsi au début de la partie 1, a et b valent respectivement 12 et 3 ; à la fin de la partie 1, a et b valent respectivement 3 et 12. A la fin de la partie 2, a et b valent à nouveau respectivement 12 et 3.

    p, m et g signifient respectivement petit, milieu et grand. A partir de a, b et c, la partie 3 met le plus petit des trois nombres dans p, les plus grand des trois dans g et celui du milieu dans m. On aurait aussi pu utiliser

    
          d <- sort( c(a,b,c) )     
          p <- d[1]     
          m <- d[2]     
          g <- d[3]     
              
              
    

    Les 4 premières instructions de la partie 4 (qui se terminent par } # fin de si 1) mettent 0 dans pg quoi qu'il arrive et ensuite mettent dans pg a suivi de b si a est inférieur à b. Il est prudent d'initialiser toute variable. Sans l'instruction pg <- 0 la variable pg n'existerait pas pour a supérieur ou égal à b. La dernière instruction de la partie 4, qui correspond à 7 lignes de texte, met dans p et g respectivement le plus petit et le plus grand des deux nombres a et b.

    Dans la partie 5, il n'y a pas d'accolade pour délimiter les parties alors et sinon. C'est une mauvaise pratique : il faut toujours mettre des accolades. La deuxième instruction qui suit # attention 2 est toujours exécutée, ce qui n'est sans doute pas ce que voulait réaliser le programmeur. Par contre le code qui suit # attention 3 est refusé par R qui y voit un else inattendu : else n'est pas une instruction, mais seulement une partie d'une instruction if. Ne pas avoir mis de parenthèse oblige R à déduire qu'il n'y a pas de else.

  7. Il s'agit des fonctions which.max, ifelse, apply, colMeans, sweep et transform définies dans le package base. La conclusion qui s'impose est qu'il est conseillé de bien connaitre les fonctions des packages fondamentaux que sont base, stats et graphics avant de programmer «à tout-và». Voici des exemples d'utilisation de ces fonctions.

    
          # première position du maximum dans un vecteur     
              
          vect <- c(1,8,7,2,5,8,1)     
              
          cat("Dans ",vect," le maximum (",max(vect),") est en position ",which.max(vect),"\n\n",sep=" ")     
          # pour avoir toutes les positions : which( vect==max(vect) )     
              
          # dichotomisation d'un vecteur numérique via un seuil fixé     
              
          vectBin <- ifelse( vect<3, "précoce", "tardif")     
          cat("Voici le comptage des \"jeunes\" et des \"vieux\" (seuil=3) \n")     
          print( table( vectBin ) )     
              
          # création d'une matrice numérique     
              
          nblig <- 3     
          nbcol <- 4     
          mat   <- matrix(data=1:12,nrow=nblig)     
          colnames(mat)  <- paste("Col",1:nbcol,sep="")     
          row.names(mat) <- paste("Lig",1:nblig,sep="")     
          cat("\nMatrice de départ :\n")     
          print(mat)     
              
          # ajout des min, max, moy et ect des colonnes     
              
          newMat <- rbind(     
             apply(X=mat,MARGIN=2,FUN=min),     
             colMeans(mat),                 # donc équivalent à apply(X=mat,FUN=mean,MARGIN=2)     
             apply(mat,2,max),              # plus court, mais plus dangereux     
             apply(X=mat,MARGIN=2,FUN=sd) , # surtout pas sd(mat), c'est l'écart-type général     
             mat     
          ) # fin de rbind     
          row.names(newMat)[1:4] <- c("Min","Moy","Max","Ect")     
              
          cat("\nMatrice après ajout des calculs en colonne\n")     
          print(newMat)     
              
          # division des lignes d'une matrice par leur somme [en ligne]     
              
          cat("\nMatrice après division en ligne\n")     
          print( sweep(x=mat,MARGIN=1,STATS=rowSums(mat),FUN="/") ) # noter x= pas X=     
              
          # transformation : conversion de pouces en cm dans un data frame     
              
          df            <- as.data.frame(mat[,1:2])     
          names(df)     <- c("Poids (kg)","Taille (pouces)")     
          row.names(df) <- paste("Ind",sprintf("%03d",1:nrow(df)),sep="_")     
          cat("\nDonnées initiales en \"data frame\":\n")     
          print(df)     
          cat("\nDonnées après conversion des pouces en cm\n")     
          print(transform(df,"Taille (cms)"=df[,2]*2.54))     
              
          # colnames(df)[2] <- "height"     
          # transform(df,taille=height*2.54) avec des colonnes nommées     
              
              
    
    
         Dans  1 8 7 2 5 8 1  le maximum ( 8 ) est en position  2     
              
         Voici le comptage des "jeunes" et des "vieux" (seuil=3)     
         vectBin     
         précoce  tardif     
               3       4     
              
         Matrice de départ :     
              Col1 Col2 Col3 Col4     
         Lig1    1    4    7   10     
         Lig2    2    5    8   11     
         Lig3    3    6    9   12     
              
         Matrice après ajout des calculs en colonne     
              Col1 Col2 Col3 Col4     
         Min     1    4    7   10     
         Moy     2    5    8   11     
         Max     3    6    9   12     
         Ect     1    1    1    1     
         Lig1    1    4    7   10     
         Lig2    2    5    8   11     
         Lig3    3    6    9   12     
              
         Matrice après division en ligne     
                    Col1      Col2      Col3      Col4     
         Lig1 0.04545455 0.1818182 0.3181818 0.4545455     
         Lig2 0.07692308 0.1923077 0.3076923 0.4230769     
         Lig3 0.10000000 0.2000000 0.3000000 0.4000000     
              
         Données initiales en "data frame":     
                 Poids (kg)  Taille (pouces)     
         Ind_001           1               4     
         Ind_002           2               5     
         Ind_003           3               6     
              
         Données après conversion des pouces en cm     
                 Poids..kg.. Taille..pouces. Taille..cms.     
         Ind_001           1               4        10.16     
         Ind_002           2               5        12.70     
         Ind_003           3               6        15.24     
              
    
  8. Il suffit d'utiliser la fonction cbind() qui crée une matrice ; son affichage provoque une numérotation automatique par défaut :

    
              
         > data(mtcars)     
              
         > head(mtcars)     
                            mpg cyl disp  hp drat    wt  qsec vs am gear carb     
         Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4     
         Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4     
         Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1     
         Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1     
         Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2     
         Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1     
              
         > cbind(names(mtcars))     
               [,1]     
          [1,] "mpg"     
          [2,] "cyl"     
          [3,] "disp"     
          [4,] "hp"     
          [5,] "drat"     
          [6,] "wt"     
          [7,] "qsec"     
          [8,] "vs"     
          [9,] "am"     
         [10,] "gear"     
         [11,] "carb"     
              
    
  9. Pour définir les paramètres d'une fonction, on doit impérativement les nommer. Par contre, il est facultatif de leur donner une valeur par défaut avec =. Mettre l'ellipse dans la définition des paramètres signifie que la fonction peut acepter (et transmettre) d'autres paramètres que ceux explicitement fournis. Un exemple d'utilisation de l'ellipse sera fourni à l'exercice 13. On peut tester si un paramètre est présent (ou plutôt absent) avec missing(paramètre). La liste des paramètres est fournie par formals(fonction) mais on peut aussi utiliser args(fonction) pour connaitre l'entête de la fonction ; le texte de la fonction est donné par body(fonction). Une fonction est une variable : on peut l'affecter, la copier, la supprimer...

    
         # ce fichier est nommé fonction.r ; on l'exécute par :     
         #     
         #    source(file="fonction.r",encoding="latin1",echo=TRUE)     
         #     
              
         # 1. définition d'une fonction     
              
         carre <- function(x) {     
              
           # calcule le carré de son argument     
              
           y <- x*x     
              
           return(y)     
              
         } # fin de fonction carre     
              
         # 2. une fonction est une variable     
              
         print( carre )     
         copieDeCarre <- carre     
              
         # 3. entête (signature) de la fonction     
              
         print( formals(carre) )     
         print( args(carre) )     
              
         # 4. corps (texte) de la fonction     
              
         print( body(carre) )     
              
         # un exemple plus conséquent     
              
         print( formals(lm) )     
         print( args(lm) )     
              
              
    
    
              
         > # ce fichier est nommé fonction.r ; on l'exécute par :     
         > #     
         > #    source(file="fonction.r",encoding="latin1",echo=TRUE)     
         > #     
         >     
         > # 1. définition d'une fonction     
              
         > carre <- function(x) {     
              +     
              +   # calcule le carré de son argument     
              +     
              +   y <- x*x     
              +     
              +   return(y)     
              +     
              + } # fin de fonction carre     
              
              
         > # 2. une fonction est une variable     
         >     
         > print( carre )     
         function(x) {     
              
           # calcule le carré de son argument     
              
           y <- x*x     
              
           return(y)     
              
         }     
              
         > copieDeCarre <- carre     
              
         > # 3. entête (signature) de la fonction     
         >     
         > print( formals(carre) )     
         $x     
              
              
              
         > print( args(carre) )     
         function (x)     
         NULL     
              
         > # 4. corps (texte) de la fonction     
         >     
         > print( body(carre) )     
         {     
             y <- x * x     
             return(y)     
         }     
              
         > # un exemple plus conséquent     
         >     
         > print( formals(lm) )     
         $formula     
              
              
         $data     
              
              
         $subset     
              
              
         $weights     
              
              
         $na.action     
              
              
         $method     
         [1] "qr"     
              
         $model     
         [1] TRUE     
              
         $x     
         [1] FALSE     
              
         $y     
         [1] FALSE     
              
         $qr     
         [1] TRUE     
              
         $singular.ok     
         [1] TRUE     
              
         $contrasts     
         NULL     
              
         $offset     
              
              
         $...     
              
              
              
         > print( args(lm) )     
         function (formula, data, subset, weights, na.action, method = "qr",     
             model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE,     
             contrasts = NULL, offset, ...)     
         NULL     
              
    

    Pour utiliser les paramètres d'une fonction, on peut donner leur valeur par position ou avec leur nom. R permet d'utiliser des noms abbrégés à condition qu'il n'y ait pas ambiguité :

    
         # 1. définition de f     
              
         f <- function(xLong=-1,xEncorePlusLong) {     
              
           if (missing(xEncorePlusLong)) {     
             cat("\nCalcul impossible : il faut donner deux paramètres, \n")     
             cat(" à savoir xLong et xEncorePlusLong.\n")     
           } else {     
             cat("la somme de ",xLong," et de ", xEncorePlusLong," est ",xLong+xEncorePlusLong,"\n")     
           } # fin si     
              
         } # fin de fonction f     
              
         # 2. utilisations de f     
              
         formals(f)                   # liste des paramètres     
              
         args(f)                      # entête     
              
         f()                          # sans paramètre     
              
         f(xLong=2,xEncorePlusLong=3) # utilisation stricte des paramètres (nommés)     
              
         f(xEncorePlusLong=3,xLong=2) # utilisation des paramètres nommés (sans ordre)     
              
         f(2,3)                       # utilisation par position     
              
         f(xE=3)                      # utilisation d'abbréviation de la valeur par défaut     
              
         f(2,xE=3)                    # utilisation d'abbréviation non ambigue     
              
         f(2,x=3)                     # utilisation ambigue non autorisée     
              
              
    
    
              
         > # 1. définition de f     
              
         > f <- function(xLong=-1,xEncorePlusLong) {     
              
                if (missing(xEncorePlusLong)) {     
                  cat("\nCalcul impossible : il faut donner deux paramètres, \n")     
                  cat(" à savoir xLong et xEncorePlusLong.\n")     
                } else {     
                  cat("la somme de ",xLong," et de ", xEncorePlusLong," est ",xLong+xEncorePlusLong,"\n")     
                } # fin si     
              
         } # fin de fonction f     
              
         > # 2. utilisations de f     
              
         > formals(f)                   # liste des paramètres     
         $xLong     
         -1     
              
         $xEncorePlusLong     
              
              
              
         > args(f)                      # entête     
         function (xLong = -1, xEncorePlusLong)     
         NULL     
              
         > f()                          # sans paramètre     
              
         Calcul impossible : il faut donner deux paramètres,     
          à savoir xLong et xEncorePlusLong.     
              
         > f(xLong=2,xEncorePlusLong=3) # utilisation stricte des paramètres (nommés)     
         la somme de  2  et de  3  est  5     
              
         > f(xEncorePlusLong=3,xLong=2) # utilisation des paramètres nommés (sans ordre)     
         la somme de  2  et de  3  est  5     
              
         > f(2,3)                       # utilisation par position     
         la somme de  2  et de  3  est  5     
              
         > f(xE=3)                      # utilisation d'abbréviation de la valeur par défaut     
         la somme de  -1  et de  3  est  2     
              
         > f(2,xE=3)                    # utilisation d'abbréviation non ambigue     
         la somme de  2  et de  3  est  5     
              
         > f(2,x=3)                     # utilisation ambigue non autorisée     
         Erreur dans f(2, x = 3) :     
           l'argument 2 correspond à plusieurs arguments formels     
              
    

    Au niveau du renvoi des valeurs calculées, R n'autorise qu'une seule expression. Pour renvoyer plusieurs valeurs, il faut choisir entre vecteur (éventuellement avec noms), matrice, liste ou "objet" plus sophistiqué. Une liste avec ses composantes nommées est souvent un bon choix, qui évite les erreurs d'indice :

    
         # une fonction qui renvoie min et max dans un vecteur     
              
         minEtmax <- function(x) {     
           return( c(min(x),max(x)) )     
         } # fin de fonction minEtmax     
              
         # utilisation : il faut se rappeler de l'ordre de renvoi     
              
         vect <- c(1,8,15,2,50,7)     
         mm   <- minEtmax(vect)     
              
         cat(" min et max de ",paste(vect,collapse=" "),":",paste(mm,collapse=" "),"\n")     
         vmin1 <- mm[1] # il faut se rappeler de l'indice 1     
              
         # une fonction qui renvoie min et max dans un vecteur nommé     
              
         minEtmaxN <- function(x) {     
           vn        <- c(min(x),max(x))     
           names(vn) <- c("min","max")     
           return( vn )     
         } # fin de fonction minEtmaxN     
              
         mmN   <- minEtmaxN(vect)     
         vmin2 <- mmN["min"] # pas besoin de se rappeler de l'indice     
              
         # une fonction qui renvoie min et max dans une liste     
              
         minEtmaxL <- function(x) {     
           lst     <- list(min=min(x),max=max(x))     
           return( lst )     
         } # fin de fonction minEtmaxL     
              
         mmL   <- minEtmaxL(vect)     
         vmin3 <- mmL$min     
              
              
              
    
    
              
         > # une fonction qui renvoie min et max dans un vecteur     
              
         > minEtmax <- function(x) {     
            return( c(min(x),max(x)) )     
         } # fin de fonction minEtmax     
              
              
         > # utilisation : il faut se rappeler de l'ordre de renvoi     
         >     
         > vect <- c(1,8,15,2,50,7)     
         > mm   <- minEtmax(vect)     
              
         > cat(" min et max de ",paste(vect,collapse=" "),":",paste(mm,collapse=" "),"\n")     
          min et max de  1 8 15 2 50 7 : 1 50     
              
         > vmin1 <- mm[1] # il faut se rappeler de l'indice 1     
              
         > # une fonction qui renvoie min et max dans un vecteur nommé     
         >     
         > minEtmaxN <- function(x) {     
            vn        <- c(min(x),max(x))     
            names(vn) <- c("min","max")     
            return( vn )     
         } # fin de fonction minEtmaxN     
              
         > mmN   <- minEtmaxN(vect)     
         > vmin2 <- mmN["min"] # pas besoin de se rappeler de l'indice     
              
         > # une fonction qui renvoie min et max dans une liste     
         >     
         > minEtmaxL <- function(x) {     
           lst     <- list(min=min(x),max=max(x))     
           return( lst )     
         } # fin de fonction minEtmaxL     
              
         > mmL   <- minEtmaxL(vect)     
         > vmin3 <- mmL$min     
              
    

    A l'usage, une liste avec noms se révèle être l'objet le plus simple et le plus souple à utiliser pour le renvoi des valeurs : on peut renvoyer des valeurs de nature différente, les objets renvoyés sont nommés...

    Quand on écrit des fonctions, il est tentant de les utiliser juste pour voir ce qu'elles font. Lorsqu'elles renvoient des valeurs, leur return(...) affiche en interactif souvent trop de valeurs. Utiliser return(invisible(...) permet de masquer l'affichage du return() s'il n'y a pas affectation.

    
         # cette fonction renvoie beaucoup de valeurs     
         # en interactif, vu() les affiche...     
              
         vu <- function() {     
           cat(" simulation (1)...\n") ;     
           return( runif(10) )     
         } # fin de fonction vu     
              
         # cette fonction renvoie beaucoup de valeurs     
         # en interactif, pasvu() n'affiche rien     
              
         pasvu <- function() {     
           cat(" simulation (2)...\n") ;     
           return( invisible(runif(10)) )     
         } # fin de fonction pasvu     
              
         # démonstration :     
              
         vu()     
         pasvu()     
              
         # les deux fonctions ont le même comportement     
         # en cas d'affectation     
              
         x1 <- vu()     
         x2 <- pasvu()     
              
    
    
              
         > # cette fonction renvoie beaucoup de valeurs     
         > # en interactif, vu() les affiche...     
         >     
         > vu <- function() {     
         +   cat(" simulation (1)...\n") ;     
         +   re .... [TRUNCATED]     
              
         > # cette fonction renvoie beaucoup de valeurs     
         > # en interactif, pasvu() n'affiche rien     
         >     
         > pasvu <- function() {     
         +   cat(" simulation (2)...\n") ;     
          .... [TRUNCATED]     
              
         > # démonstration :     
         >     
         > vu()     
          simulation (1)...     
          [1] 0.64978773 0.02535503 0.48913900 0.71141735 0.65846263 0.26194920 0.28039553 0.82349795 0.71213578 0.21841828     
              
         > pasvu()     
          simulation (2)...     
              
         > # les deux fonctions ont le même comportement     
         > # en cas d'affectation     
         >     
         > x1 <- vu()     
          simulation (1)...     
              
         > x2 <- pasvu()     
          simulation (2)...     
              
    
  10. Trois fonctions sont définies puis utilisées : f, g et h.

    f est définie avec une syntaxe courte, sans valeur par défaut. Utiliser f() provoquera une erreur : Erreur dans x * x : 'x' est manquant. Aucun test sur le type du seul argument x n'est effectué. Donc f(TRUE) renverra 1 et pour f("oui") on aura : Erreur dans x * x : argument non numérique pour un opérateur binaire. Si on tape juste f, R renvoie le code source de la fonction. f(3) renvoie le carré de 3, donc 9. f(1:5) renvoie les carrés respectifs des entiers de 1 à 5. Enfin, f(list(a=2,b=2)) provoquerait l'erreur : Erreur dans x * x : argument non numérique pour un opérateur binaire.

    g est définie avec NA comme valeur par défaut et teste si l'argument x est une liste. Dans ce cas, R applique la fonction carré à chacun des éléments de la liste à l'aide de lapply ; sinon, on renvoie le calcul de carre(x) -- s'il est valide. Si on tape juste g(), R passe dans le else et renvoie carre(NA) soit NA.

    h est définie avec deux paramètres (x et graphique) et l'ellipse. Si x est une liste, R applique la fonction carré à chacun des éléments de la liste à l'aide de lapply ; sinon, on met dans y le résultat de carre(x) et si graphique est mis à vrai, on trace y à l'aide des paramètres éventuels passé dans l'ellipse. Ainsi h(x=1:5, graphique=TRUE,col="red",pch=20 ) trace carre(1:5) en rouge avec des points pleins (pch=20).

    
         ------------------------------------------  calculs avec f     
         function(x) carre(x)     
              
         [1] 9     
         [1]  1  4  9 16 25     
         ------------------------------------------  calculs avec g     
         [1] NA     
         [1] 9     
         $a     
         [1] 4     
              
         $b     
         [1] 4     
         ------------------------------------------  calculs avec h     
              
         function(x=NA,graphique=FALSE,...) {     
            if (is.list(x)) {     
              return( lapply(x,carre)  )     
            } else {     
              y <- carre(x)     
              if (graphique) {     
                plot( y, ...)     
              } # fin de si sur graphique     
              return( y )     
            } # fin de si sur is.list     
         } # fin de fonction h     
              
         [1] NA     
         [1] 9     
         [1]  1  4  9 16 25     
         $a     
         [1] 4     
              
         $b     
         [1] 4     
              
         [1]  1  4  9 16 25     
         [1]  1  4  9 16 25     
         [1]  1  4  9 16 25     
              
    

    Voici les deux graphes produits :

    params_1.png

    params_2.png

    Pour la fonction h, si x n'est pas une liste, on renvoie son carré et, si x est une liste, on renvoie aussi son carré via apply.

    source() vient lire et exécuter le code source. Il y a de nombreuses options, voir par exemple notre introduction à R, à l'exercice 1 de la séance 2.

    sink() permet de rediriger l'affichage vers un fichier.

    sinksrc() réalise les deux opérations. On peut donc à la fois voir ce qui est exécuté et obtenir automatiquement un fichier "lst" de l'exécution comme en SAS. Le choix de .sor comme extension du fichier-texte d'exécution permet de laisser chaque utilisateur décider du logiciel qu'il veut associer à cette extension. Un choix astucieux peut être de prendre Firefox pour afficher...

  11. Les actions élémentaires de controle de flux se nomment if/else (alternative si/sinon), switch (structure de cas), for (boucle pour), while (boucle tant que), repeat (boucle répéter jusqu'à). La gestion des erreurs ou "exceptions" se fait avec try et tryCatch.

    Pour trouver le maximum d'un vecteur de valeurs, il suffit dans une boucle de comparer le maximum trouvé jusque là avec la valeur courante, d'où le code R

    
         # détermination de maxV, plus grand élément du vecteur V     
              
         nbe  <- length(V)     
         maxV <- V[1]  # il est prudent d'initialiser avec le premier élément de V     
         for (idc in (2:nbe)) {     
           eltc <- V[idc] # élément courant     
           if (eltc>maxV) {     
              maxV <- eltc     
           } # fin si     
         } # fin pour idc     
              
              
    

    Pour calculer le nombre d'occurences de ce maximum dans le vecteur, il suffit dans une boucle d'incrémenter le nombre d'occurrences à chaque fois que l'on trouve le maximum, d'où le code R

    
         # comptage du nombre d'occurence du plus grand élément du vecteur V     
         # sachant que ce plus grand élément est dans maxV     
              
         nbe  <- length(V) # nombre d'éléments     
         nbo  <- 0         # nombre d'occurences     
         for (idc in (1:nbe)) {     
           if (V[idc]==maxV) {     
              nbo <- nbo + 1     
           } # fin si     
         } # fin pour idc     
              
              
    

    Les codes R précédents utilisent deux boucles et sont donc deux fois plus lents qu'un seul parcours de boucle. Pour déterminer en une seule fois le maximum et son nombre d'occurence, il suffit de remarquer que pour chaque élément du vecteur il y a trois possibilités : pour une valeur inférieure au maximum, il n'y a rien à faire ; pour une valeur égale au maximum trouvé, il faut incrémenter le nombre d'occurences ; pour une valeur supérieure, il faut en faire le nouveau maximum et donc mettre le nombre d'ocurences à 1, d'où le code R

    
         # détermination de maxV, plus grand élément du vecteur V     
         # et comptage du nombre d'occurences (nbo) en une seule boucle     
              
         nbe  <- length(V)       # nombre d'élements     
         maxV <- V[1]            # maximum     
         nbo  <- 1               # nombre d'occurences     
              
         for (idc in (2:nbe)) {  # eltc = V[idc] est l'élément courant     
              
           eltc <- V[idc]     
           if (eltc>maxV) {      # nouveau maximum, donc une seule occurence     
              maxV <- eltc     
              nbo  <- 1     
           } else {     
             if (eltc==maxV) {   # une nouvelle occurence du maximum     
                nbo  <- nbo + 1     
             } # fin si numéro 2     
           } # fin si numéro 1     
              
         } # fin pour idc     
              
    

    La bonne solution consiste bien sur à profiter des opérations vectorielles de R et donc de ne pas écrire de boucle explicite :

    
         # détermination de maxV, plus grand élément du vecteur V     
         # et comptage du nombre d'occurences sans utiliser de boucle     
              
         maxV <- max(V)     
         nbo  <- sum( V==maxV )     
              
         # juste pour savoir : sum( V==max(V) )     
              
    
  12. Après quelques essais en mode interactif, il est possible de se rendre compte que :

    • pour appliquer ls() à un package, il faut que ce package soit chargé,

    • la fonction ls() renvoie un vecteur de chaines de caractères,

    • il peut arriver que certains objets aient plusieurs classes, comme par exemple l'objet DNase du package datasets.

    La syntaxe de ls() pour voir les objets du package XXX est ls("package:XXX"). Il faut donc construire avec paste() la chaine de caractères du paramètre avant de l'exécuter avec eval(). Une fonction que l'on définit a pour type closure et function comme classe. Pour les primitives R et les fonctions dans les packages, cela devient function ou, pour les méthodes S4, standardGeneric. Nous avons donc séparé l'affichage des fonctions et des objets d'une autre classe. Pour connaitre la classe de chaque objet, il faut exécuter la fonction class() sur chaque objet. Plutôt que d'évaluer l'appel de class, nous avons copié/créé l'objet en jdd à l'aide de la fonction get() avant de consulter la classe de jdd.

    La fonction cats() ne fait pas partie de R standard. C'est une de nos fonctions usuelles qui sert à souligner un texte pour plus de lisibilité.

    
         #################################################################     
              
         ls2 <- function(package="base") {     
              
         #################################################################     
              
         # affiche les tailles des objets disponibles dans le package via ls()     
              
         cats(paste("Objets du package",package))     
              
         # il faut d'abord charger le package     
              
         phr <- paste('ldp <- library("',package,'")',sep="")     
         ## pour debug : cat(" voici le code exécuté : *** ",phr," *** \n")     
         eval(parse(text=phr))     
              
         # si on ne trouve pas le nom du package dans ldp     
         # c'est qu'on n'a pas pas réussi à le charger     
              
         if (!package %in% ldp) {     
           cat("Package ",package," non chargé.\n")     
           return(invisible(NULL))     
         } # fin si     
              
         phr <- paste('oip <- ls("package:',package,'")',sep="")     
         ## pour debug : cat(" voici le code exécuté : *** ",phr," *** \n")     
         eval(parse(text=phr))     
              
         cat(" il y a ",length(oip)," objets dans ce package\n\n")     
              
         # préparation de la structure pour les résultats     
              
         mres <- matrix(NA,nrow=length(oip),ncol=3)     
         mres <- data.frame(mres)     
         colnames(mres)  <- c("name","functionClass","other")     
         row.names(mres) <- paste("obj_",sprintf("%03d",1:nrow(mres)),sep="")     
              
         # remplissage de la structure     
              
         nd <- 0     
         for (idd in oip) {     
           nd <- nd + 1     
           mres[nd,1] <- idd     
           jdd <- get(idd)     
           clj <- class(jdd)     
           if (length(clj)==1) {     
              if (class(jdd) %in% c("function","standardGeneric","closure")) {     
                mres[nd,2] <- clj     
              } else {     
                mres[nd,3] <- clj     
              } # fin si     
           } else {     
              mres[nd,3] <- paste(clj,collapse=" & ")     
           } # fin si     
         } # fin pour     
              
         mres[is.na(mres)] <- ""     
         print(mres,right=FALSE)     
              
         } # fin de fonction ls2     
              
         ############################################################     
              
         ls2() # équivalent à ls2("base")     
              
         ls2("stats")     
         ls2("utils")     
              
    
    
              
         Objets du package base     
         ======================     
              
          il y a  1167  objets dans ce package     
              
                  name                               functionClass   other     
         obj_001  ^                                  function     
         obj_002  ~                                  function     
         obj_003  <                                  function     
         obj_004  <<-                                function     
         obj_005  <=                                 function     
         obj_006  <-                                 function     
         obj_007  =                                  function     
         [...]     
              
              
         Objets du package stats     
         =======================     
              
          il y a  493  objets dans ce package     
              
                 name                 functionClass other     
         obj_001 acf                  function     
         obj_002 acf2AR               function     
         obj_003 add1                 function     
         obj_004 addmargins           function     
         obj_005 add.scope            function     
         obj_006 aggregate            function     
         obj_007 aggregate.data.frame function     
         obj_008 aggregate.default    function     
         [...]     
              
              
         Objets du package utils     
         =======================     
              
          il y a  198  objets dans ce package     
              
                 name                                  functionClass other     
         obj_001 ?                                     function     
         obj_002 adist                                 function     
         obj_003 alarm                                 function     
         obj_004 apropos                               function     
         obj_005 aregexec                              function     
         obj_006 argsAnywhere                          function     
         [...]     
              
              
    

    Il y a une petite difficulté avec la fonction data() : elle fait une évaluation "paresseuse", au sens informatique de lazyLoad(). Il faut donc exécuter la fonction data() sur chaque objet avant de pouvoir consulter les dimensions de l'objet. Voici quelques essais pour comprendre comment utiliser l'objet renvoyé par data() :

    
         # chargeons les données du package     
              
         lesd <- data(package="datasets")     
              
         # essayons de voir ce qu'on y trouve     
              
         print( class(lesd) )     
         print( str(lesd) )     
         print( lapply(X=lesd,FUN=head) )     
              
         # ce qui nous intéresse est donc dans la rubrique results     
              
         resd <- lesd$results     
         print( class(resd) )     
         print( colnames(resd) )     
              
         # il faut exploiter la colonne Item, mais il y a parfois plusieurs mots     
              
         lesi <- resd[,"Item"]     
         print(cbind(head(lesi)))     
              
         # quelques essais :     
              
         data("AirPassengers",package="datasets")     
         print(class(AirPassengers))     
              
         data("BOD",package="datasets")     
         print(class(BOD))     
              
              
              
    
    
              
         > # chargeons les données du package     
         >     
         > lesd <- data(package="datasets")     
              
         > # essayons de voir ce qu'on y trouve     
         >     
         > print( class(lesd) )     
         [1] "packageIQR"     
              
         > print( str(lesd) )     
         List of 4     
          $ title  : chr "Data sets"     
          $ header : NULL     
          $ results: chr [1:206, 1:4] "datasets" "datasets" "datasets" "datasets" ...     
           ..- attr(*, "dimnames")=List of 2     
           .. ..$ : NULL     
           .. ..$ : chr [1:4] "Package" "LibPath" "Item" "Title"     
          $ footer : NULL     
          - attr(*, "class")= chr "packageIQR"     
         NULL     
              
         > print( lapply(X=lesd,FUN=head) )     
         $title     
         [1] "Data sets"     
              
         $header     
         NULL     
              
         $results     
              Package    LibPath              Item                     Title     
         [1,] "datasets" "/usr/lib/R/library" "AirPassengers"          "Monthly Airline Passenger Numbers 1949-1960"     
         [2,] "datasets" "/usr/lib/R/library" "BJsales"                "Sales Data with Leading Indicator"     
         [3,] "datasets" "/usr/lib/R/library" "BJsales.lead (BJsales)" "Sales Data with Leading Indicator"     
         [4,] "datasets" "/usr/lib/R/library" "BOD"                    "Biochemical Oxygen Demand"     
         [5,] "datasets" "/usr/lib/R/library" "CO2"                    "Carbon Dioxide Uptake in Grass Plants"     
         [6,] "datasets" "/usr/lib/R/library" "ChickWeight"            "Weight versus age of chicks on different diets"     
              
         $footer     
         NULL     
              
              
         > # ce qui nous intéresse est donc dans la rubrique results     
         >     
         > resd <- lesd$results     
              
         > print( class(resd) )     
         [1] "matrix"     
              
         > print( colnames(resd) )     
         [1] "Package" "LibPath" "Item"    "Title"     
              
         > # il faut exploiter la colonne Item, mais il y a parfois plusieurs mots     
         >     
         > lesi <- resd[,"Item"]     
              
         > print(cbind(head(lesi)))     
              [,1]     
         [1,] "AirPassengers"     
         [2,] "BJsales"     
         [3,] "BJsales.lead (BJsales)"     
         [4,] "BOD"     
         [5,] "CO2"     
         [6,] "ChickWeight"     
              
         > # quelques essais :     
         >     
         > data("AirPassengers",package="datasets")     
              
         > print(class(AirPassengers))     
         [1] "ts"     
              
         > data("BOD",package="datasets")     
              
         > print(class(BOD))     
         [1] "data.frame"     
              
    

    Pour construire notre fonction data2() il faut donc charger le package passé en paramètre. L'objet renvoyé est de classe packageIQR, ce qui ne semble pas très facile à utiliser, sauf qu'il s'agit d'un objet qui est une liste. La composante results contient les objets à interroger. Il s'agit d'une matrice dont la colonne "Item" contient le nom de l'objet ou des objets qui nous intéressent. Un test sur la classe pour savoir s'il y a une longueur ou des lignes et des colonnes et le tour est joué...

    
         #################################################################     
              
         data2 <- function(package="datasets") {     
              
         #################################################################     
              
         # affiche les tailles des données disponibles via data()     
              
         cats(paste("Jeux de données du package",package))     
              
         dfd  <- data(package=package)     
         ndd  <- dfd$results[,"Item"]     
              
         if (length(ndd)==0) {     
           cat("\n aucun jeu de données vu dans ce package.\n\n")     
         } else {     
              
              cat("\n il y  a",length(ndd),"jeu de données dans ce package.\n\n")     
              
              # préparation de la matrice des résultats     
              
              mres <- matrix(nrow=length(ndd),ncol=6)     
              mres <- data.frame(mres)     
              colnames(mres)  <- c("name","class","length","nrow","ncol","name2")     
              row.names(mres) <- paste("data_",sprintf("%03d",1:nrow(mres)),sep="")     
              
              nd <- 0     
              for (idd in ndd) {     
                nd <- nd + 1     
                jdd <- strsplit(x=idd,split=" ")[[1]][1] # le premier  mot     
                kdd <- strsplit(x=idd,split=" ")[[1]][2] # le deuxieme mot avec des parenthes eventuelles     
                kdd <- gsub("[()]","",kdd)     
                if (is.na(kdd)) {     
                   phr <- paste(" data(",jdd,",package='",package,"')",sep="")     
                } else {     
                  phr <- paste(" data(",kdd,",package='",package,"')",sep="")     
                } # finsi     
                ## pour debug : cat(" voici le code exécuté : *** ",phr," *** \n")     
                eval(parse(text=phr))     
                ddd <- get(jdd)     
                mres[nd,2] <- paste(class(ddd),collapse=" ")     
                if (!is.null(length(ddd))) {     
                   mres[nd,3] <- length(ddd)     
                   if (length(class(ddd))==1) {     
                      if (class(ddd)=="matrix")     { mres[nd,3] <- NA }     
                      if (class(ddd)=="data.frame") { mres[nd,3] <- NA }     
                   } # finsi     
                } # finsi     
                if (!is.null(nrow(ddd))) {     
                   mres[nd,4] <- nrow(ddd)     
                } # finsi     
                if (!is.null(ncol(ddd))) {     
                   mres[nd,5] <- ncol(ddd)     
                } # finsi     
                mres[nd,6] <- kdd     
                mres[nd,1] <- jdd     
              } # fin pour     
              
              # on trie par nom de jeu de données     
              
              idx <- order(toupper(row.names(mres)))     
              mres <- mres[idx,]     
              mres[is.na(mres)] <- ""     
              
              # on reformate les chaines à gauche     
              
              lm1      <- max(nchar(mres[,1]))     
              mres[,1] <- substr(paste(mres[,1],copies("_",lm1)),1,lm1)     
              lm2      <- max(nchar(mres[,2]))     
              mres[,2] <- substr(paste(mres[,2],copies("_",lm2)),1,lm2)     
              lm6      <- max(nchar(mres[,6]))     
              mres[,6] <- substr(paste(mres[,6],copies("_",lm6)),1,lm6)     
              
              print(mres)     
              
         } # fin si     
              
         } # fin de fonction data2     
              
         ############################################################     
              
         data2() ; # équivalent à data2("datasets") ;     
              
         data2("ade4") ;     
              
    
    
              
         Jeux de données du package datasets     
         ===================================     
              
          il y  a 206 jeu de données dans ce package.     
              
              
                                   name                                               class length nrow ncol        name2     
         data_001 AirPassengers _______ ts ________________________________________________    144            ___________     
         data_002 BJsales _____________ ts ________________________________________________    150            ___________     
         data_003 BJsales.lead ________ ts ________________________________________________    150           BJsales ____     
         data_004 BOD _________________ data.frame ________________________________________           6    2  ___________     
         data_005 CO2 _________________ nfnGroupedData nfGroupedData groupedData data.frame      5   84    5  ___________     
         data_006 ChickWeight _________ nfnGroupedData nfGroupedData groupedData data.frame      4  578    4  ___________     
         data_007 DNase _______________ nfnGroupedData nfGroupedData groupedData data.frame      3  176    3  ___________     
         data_008 EuStockMarkets ______ mts ts matrix _____________________________________   7440 1860    4  ___________     
         [...]     
              
              
         Jeux de données du package ade4     
         ===============================     
              
              
          il y  a 105 jeu de données dans ce package.     
              
                           name      class length nrow ncol name2     
         data_001 abouheif.eg _ list _____      6     
         data_002 acacia ______ data.frame          32   15     
         data_003 aminoacyl ___ list _____      5     
         data_004 apis108 _____ data.frame         180   10     
         data_005 ardeche _____ list _____      6     
         data_006 arrival _____ list _____      2     
         [...]     
              
    

    Les listings complets pour la liste des objets et des données sont ici et .

  13. Pour choisir entre plusieurs solutions R, il est en général de conseillé de préférer les solutions les plus rapides à l'exécution tout en gardant un bon niveau de lisibilité. Les solutions les plus courtes à écrire ne sont donc pas forcément les meilleures. Les opérations vectorielles de base, souvent programmées en C, sont en général plus rapides que des boucles explicites. Une bonne pratique consiste à optimiser les appels par référence, à initialiser les vecteurs et les matrices au lieu de les augmenter dynamiquement, et à utiliser une fonction duree() pour essayer de voir ce qui ralentit l'exécution. Dans la série d'exercices numéro 4, on verra comment "profiler" les appels internes de fonction. Le code suivant utilise une seule fois V[idc] :

    
         # détermination de maxV, plus grand élément du vecteur V     
         # et comptage du nombre d'occurences (nbo) en une seule boucle     
              
         nbe  <- length(V)       # nombre d'élements     
         maxV <- V[1]            # maximum     
         nbo  <- 1               # nombre d'occurences     
              
         for (idc in (2:nbe)) {  # eltc = V[idc] est l'élément courant     
              
           eltc <- V[idc]     
           if (eltc>maxV) {      # nouveau maximum, donc une seule occurence     
              maxV <- eltc     
              nbo  <- 1     
           } else {     
             if (eltc==maxV) {   # une nouvelle occurence du maximum     
                nbo  <- nbo + 1     
             } # fin si numéro 2     
           } # fin si numéro 1     
              
         } # fin pour idc     
              
    

    alors que le code suivant utilise trois fois V[idc], ce qui peut ralentir l'exécution, surtout si on utilise une expression au lieu d'un simple indice comme idc :

    
         # détermination de maxV, plus grand élément du vecteur V     
         # et comptage du nombre d'occurences en une seule boucle     
              
         nbe  <- length(V)  # nombre d'élements     
         maxV <- V[1]       # maximum     
         nbo  <- 1          # nombre d'occurences     
         for (idc in (2:nbe)) {     
           if (V[idc]>maxV) {     
              maxV <- V[idc]     
              nbo  <- 1     
           } else {     
             if (V[idc]==maxV) {     
                nbo  <- nbo + 1     
             } # fin si numéro 2     
           } # fin si numéro 1     
         } # fin pour idc     
              
              
    

    Concernant la durée d'exécution, il y a en R une fonction nommée Sys.time(). Il suffit donc de trouver comment l'appeler avant et après l'exécution du code à tester pour connaitre le temps d'éxcéution du code. Voici un exemple de fonction duree() et un exemple ce qu'il ne faut pas faire, via la fonction bad() et ce qu'il faut faut faire avec la fonction good(). On notera l'ellipse comme seul paramètre de cette fonction duree() :

    
         # exemple de fonction duree     
              
         duree <- function(...) {     
           tempsDeb <- Sys.time()     
           eval.parent(...)     
           tempsFin <- Sys.time()     
           cat("durée = ",tempsFin-tempsDeb,"s\n")     
         } # fin de fonction duree     
              
         # une fonction lente     
              
         bad <- function(n) {     
           v <- c()     
           i <- 1     
           while (i<=n) {     
             v <- c(v,i)     
             i <- i + 1     
           } # fin tant que     
           return(v)     
         } # fin de fonction bad     
              
         # une fonction rapide     
              
         good <- function(n) {     
           return(1:n)     
         } # fin de fonction good     
              
         # vérifions les temps d'exécutions :     
              
         nbVal <- 3*10**4     
              
         duree( good(nbVal) )  # moins de 0.001 secondes     
         duree( bad(nbVal) )   # environ 2.9 secondes     
              
         # solution intermédiaire     
              
         entre <- function(n) {     
           vect  <- rep(NA,n)     
           for (idv in 1:n) {     
             vect[idv] <- idv     
           } # fin de pour     
           return(vect)     
         } # fin de fonction entre     
              
         duree( entre(nbVal) )   # environ  0.13 secondes     
              
    
    
              
         durée =  7.843971e-05 s     
         durée =  2.83047 s     
         durée =  0.1272614 s     
              
    

    Une bonne pratique est souvent de prévoir la dimension des résultats, de construire une structure adaptée et de remplir cette structure, comme nous avons fait avec les fonctions ls2() et data2() car R alloue alors la mémoire en début de script. Modifier une structure en cours de script oblige R à effectuer une copie complète de la structure en mémoire, ce qui peut se révéler long si la structure est importante. A l'exercice 4 de la séance 2 nous le démontrerons avec des temps d'exécution dans un rapport de 1 à 10.

  14. En fait, il y a de nombreux ouvrages sur R, sur l'initiation à R pour les statistiques. Par contre peu d'ouvrages récents sont complets car R comporte beaucoup de fonctions, même si on se restreint aux packages de base.

    
         #  nombre d'objets dans les packages     
              
         cat(" il y a ",length(ls("package:base"))," objets dans le package base\n")     
              
         # il faut charger les packages avant de pouvoir utiliser ls  sur ces packages     
         # sauf pour base et utils qui sont chargés automatiquement     
              
         library(gdata)     
         library(tools)     
              
              
         opg <- length(ls("package:gdata"))     
         opt <- length(ls("package:tools"))     
         opu <- length(ls("package:utils"))     
              
         # on peut aussi nommer les composantes des vecteurs     
              
         cats("pour les autres packages :")     
         nbo <- c(opg,opt,opu)     
         names(nbo) <- c("gdata","tools","utils")     
         print(nbo)     
              
              
    
    
          il y a  1167  objets dans le package base     
              
         pour les autres packages :      
         ==========================      
              
         gdata tools utils      
            64    97   198      
              
    

    On trouvera dans le tableau suivant des liens vers la liste des objets de ces packages.

    Package Nb_objets Lien
    base 1167  lls_base.sor
    gdata 64  lls_gdata.sor
    tools 97  lls_tools.sor
    utils 198  lls_utils.sor

    On peut toutefois citer comme ouvrages assez exhaustifs et dédiés plus à R qu'aux statistiques :

    En ce qui concerne la programmation, il y a peu d'ouvrages dédiés, sauf les ouvrages de Matloff et Gentleman :

    Enfin, de nombreux ouvrages donnent des exemples généraux de calculs statistiques avec des programmes R, comme :

    non su non su
    P. Teetor J. Adler
    non su non su
    N. Matloff R. Gentleman
    non su non su
    Y. Cohen B. Everitt

    Pour finir, en français, nous conseillons les livres de Cornillon et Husson, la série "Pratique R" chez Springer, et «bien sûr» R, l'essentiel, la traduction de R in a nutshell aux éditions Pearson :

    non su non su non su non su non su non su
    Cornillon et al. Husson et al. Adler Cornillon, Matzner-Lober Robert, Casella Aragon
  15. Comme site en français, un bon choix nous semble celui d'Aymeric Duclert à l'adresse :

              http://www.duclert.org/Aide-memoire-R/Le-langage/Introduction.php

    Toujours en français, le site abcdr allstat pourrait être intéressant s'il était plus fourni. La page de Ricco Rakotomalala contient de nombreux liens avec des commentaires. Et bien sûr, le site ADE4 du PBIL est une mine de renseignements avec ses cours et TD pour R. Enfin, le site zoonek est assez intéressant dans la mesure où il présente aussi des commandes Unix pour les fichiers R, mais il n'est plus maintenu depuis 2004.

    En anglais, il y a sans doute une petite dizaine de sites à connaitre. Le site ucr de l'université de Californie a de nombreuses rubriques (parcourir les onglets) sur R pour la bioinformatique. finzi est une référence majeure pour chercher des informations sur R. Quick-R est sobre et assez pratique. Enfin, le site crantastic contient des informations intéressantes sur les packages.

    Comme vidéos, nous conseillons en francais celles de Lausanne et en anglais german accent (!), celles de Revolution ,ramstatvid et les vidéos qui accompagnent celles de R. Brown.

    Lorsqu'on cherche des fonctions R ou des packages R, à part le CRAN et ses "taskviews", le meilleur site de recherche est sans doute R site search.

    Enfin, le site de Bioconductor est bien sûr incontournable pour la bioinformatique, dès qu'il s'agit de génomique ou de puces à ADN ou de NGS.

 

 

retour gH    Retour à la page principale de   (gH)