Introduction à la programmation avec R
gilles.hunault "at" univ-angers.fr
Cours 6 - Eviter de programmer en R
Table des matières cliquable
1. Via les fonctions de base déjà existantes
3. Eviter de programmer des boucles avec la famille *apply*
Exercices : énoncés solutions [Retour à la page principale du cours]1. Via les fonctions de base déjà existantes
Lorsqu'on débute en programmation, il est bon d'apprendre à savoir tout faire, tout calculer. Par exemple pour les statistiques usuelles, il faut savoir effectuer un calcul de moyenne, de médiane, d'écart-type. Il faut avoir écrit au moins une fois dans sa vie la recherche du minimum, du maximum dans un vecteur, avec leur nombre d'ocurrences et leurs positions pour commencer à savoir programmer.
Savoir programmer, c'est ensuite savoir utiliser les programmes des autres, donc ne pas réinventer la roue, l'eau tiède, etc. R dispose de nombreuses fonctions de base en standard, via les packages base, stats, graphics et utils :
package nbObjets liste base 1167 lls_base.sor stats 493 lls_stats.sor graphics 87 lls_graphics.sor utils 198 lls_utils.sor Apprendre à programmer en R consiste donc à passer beaucoup de temps, au moins au début, pour savoir ce que R sait déjà faire -- et il en fait déjà beaucoup. Car tous les traitements de données usuels (transformations, recodages, discrétisation, fusion...) ont déjà été passés en revue, analysés, programmés, parfois de façon très sophistiquée. On pourra consulter les fonctions transform() et stack() pour s'en rendre compte.
De même, puisque R est un logiciel pour les calculs statistiques, tous les calculs usuels et même les calculs récents en statistique, bioinformatique, etc. sont déjà implémentés en R. Une grande conclusion est qu'il faut passer du temps avant de «maitriser la bête». L'expérience prouve qu'on y gagne en vitesse de développement et en compréhension du fonctionnement du langage R.
Soyons clairs : sauf si vous inventez une nouvelle méthode de calcul en statistiques, vous n'avez pas à programmer le moindre calcul statistique en R. Vous avez à lire les données, appeler les fonctions de R qui calculent, vous avez à mettre en forme les résultats, mais vous n'avez pas à programmer le moindre calcul statistique en R (redite volontaire).
Pour mémoire, le site rdocumentation recense plus de 2 millions de fonctions.
2. Via les packages
Plus encore que les deux mille et quelques fonctions des quatre packages de la section précédente, c'est la foultitude des packages spécialisés qui fait la richesse de R et qui le rend incomparable pour les calculs et les graphiques statistiques.
Même si d'autres langages peuvent être considérés comme "plus beaux", "plus propres", "plus efficaces", disons comme Ruby, Python, Java, C..., aucun autre langage de programmation n'est aussi complet dès qu'il s'agit de graphiques et de calculs statistiques. La preuve : ces langages ont préféré développer des interfaces de dialogue avec R plutôt que de réimplémenter les mêmes calculs et graphiques. C'est ainsi qu'on trouve rpy, rinruby, rJava, Rcpp...
Il suffit de consulter chaque jour la liste des packages ajoutés quotidiennement pour constater que le nombre de packages grandit vraiment très vite et qu'aucun autre langage ne croit aussi vite...
Même dans certains domaines où on pourrait ne pas l'y attendre, R se révéle performant grâce à son aspect vectoriel. Le package XML et le livre associé en sont un très bon exemple.
Au passage, l'un des points forts de R est la mise en forme ou la restructuration des données. On pourra consulter les packages plyr et reshape2 pour s'en rendre compte
3. Eviter de programmer des boucles avec la famille *apply*
Programmer des calculs, des affichages revient souvent à appliquer un même traitement à de nombreux "objets". Dans la mesure où R distingue les vecteurs, les matrices et les listes (dont les data frames), il n'est pas étonnant que ce mot appliquer se décline en plusieurs versions suivant les structures de données :
> cbind(apropos("apply")) [1,] "apply" [2,] "dendrapply" [3,] "eapply" [4,] "kernapply" [5,] "lapply" [6,] "mapply" [7,] ".mapply" [8,] "rapply" [9,] "sapply" [10,] "tapply" [11,] "vapply"La fonction apply() utilise des objets "matrice". Sa syntaxe de base utilise les paramètres X qui est la matrice ou un objet assimilable à une matrice, FUN la fonction à appliquer, qu'elle soit nommée ou anonyme et MARGIN pour préciser si on applique la fonction dans le sens des lignes (MARGIN=1) ou des colonnes (MARGIN=2).
La fonction lapply() utilise une liste nommée X et lui applique la fonction FUN élément par élément. L'objet renvoyé est une liste.
La fonction sapply() exécute lapply() et en simplifie la sortie pour renvoyer un vecteur ou une matrice. sapply utilise donc les mêmes paramètres que lapply.
La fonction rapply() exécute récursivement son paramètre f sur son autre paramètre object, ce que ne savent pas faire les fonctions précédentes.
La fonction tapply() permet de découper le paramètre X selon le paramètre INDEX avant d'appliquer son paramètre FUN à chaque découpage obtenu. C'est donc en quelque sorte l'enchainement de split() et lapply().
La fonction vapply permet de préciser à sapply quel type de données on obtiendra en sortie.
La fonction mapply() travaille en "multivarié".
Notre cours 1 de programmation R avancée, exercice 5 et sa solution montre par l'exemple comment utiliser ces fonctions.
4. Apprendre les actions courantes en R
S'il n'est pas possible de tout apprendre en R, il est facile de prévoir un planning et un ordre pour apprendre les actions courantes en R :
On commence presque toujours par lire des données. Donc tout ce qui se nomme read.* est bon à prendre et à apprendre, dont la lecture des fichiers Excel, des fichiers PDF ou autres fichiers XML ou Fasta...Si on ne lit pas des fichiers, c'est qu'on lit des bases de données ou qu'on simule des données. Là encore les packages de R sont faciles à trouver qui remplissent ces tâches, par exemple avec R Site Search...
> apropos("read") readBin readChar readCitationFile read.csv read.csv2 read.dcf read.delim read.delim2 read.DIF read.fortran read.ftable read.fwf readline readLines readRDS .readRDS readRenviron read.socket read.table Sys.readlink > ls("package:gdata") aggregate.table ans Args as.levelsMap as.listLevelsMap as.object_size bindData case cbindX centerText combine ConvertMedUnits drop.levels duplicated2 elem env frameApply getDay getHour getMin getMonth getSec getYear humanReadable installXLSXsupport interleave is.levelsMap is.listLevelsMap is.object_size isUnknown is.what keep ll lowerTriangle lowerTriangle<- ls.funs mapLevels mapLevels<- matchcols NAToUnknown nobs nPairs object.size read.xls remove.vars rename.vars reorder.factor resample sheetCount sheetNames startsWith trim trimSum unknownToNA unmatrix upperTriangle upperTriangle<- wideByFactor write.fwf xls2csv xls2sep xls2tab xls2tsv xlsFormatsAvec des données, on effectue des calculs, on produit des graphiques. Les task views du CRAN permettent de s'y retrouver. Pour celles et ceux qui travaillent dans le domaine de la bioinformatique, on rajoutera les pages Explore packages et help de bioconductor.
Une fois maitrisés les calculs élémentaires, vous pouvez aborder les modélisations classiques qui reposent sur le modèle linéaire (soit la fonction lm()) et sa généralisation (soit la fonction glm()) soit basculer vers le non linéaire, par exemple avec la fonction nlm() ou des fonctions comme loess() ou lowess().
> apropos("lm") allQLm .__C__anova.glm .__C__anova.glm.null .__C__glm .__C__glm.null .__C__lm .__C__mlm colMaxs colMeans .colMeans colMedians colMins confint.lm contr.helmert .__C__optionalMethod dummy.coef.lm getAllMethods glm glm.control glm.fit KalmanForecast KalmanLike KalmanRun KalmanSmooth kappa.lm lm .lm.fit lm.fit lm.influence lm.wfit modelesCLMMetavir model.matrix.lm nlm nlminb predict.glm predict.lm residuals.glm residuals.lm summary.glm summary.lm > apropos("ova") anova .__C__anova .__C__anova.glm .__C__anova.glm.null manova power.anova.test stat.anova summary.manovaPour les tests, R implémente "tout ce qui bouge" et plus si affinités, ce qui se nomme plus ou moins *.test() ou anova() :
> apropos("test") "ansari.test" "bartlett.test" "binom.test" "Box.test" "chisq.test" "cor.test" "file_test" "fisher.test" "fligner.test" "friedman.test" "kruskal.test" "ks.test" "mantelhaen.test" "mauchly.test" "mcnemar.test" "mood.test" "oneway.test" "pairwise.prop.test" "pairwise.t.test" "pairwise.wilcox.test" "poisson.test" "power.anova.test" "power.prop.test" "power.t.test" "PP.test" "prop.test" "prop.trend.test" "quade.test" "shapiro.test" "testInheritedMethods" "testPlatformEquivalence" "testVirtual" "t.test" ".valueClassTest" "var.test" "wilcox.test" > apropos("anova") "anova" ".__C__anova" ".__C__anova.glm" ".__C__anova.glm.null" "manova" "power.anova.test" "stat.anova" "summary.manova"Il faut ensuite mettre en forme les calculs, produire des rapports, intégrer les graphiques, exporter vers des formats classiques comme les précédents (Excel, PDF, base de données, XML...). Donc il faut approfondir tout ce qui se nomme write.* et tout ce qui se trouve dans le package gdata.
> apropos("write") aspell_write_personal_dictionary_file .rs.defaultLibPathIsWriteable .rs.ensureWriteableUserLibrary .rs.isLibraryWriteable .rs.writeableLibraryPaths RtangleWritedoc RweaveLatexWritedoc write writeBin writeChar write.csv write.csv2 write.dcf write.ftable writeLines write.socket write.table > library(XML) > ls("package:XML") addAttributes addChildren addNode addSibling append.xmlNode append.XMLNode asXMLNode asXMLTreeNode catalogAdd catalogClearTable catalogDump catalogLoad catalogResolve coerce comment.SAX COMPACT compareXMLDocs docName docName<- Doctype DTDATTR dtdElement dtdElementValidEntry dtdEntity dtdIsAttribute DTDLOAD DTDVALID dtdValidElement endElement.SAX ensureNamespace entityDeclaration.SAX findXInclude free genericSAXHandlers getChildrenStrings getDefaultNamespace getEncoding getHTMLExternalFiles getHTMLLinks getLineNumber getNodeLocation getNodePosition getNodeSet getRelativeURL getSibling getXIncludes getXMLErrors htmlParse htmlTreeParse HUGE isXMLString libxmlFeatures libxmlVersion makeClassTemplate matchNamespaces names.XMLNode newHTMLDoc newXMLCDataNode newXMLCommentNode newXMLDoc newXMLDTDNode newXMLNamespace newXMLNode newXMLPINode newXMLTextNode NOBASEFIX NOBLANKS NOCDATA NODICT NOENT NOERROR NONET NOWARNING NOXINCNODE NSCLEAN OLD10 OLDSAX parseDTD parseURI parseXMLAndAdd PEDANTIC processingInstruction.SAX processXInclude readHTMLList readHTMLTable readKeyValueDB readSolrDoc RECOVER removeAttributes removeChildren removeNodes removeXMLNamespaces replaceNodes saveXML SAX1 schemaValidationErrorHandler setXMLNamespace show source startElement.SAX supportsExpat supportsLibxml text.SAX toHTML toString.XMLNode XINCLUDE xml xmlAncestors xmlApply xmlAttributeType xmlAttrs xmlAttrs<- xmlCDataNode xmlChildren xmlChildren<- xmlCleanNamespaces xmlClone xmlCodeFile xmlCommentNode xmlContainsElement xmlContainsEntity xmlDeserializeHook xmlDoc xmlDOMApply xmlElementsByTagName xmlElementSummary xmlErrorCumulator xmlEventHandler xmlEventParse xmlGetAttr xmlHandler xmlHashTree xmlInternalTreeParse xmlName xmlName<- xmlNamespace xmlNamespace<- xmlNamespaceDefinitions xmlNamespaces xmlNamespaces<- xmlNativeTreeParse xmlNode xmlOutputBuffer xmlOutputDOM xmlParent xmlParent<- xmlParse xmlParseDoc xmlParserContextFunction xmlParseString xmlPINode xmlRoot xmlSApply xmlSchemaParse xmlSchemaValidate xmlSearchNs xmlSerializeHook xmlSize xmlSize.default xmlSource xmlSourceFunctions xmlSourceSection xmlStopParser xmlStructuredStop xmlTextNode xmlToDataFrame xmlToList xmlToS4 xmlTree xmlTreeParse xmlValue xmlValue<- xmlXIncludes xpathApply xpathSApply5. Comment survivre à R ?
Malheureusement, la richesse de R en fait aussi son plus gros défaut. Il faut apprendre une grosse centaine de fonctions (et leurs paramètres) avant de commencer à écrire des programmes concis, il faut désapprendre les boucles dans un certain nombre de cas avant de bien profiter des aspects vectoriels de R, il faut être à l'aise avec les méthodes statistiques pour s'y retrouver facilement dans les packages et les options implémentées, bref ce n'est pas une mince affaire que de bien programmer en R.
Heureusement, avec «du coeur à l'ouvrage» et de nombreuses heures d'entrainement, on y arrive toujours !
Exercices : énoncés solutions [Retour à la page principale du cours]
Retour à la page principale de (gH)