Programmer sous R - V1.2 - TONIC

Transcription

Programmer sous R - V1.2 - TONIC
Programmer sous R
Version 1.2 (mars 2013)
F. Aubry, INSERM U825, Toulouse
Table des matières
INSTALLATION ..................................................................................................................... 5
CHOIX DU SITE MIROIR ............................................................................................................ 5
AIDE EN LIGNE ........................................................................................................................ 5
CHOIX DE L’ÉDITEUR EN LIGNE ............................................................................................... 6
MISE À JOUR ............................................................................................................................ 6
Chargement des packages .................................................................................................. 6
Activation des packages ..................................................................................................... 7
Mise à jour des packages ................................................................................................... 7
Mise à jour des versions de R............................................................................................. 7
R COMMANDER ....................................................................................................................... 8
SAUVEGARDE ET RÉCUPÉRATION DES SESSIONS ...................................................................... 8
FONCTIONS ET MÉTHODES, 1ÈRE PARTIE .................................................................... 9
ACCÈS À L’AIDE EN LIGNE ....................................................................................................... 9
ARGUMENTS DES FONCTIONS ................................................................................................ 10
LES MÉTHODES ...................................................................................................................... 10
LES TYPES DE DONNÉES.................................................................................................. 12
FONCTIONS GÉNÉRIQUES ET VALEURS SPÉCIALES .................................................................. 12
LES TYPES ATOMIQUES .......................................................................................................... 13
Nombres ........................................................................................................................... 13
Chaînes de caractères ...................................................................................................... 14
Les logiques ou booléens.................................................................................................. 15
VECTEURS ............................................................................................................................. 16
Constructeur ..................................................................................................................... 16
Accès aux éléments ........................................................................................................... 17
Opérations sur les vecteurs .............................................................................................. 18
Quelques fonctions utilitaires........................................................................................... 18
FACTEURS ............................................................................................................................. 19
Constructeur ..................................................................................................................... 20
Contrastes......................................................................................................................... 21
Utilitaires ......................................................................................................................... 25
MATRICES ............................................................................................................................. 25
Constructeur ..................................................................................................................... 25
Sélectionner des éléments d’une matrice ......................................................................... 25
Opérations ........................................................................................................................ 26
Utilitaires ......................................................................................................................... 27
SÉRIES TEMPORELLES ............................................................................................................ 28
TABLES ................................................................................................................................. 28
LISTES ................................................................................................................................... 28
Constructeur ..................................................................................................................... 28
Accès aux éléments de la liste .......................................................................................... 29
Utilitaires ......................................................................................................................... 29
DATE ..................................................................................................................................... 30
DATA FRAME ......................................................................................................................... 30
Construction et accès aux éléments ................................................................................. 30
Facteurs ............................................................................................................................ 30
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 2
Utilitaires ......................................................................................................................... 31
Jointure de deux data.frame ............................................................................................. 33
FORMULE .............................................................................................................................. 35
Construction de la partie gauche ..................................................................................... 36
Construction générale de la partie droite ........................................................................ 36
Autres arguments.............................................................................................................. 38
Utilitaires ......................................................................................................................... 39
SCRIPTS ................................................................................................................................. 41
CHARGER DU CODE ............................................................................................................... 41
AFFECTATION D’UNE VALEUR À UNE VARIABLE .................................................................... 41
STRUCTURES DE CONTRÔLE .................................................................................................. 42
Tests .................................................................................................................................. 42
Boucles ............................................................................................................................. 43
Fonctions d’itérations ...................................................................................................... 44
Gestion des erreurs .......................................................................................................... 46
VISUALISER LES VALEURS ..................................................................................................... 47
Généralités ....................................................................................................................... 47
Visualiser à la console ..................................................................................................... 47
Imprimer sur la console ................................................................................................... 48
GESTION DES FICHIERS, LIRE ET ÉCRIRE DES DONNÉES SUR DISQUE ....................................... 48
Système de fichiers ........................................................................................................... 48
Accès aux fichiers de données .......................................................................................... 49
FONCTIONS : MANIPULER ET CONTRÔLER LES ARGUMENTS ................................................... 52
Définition de la fonction ................................................................................................... 52
L’argument spécial ... ....................................................................................................... 53
Contrôler la valeur des arguments................................................................................... 54
Gérer des fonctions comme argument .............................................................................. 56
Créer de nouveaux opérateurs binaires ........................................................................... 57
OPTIONS ................................................................................................................................ 59
MISE AU POINT DU SCRIPT ..................................................................................................... 60
Gestion générale .............................................................................................................. 60
Diagnostic de sortie en erreur ......................................................................................... 60
Diagnostic en ligne........................................................................................................... 60
LES DISTRIBUTIONS.......................................................................................................... 62
LES GRAPHIQUES .............................................................................................................. 63
LES FENÊTRES GRAPHIQUES .................................................................................................. 63
LES GRAPHIQUES DE BASE ..................................................................................................... 63
LE PACKAGE LATTICE ............................................................................................................ 64
EXERCICES RÉCAPITULATIFS ...................................................................................... 65
EXERCICE RÉCAPITULATIF I................................................................................................... 65
EXERCICE RÉCAPITULATIF II : OPÉRATIONS SUR DES MATRICES CREUSES ............................. 66
SOLUTION DES EXERCICES ............................................................................................ 67
EXERCICE PAGE 14 : .............................................................................................................. 67
EXERCICE PAGE 26 : .............................................................................................................. 67
EXERCICE PAGE 31 : .............................................................................................................. 69
EXERCICE PAGE 36 : .............................................................................................................. 69
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 3
EXERCICE PAGES 43 ET 44 : ................................................................................................... 69
EXERCICES PAGES 53 ET 54 : ................................................................................................. 73
EXERCICE RÉCAPITULATIF I................................................................................................... 76
EXERCICE RÉCAPITULATIF II : OPÉRATIONS SUR DES MATRICES CREUSES ............................. 83
LES PACKAGES ESSENTIELS.......................................................................................... 87
QUELQUES RÉFÉRENCES ................................................................................................ 89
DOCUMENTS ACCESSIBLES À PARTIR DU SITE DE R................................................................ 89
AUTRES DOCUMENTS, BLOGS, FAQ…................................................................................... 89
Je remercie Jérôme Llido pour sa relecture du document.
Si vous remarquez des erreurs ou des imprécisions, ou si vous voulez améliorer le document
par vos remarques, vos expériences, des exemples…, n’hésitez pas à m’en faire part à
l’adresse suivante (codée sous forme d’une chaîne de caractères) :
'\u0066\u006C\u006F\u0072\u0065\u006E\u0074\u002E\u0061\u0075\u0062\u0072\u0079\u
0040\u0069\u006E\u0073\u0065\u0072\u006D\u002E\u0066\u0072'
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 4
R tourne sur toutes les plates-formes, Windows 32 et 64 bits, Apple et Unix. Il peut être
téléchargé sur le site de R (http://www.r-project.org/). On télécharge alors le cœur puis les
‘packages’ d’intérêt.
R natif propose une console alphanumérique et l’appel des procédures se fait donc ligne à
ligne. Il est possible d’écrire des scripts et des fonctions. L’objectif de ce document est de
donner les bases pour le faire.
Dans les noms des variables, des fonctions ou des arguments de fonction, le point n’a aucun
sens particulier. De ce fait, les noms du type ma.variable, is.logical ou lower.tail sont valides.
ATTENTION : R est sensible à la casse. De ce fait, a et A référencent deux variables ou
fonctions différentes.
Installation
Aller sur le site de R, ou l’un des sites miroir, et charger l’installation de l’exécutable qui
s’installe alors comme tout exécutable en fonction du système d’exploitation.
Choix du site miroir
On peut facilement choisir son site miroir par défaut, ce qui fait que la question du choix du
site miroir ne se posera plus. Il suffit de modifier les fichiers Rprofile.site dans le répertoire
etc du programme R :
# set a CRAN mirror : par exemple celui de Montpellier
local({r <- getOption( "repos")
r["CRAN"] <- "http:// ftp.igh.cnrs.fr/pub/CRAN"
options( repos=r)})
Aide en ligne
Pour l’aide en ligne, R donne deux possibilités, 1) de l’obtenir sous forme de fenêtres de
texte, 2) de l’avoir sous forme de pages HTML dans le navigateur par défaut. Cette seconde
option est plus pratique. Elle est facilement programmable par défaut dans le fichier
Rprofile.site :
options( help_type="html")
Si on choisit l’aide sous forme de pages HTML, R utilise la boucle local localhost (n° IP
127.0.0.1) pour dialoguer avec le navigateur par défaut. En conséquence :
1) l’aide ne peut pas s’afficher si le navigateur travaille hors connexion ; il faut donc
décocher cette option et réessayer ;
2) on ne peut plus naviguer dans l’aide si on a quitté R.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 5
On peut aussi accéder à une aide en ligne à partir du répertoire doc/html du programme R, en
lisant dans le navigateur le fichier index.html (file://${R_HOME}/doc/html/index.html1). Il
existe aussi des fichiers d’aide dans le répertoire doc/manual.
On trouvera un certain nombre de référence à des manuels sur le site officiel de R, à la page
‘Manuals’ et ‘Contributed documentation’. Un des documents de référence est celui
d’Emmanuel Paradis dont il existe une version anglaise et une version française.
Choix de l’éditeur en ligne
On peut aussi choisir l’éditeur de texte à appeler à partir de R :
options( editor=cheminDAccesALEditeur)
Tinn-R
est
un
éditeur
spécialement
développé
pour
R
(http://www.sciviews.org/Tinn-R ou http://sourceforge.net/projects/tinn-r) qui
intègre une fonction d'aide à la saisie des arguments dans une fonction R (R-card).
De plus, il permet de travailler avec n'importe quelle interface graphique de R
dont la console standard Rgui (chemin d'accès à spécifier dans le volet R du menu
Options, item Main/Applications) et d'envoyer ou d'exécuter pas à pas du code.
Pour plus de détails, voir l'aide proposée avec Tinn-R.
RStudio est un nouvel environnement complet de développement (en anglais IDE
ou integrated development environment) libre spécialement développé pour R
(http://www.rstudio.com). C’est celui qui est à recommandé actuellement.
Il existe d’autres éditeurs permettant cette interaction. Pour plus de détails voir la
page consacrée à ce sujet sur le forum francophone des utilisateurs du logiciel R
hébergé par le Cirad (Centre de recherche agronomique pour le développent) à
l’adresse http://forums.cirad.fr/logiciel-R/faq.php (adresse du forum aux
questions).
Mise à jour
Chargement des packages
1) Lancer R
2) Dans le menu Packages, cliquez sur Installer les packages
i) Sauf si un site miroir par défaut n’est pas défini, choisir le site miroir (i.e., France,
Montpellier)
ii) Choisir les packages à installer.
1
${R_HOME} doit être remplacé par le chemin d’accès au répertoire d’installation de R.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 6
Activation des packages
Les packages peuvent être activés manuellement par la commande library( nomDuPackage)
qui peut prendre la forme require( nomDuPackage). Si l’appel au package est fait dans un
script destiné à être diffusé, on peut alors utiliser le code suivant
if( !require( nomDuPackage)) {
install.packages( "nomDuPackage")
}
Il n’est besoin de charger le package en mémoire qu’une fois par session.
Si le package est utilisé systématiquement, il est préférable de la charger automatiquement à
l’ouverture de la session. On peut automatiser le lancement de deux manières différentes
i) ajouter la ligne de commande à la fin du fichier texte Rprofile.site
ii)
a) dans son propre répertoire de base ($HOME pour Unix, Mes Documents
pour Windows), créer le fichier texte .Rprofile [le nom du fichier commence par un point]
b) dans ce fichier, y mettre la ligne de commande.
Mise à jour des packages
Elle se fait par l’item Mise à jour des packages du menu Packages. Cependant, R refuse de
mettre à jour les packages actifs, c’est-à-dire ceux utilisés dans la session. Il faut donc
décharger les packages de la mémoire. Le plus simple est de sortir de R et de le relancer sans
les packages. Si les packages sont lancés automatiquement, il faut d’abord mettre les lignes de
lancement des packages en commentaire (i.e., mettre un # en début de ligne), relancer R,
mettre à jour les packages, ressortir de R et supprimer les commentaires. En effet, R ne met
pas à jour les packages actifs.
S’il y a peu de packages à modifier et qu’on connait les noms, on peut aussi utiliser la
commande detach :
detach( package:nomDuPackage, unload=TRUE).
Quelquefois, les packages mis à jour ont été compilés par une release ou une version
ultérieure de celle couramment utilisée. De ce fait quand on essaie de les lancer par la
commande
library( nomDuPackage)
on obtient un message d’alerte du type
Le package nomDuPackage a été compilé par la version XX.YY
Généralement, il peut quand même fonctionner correctement mais il est préférable de se
méfier et de charger la nouvelle version/release de R (cf. infra)
Mise à jour des versions de R
Régulièrement, le consortium R met à jour le logiciel. Les mises à jour peuvent être mineures
et elles sont alors indiquées par un changement du numéro de release (par exemple, R.14.0 à
R.14.1), soit elles sont majeures et conduisent à la mise à disposition d’une nouvelle version
(par exemple, R.14.1 à R.15.0). R n’offre pas d’alerte automatique pour indiquer ces mises à
jour et il faut aller régulièrement les vérifier sur le site.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 7
Quand on installe une nouvelle mise à jour, R n’écrase pas l’ancienne version contrairement à
ce qui se passe pour les mises à jour des packages, mais crée un nouvel environnement qui ne
contient aucun des packages supplémentaires, autres que ceux de base. Pour recréer son
propre environnement, il suffit alors de copier tous les répertoires contenus par le répertoire
library de l’ancienne version et qui contient tous les packages utilisés, dans le répertoire
library de la nouvelle version, sans écraser ceux qui existent déjà et qui correspondent aux
packages de base livrés avec la version. Ensuite, il suffit de mettre à jour les packages comme
expliqué dans le paragraphe précédent.
Actuellement, pour les utilisateurs de Windows seulement, Tal Galili a développé un package
qui permet une mise à jour automatique de R à télécharger depuis le site de R. Voir
http://www.r-bloggers.com/updating-r-from-r-on-windows-using-the-installrpackage/?utm_source=feedburner&utm_medium=email&utm_campaign=Feed%3A+RBlo
ggers+%28R+bloggers%29
R Commander
John Fox a développé une interface R commander, plus orientée vers l’utilisateur qui est à
mi-chemin entre la commande ligne à ligne et l’interface offert par Statistica®. Elle propose
l’accès aux principales fonctions sans avoir à écrire de lignes de commande. Je conseille de
l’installer. Pour cela, il suffit de charger le package Rcmdr ainsi que tous les packages
RcmdPlugin.xxx.
Par défaut, il faut réinstaller le package à chaque fois qu’on lance R. Ceci peut se faire par la
commande :
library( Rcmdr)
Cependant, ce n’est pas pratique. On peut donc automatiser son lancement par une des
procédures décrites ci-dessus.
Pour l’utilisation R commander on peut par exemple consulter "Analyses statistiques de base
avec R et Rcmdr comme interface graphique" de Christian Jost (http://cognition.upstlse.fr/_christian/poly/stats/TP-BS15M-Rcmdr.pdf).
Sauvegarde et récupération des sessions
Lors d’une session un peu longue, on peut vouloir créer des points de sauvegarde et de
restauration intermédiaire. Ceux-ci peuvent être anonymes et seront alors rechargés
automatiquement à chaque lancement de R :
- fichier .rData (sauver l’environnement de travail du menu Fichier) ;
- fichier .rhistory (sauver l’historique des commandes du menu Fichier).
On peut aussi donner des noms à ces fichiers tout en conservant les extensions. Cependant,
contrairement aux fichiers anonymes, ceux-ci devront être explicitement relus pour que leur
contenu soit utilisable sauf si on programme une fonction automatique pour le faire (fonction
.First).
Si on ne veut sauver que quelques données, il faut alors utiliser la fonction save.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 8
On peut vouloir automatiser ces sauvegardes à chaque sortie de R en créant une fonction
nommée .Last.
Fonctions et méthodes, 1ère partie
Nous verrons plus loin comment coder le corps des fonctions. L’objectif de ce paragraphe est
d’expliquer le codage des appels aux fonctions2 afin de mieux comprendre la documentation.
Accès à l’aide en ligne
Il existe trois niveaux d’aide en ligne :
1) recherche générale sur le site CRAN de R, ce qui nécessite une connexion Web, par la
fonction RSiteSerach( "nomRecherche") ;
2) recherche générale locale parmi les packages installés par la commande help.search dont
une utilisation simplifiée s’écrit ??nomDeLItem ; R génère alors une fenêtre texte avec tous
les items trouvés proches de l’item cherché ;
3) recherche de la documentation d’une fonction particulière :
?nomDeLaFonction
parmi les packages actifs en mémoire,
?nomDuPackage::nomDeLaFonction
parmi les packages en mémoire,
?nomDuPackage:::nomDeLaFonction
parmi les packages installés.
Quelques aides particulières
?Startup
sur l’initialisation de R au début d’une session
?Syntax
sur la syntaxe de R et la précédence des opérateurs
?Arithmetic
sur les opérateurs arithmétiques
?Comparison
sur les opérateurs de comparaison
sur les structures de commande (boucles, répétitions…)
?Control
?Extract
sur les opérateurs d’accès aux éléments de vecteurs, matrices…
?Logic
sur les opérateurs et données logiques
?NumericConstants sur les constantes numériques
?Paren
sur les parenthèses
?Quotes
sur les caractères d’échappement
sur les mots réservés
?Reserved
Dans ce document, je liste un certain nombre de fonctions en donnant leur utilisation. Je
n’explicite que rarement tous les arguments des fonctions ni tous leurs comportements selon
leurs arguments. Je conseille de se reporter à la documentation en ligne ?nomDeLaFonction.
2
N.B. : dans la suite de la documentation, quand je parlerai d’utiliser la fonction f pour effectuer une
certaine tâche (par exemple, pour lister l’ensemble des objets présents en mémoire), cela signifie qu’il est faut
appeler la fonction en donnant les valeurs nécessaires à ses arguments si nécessaire (dans l’exemple, on écrira
simplement ls()).
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 9
Arguments des fonctions
Une fonction peut avoir des arguments nommés explicitement. Par exemple, la fonction atan2
a deux arguments nommés respectivement x et y. Elle peut aussi avoir des arguments
anonymes en nombre variable. Ceux-ci sont alors codés par le symbole spécial ... (cf. la
fonction list). Elle peut aussi mélanger les deux types d’arguments. Dans ce cas, les
arguments anonymes peuvent être listés en premier comme dans la fonction max, en dernier
(cf. apply) ou au milieu des arguments (cf. aggregate).
Cette dernière option est possible car si les arguments peuvent être passés à la fonction par
position comme dans la majorité des langages informatiques comme par exemple, dans
l’appel suivant à la fonction lm :
form <- …
don <- read.table( arguments)
lm( form, don)
form est obligatoirement une formule3 et don un data.frame4 puisque c’est dans cet ordre
qu’ils sont défini dans la signature de la fonction. Ils peuvent aussi être passés par nom. Dans
ce cas, l’ordre est quelconque :
lm( data=don, formula=form).
On passe généralement par position le premier argument de la fonction qui peut avoir un rôle
spécial (cf. infra, la notion de méthode) tandis que les autres arguments sont passés soit par
identificateur :
lm( form, data=don).
soit par position.
Les arguments peuvent aussi avoir des valeurs par défaut qui sont codés de la forme :
nomDeLArgument=valeurParDefaut
5
dans la signature de la fonction.
La valeur par défaut peut être une valeur immédiate (un nombre, une chaîne de caractères, une
fonction…) ou une fonction des arguments précédemment définis (cf. l’argument scores de la
fonction contr.poly).
Les méthodes
R est un langage orienté objet basé sur la notion de classes d’objet, c’est-à-dire que chaque
données utilisées par R appartient à au moins une classe (accessible par la fonction class). Il y
a deux versions du codage des classes, les classes de type S3, le codage le plus ancien, et
celles de type S4. Les objets de type S3 sont largement basés sur les list et donc leurs
membres sont accessibles via l’opérateur $ (p.ex., lm.result <- lm( ... ; lm.result$coefficients
sont les paramètres estimés) tandis que nommés slots, ils sont accessibles par l’opérateur @
pour les objets S4 (p.ex., lmer.result <- lmer( ... ; lmer.result@fixef).
3
cf. infra
cf. infra
5
On appelle signature d’une fonction la ligne qui définit ses arguments et leur type ainsi que le type de
retour. C’est donc la signature de la fonction qui est donnée dans l’aide.
4
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 10
L’avantage des classes est la notion de méthode qui est une fonction générique dont le
comportement dépend de l’objet auquel est elle est appliquée. Par exemple, pour avoir un
résumé d’une analyse faite par lm ou lmer, on utilisera la méthode summary. Pour les objets
de type S3, la méthode générique (p.ex., anova, summary) fait appel à une méthode spécifique
de nom nomDeLaMetodeGenerique.nomDeLaClasse (p.ex., anova.lm, anova.aov...). Ceci
explique pourquoi certaines pages comme celle de lm renvoient à des descriptions de fonction
ayant ce nom composite. Cependant, l’utilisateur n’a généralement pas besoin de connaître
ces noms spécifiques, sauf pour accéder à la documentation. L’appel explicite est plus
compliqué pour les objets de classe S4.
Pour plus de détails sur les types de classes et leur codage, par exemple :
- pour les classes de type S3 :
Classes S3. (http://www.duclert.org/Aide-memoire-R/Le-langage/Classes-S3.php)6
- pour les classes de type S4 :
Petit manuel de S4, Programmation orienté objet sous R. Christophe Genlini.
(http://cran.r-project.org/doc/contrib/Genolini-PetitManuelDeS4.pdf)
6
Cette page est l’une d’un site consacré à la programmation sous R accessible à l’adresse
http://www.duclert.org/. Il faut noter qu’il y a aussi une page consacrée aux classes de type S4.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 11
Les types de données
Les types de données sont définis par leur longueur, leurs dimensions pour certaines, leur
mode et leur classe. De plus, chaque donnée peut avoir des attributs spécifiques qui seront
utilisés par R pour manipuler la donnée ou définis par l’utilisateur. Il n’est pas conseillé de
modifier les attributs spécifiques car cela peut conduire à des comportements aberrants.
Fonctions génériques et valeurs spéciales
La longueur (fonction length) donne le nombre d’éléments qui constituent la donnée.
Le mode définit la nature de l’élément, c’est-à-dire les opérations susceptibles d’être
appliquées à la donnée. Il est accessible par la fonction mode. Généralement, le mode fait
référence à un type atomique. Par exemple, mode( a) est "numeric" signifie qu’on peut
appliquer à la variable a toutes les opérations arithmétiques classiques.
La classe définit le comportement de l’objet, c’est-à-dire les opérations de manipulation
applicable à cet objet, ce qu’on appelle dans la terminologie objet, les méthodes. Elle est
accessible par la fonction class. On peut utiliser la fonction isS4 pour vérifier que la classe de
l’objet en question est du type S4.
Alors que le mode définit les opérations applicables aux éléments (ou membres) de la donnée,
la classe définit donc les opérations applicables à la donnée considérée comme un tout. Par
exemple :
mode( mat) donne "numeric"
class( mat) donne "matrix"
mode( a) donne "numeric"
class( a) donne "numeric"
signifie que la commande mat <- mat + a a un sens (on ajoute la valeur de a à chaque élément
de la matrice) puisque les modes sont identiques. De plus, on peut accéder au nombre de
lignes et de colonnes de mat (fonction dim) mais cela n’a pas de sens pour a puisque a n’est
pas de la classe matrix.
La fonction attributes donne la liste des attributs de la donnée et leur valeur tandis que la
fonction attr permet de manipuler les attributs spécifiques ou définis par l’utilisateur. Certains
attributs spécifiques sont accessibles par des fonctions spéciales. Par exemple, l’attribut dim
d’une matrice est accessible par la fonction dim, ce qui est plus pratique.
Dans R, il existe une différence entre la notion de membre/slot et d’attribut. Le membre/slot
contient la valeur de la donnée tandis que les attributs contiennent des informations sur la
donnée elle-même. Par exemple,
mat <- matrix( 0, ncol=10, nrow=20, dimnames=list( nomLignes, nomColonnes))
sera une matrice nulle de 20 lignes et de 10 colonnes, dont les noms des lignes seront données
par le vecteur nomLignes et celui des colonnes, par le vecteur nomColonnes. Elle aura deux
attributs dim (les dimensions) et dimnames (les noms des lignes et des colonnes). Il faut donc
manipuler les attributs d’un objet avec précaution.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 12
La fonction str donne la structure de la donnée, c’est-à-dire ses composantes et ses attributs.
La méthode summary donne un résumé de la donnée qui dépend à la fois de sa classe et de
son mode. Par exemple, pour un vecteur de nombres, elle renvoie le minimum, le maximum,
les 1er, 2ème (médiane) et 3ème quantiles ainsi que la moyenne et pour un vecteur de chaînes de
caractères, elle renvoie la longueur, le mode et la classe.
Pour tester si une donnée est d’un type particulier, on peut utiliser une fonction
is.nomDuType. Par exemple, pour tester si a est un entier, on écrira is.integer( a) ou si a est
une matrice, on utilisera is.matrix( a). Il faut cependant être prudent dans l’utilisation de ces
tests. En effet, si a est une matrice de nombre, is.numeric( a) sera aussi vrai.
Pour les classes de données n’ayant pas ce type de fonction, on peut utiliser la fonction
générique inherits.
Pour convertir une donnée dans un autre type, on dispose des fonctions as.nomDuTypeCible.
Par exemple si a est une chaîne de caractères représentant le chiffre 1, as.integer( a) sera
l’entier 1. Si la conversion n’est pas valide, la fonction retournera une erreur.
Enfin, il faut noter que R reconnaît deux valeurs qui ont un rôle spécial dans la manipulation
des données :
- la valeur NULL de longueur nulle qui signifie qu’aucune valeur n’y est affectée ;
- la valeur NA (Not Available) de longueur égale à 1 qui signifie que la valeur est
inconnue7.
Il ne faut pas confondre les deux valeurs. En effet, si vec.1 est un vecteur et qu’on crée un
second vecteur vec.2 qui contient tous les éléments de vec.1 auxquels on ajoute la valeur
NULL, alors vec.2 et vec.1 sont identiques. Si on ajoute la valeur NA, alors la longueur de
length( vec.2) = length( vec.1) + 1, la dernière valeur étant NA.
Pour tester qu’une variable n’est pas initialisée, c’est-à-dire si sa valeur est NULL, on peut
tester si sa longueur est nulle (length( var) == 0) mais il est plus explicite d’utiliser la fonction
is.null.
Le test à la valeur NA se fait par la fonction is.na.
Les types atomiques
Nombres
La fonction mode retourne "numeric".
Il existe deux classes, la classe "numeric" ou "double" pour les nombres flottants et la classe
"integer" pour les entiers. Le codage des nombres complexes est possible. Les nombres
complexes s’écrivent sou la forme
nombreComplexe <- partieReelle + partieImaginaire i8
7
Pour un codage plus précis de la valeur manquante, consulter la page de la documentation par ?NA.
À noter qu’il ne faut pas de blanc entre la valeur de la partie imaginaire et le symbole i. Je l’ai inséré
pour rendre compréhensible la lecture de l’instruction.
8
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 13
Exemple
1i^2
donne -1+0i
Pour les détails voir la documentation ( ?complex).
R dispose de deux valeurs spéciales, l’une pour coder moins l’infini (-Inf) et l’autre pour
coder plus l’infini (Inf). Ces valeurs peuvent être aussi testées par les fonctions is.finite et
is.infinite.
R dispose aussi d’une valeur spéciale pour coder le résultat d’opérations mathématiques qui
ne sont pas des nombres comme la division par zéro : NaN. Cette valeur peut être testée par la
fonction is.nan.
Enfin R propose une variable spéciale de nom pi qui contient la valeur de la constante pi.
Cette valeur peut être écrasée par l’utilisateur s’il réinitialise la variable pi. Donc, faire
attention à ne jamais utiliser de variable ayant comme nom pi.
Pour les opérations mathématiques, voir la page Arithmetic de l’aide. Pour les comparaisons,
voir la page Comparison.
On peut aussi appliquer aux données numériques nombre de transformations numériques
comme le logarithme… Voir la documentation.
Parmi les fonctions utilitaires, signalons la fonction round qui permet d’arrondir un nombre
réel à un nombre décimal ayant un nombre donné de chiffre après la virgule. round( a, 0)
permet d’arrondir la variable a à l’entier de plus proche. Il existe aussi des fonctions de
troncature (ceiling, floor, trunc et signif) ; voir la documentation.
Chaînes de caractères
Les chaînes de caractères s’écrivent entre double guillemets (") ou entre simple quote (') :
a <- "c’est une chaine de caracteres"
Pour plus d’informations sur les fonctions de manipulation des chaînes de caractères, voir la
fenêtre ?character. Parmi ces fonctions, on peut citer :
paste qui permet de concaténer plusieurs chaînes de caractères. À noter que si une des
variables n’est pas une chaîne de caractères, cette variable est d’abord transformée en chaîne
de caractères. Ainsi,
paste( "la longueur de la variable", substitute( a), "est", length( a))
est équivalent à
paste( " la longueur de la variable", as.character( substitute( a)), "est", as.character(
length( a))).
paste a deux arguments spéciaux à coder par nom qui son sep et collapse.
nchar qui donne le nombre de caractères composant la chaîne.
grep et grepl permettent de trouver un motif particulier dans la chaîne.
sub et gsub permettent de remplacer un motif par un autre.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 14
R propose un certain nombre de commandes pour formater un titre, un commentaire ou une
légende d’un graphique (cf. text, title, legend). Voir la page d’aide plotmath et la
démonstration demo( plotmath). Supposons qu’on veut représenter sur un graphique une
sensation en fonction de l’intensité d’un stimulus dans une échelle linéaire et la courbe
d’ajustement à la loi de Fechner. On veut ajouter la légende suivante :
1) les points bleus correspondent aux données mesurées,
2) la courbe en trait continu rouge à la courbe d’ajustement de type s = Iα avec α=0,5.
On écrira alors le texte de la légende ainsi :
texte.legende <- c( "points mesurés",
paste( "ajustement à la loi de Fechner", expression( "s = I"^alpha), "avec",
expression( alpha), "= 0,5"))
On remarquera que la deuxième ligne de la légende utilise la fonction paste pour concaténer
les différentes composantes de la légende et que les expressions mathématiques sont
arguments de la fonction expression ce qui permet d’interpréter les noms des lettres grecques.
Si on avait écrit alpha à la place de expression( alpha), R aurait cherché une variable de ce
nom.
Les logiques ou booléens
Les variables logiques prennent deux valeurs FALSE pour faux et TRUE pour vrai. Ces deux
valeurs spéciales doivent être écrites en majuscule.
Une valeur logique dont la valeur n’est pas connue prendra comme valeur NA. Elle pourra
être manipulée dans des expressions logiques. Selon l’opération, le résultat pourra être
FALSE, TRUE ou NA. Par exemple, pour l’opération logique ‘ou’ notée |, on a :
FALSE | NA -> NA
TRUE | NA -> TRUE
NA | NA -> NA
tandis que pour le ‘et’ logique noté &, on a :
FALSE & NA -> FALSE
TRUE & NA -> NA
NA & NA -> NA.
Exercice : Expliquez ces résultats et essayer avec d’autres opérateurs logiques comme le ‘ou’
exclusif (xor).
Si une variable prend une valeur numérique (au sens de la fonction mode) non nulle, elle est
assimilée à la valeur logique TRUE. Si elle vaut zéro, elle est assimilée à la fonction logique
FALSE.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 15
Notons la fonction ifelse( test, yes, no) qui permet de créer de même dimension que l’objet
testé test avec comme valeurs celles de yes si l’élément testé obéit à la condition, celles de no
dans le cas contraire. yes et no peuvent être de longueur supérieure à 1, les valeurs seront
alors prises les unes après les autres. Exemple :
i <- 0
ifelse( i, "yes", "no") -> "no"
>m
# c’est une matrice de 3 lignes et de 2 colonnes
[,1]
[,2]
[1,]
0
1
[2,]
0
1
[3,]
0
1
> ifelse( m, c( "a", "b", "c", "d"), "no")9
[,1]
[,2]
[1,]
"no" "d"
[2,]
"no" "a"
[3,]
"no" "b"
L’expression i == NULL n’a aucun sens et retourne toujours une variable logique de longueur
nulle. Il faut utiliser is.null( i).
L’expression i == NA retourne toujours la valeur NA. Il faut utiliser is.na( i).
Vecteurs
Constructeur
Le vecteur est une suite de valeurs de même mode. Il se construit par
vector( modeDesElements, longueur)
où modeDesElements est une chaîne de caractères représentant n’importe quel type atomique.
Il existe aussi une autre écriture
ModeDesElements( longueur).
Par exemple, vector( "logical", 12) est équivalent à logical( 12).
On peut aussi construire un vecteur par concaténation d’éléments du même mode. La fonction
réalisant la concaténation est notée c. Si un des éléments est une chaîne de caractères, alors on
crée un vecteur de chaînes de caractères, les autres éléments étant convertis.
Une variable atomique est assimilable à un vecteur de longueur 1.
9
En anticipant sur la suite du document, on peut traduire l’opération comme suit :
mat.yes <- matrix( c("a", "b", "c", "d"), ncol=2, nrow=3)
mat.no <- matrix( "no", ncol=2, nrow=3)
mat.result <- matrix( NA, ncol=2, nrow=3)
for( iRow in 1:3) { for( jCol in 1:2) {
mat.result[ iRow, jCol] <- ifelse( m[ iRow, jCol], mat.yes[ iRow, jCol], mat.no[ iRow, jCol])
}}
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 16
Si les éléments sont des chaînes de caractères, elles peuvent être de longueurs différentes.
Notons qu’il existe deux autres façons de construire des vecteurs de nombres.
1) par la fonction seq qui permet des pas fractionnaires positifs ou négatifs ; cette fonction a
d’autres options, voir la documentation ;
2) si le pas est unitaire, par le constructeur from:to qui est équivalent à seq( from, to, by=sign(
to - from)).
ATTENTION
a + from:to + b
est équivalent à a + b + seq( from, to)
(a + from):(to + b)
est équivalent à seq( from + a, to + b).
Accès aux éléments
Les différents éléments du vecteur sont par défaut accessibles par position, le premier élément
ayant la position 1. Les positions sont donc un vecteur de chiffres mis entre crochet droit :
vec[1]
accès au premier élément du vecteur vec ;
vec[c( 1, 3, 6)]
accès aux éléments en position 1, 3 et 6 : c’est un vecteur de
longueur 3.
On peut aussi donner des noms aux différents éléments et accéder à ces éléments par leur
nom. Il y a deux manières pour cela :
1) on crée le vecteur vec puis on donne les noms aux éléments grâce à la fonction
names :
names( vec) <- c( …)
# vecteur de chaîne de caractères de longueur
length( vec)
2) on donne explicitement les noms en construisant le vecteur par la fonction de
concaténation c :
vec <- c( a=1, b=2, …)
# la première valeur à comme nom a, la second
b…
L’accès aux éléments peut alors se faire par nom.
On peut aussi accéder aux éléments par une condition logique ou par un vecteur de logiques.
Par exemple,
vec[vec > 6] renvoie un vecteur contenant tous les éléments de vec supérieurs à 6.
R propose une méthode commode pour supprimer des éléments d’un vecteur : on fait précéder
la liste des numéros des éléments à supprimer du signe -. Par exemple :
vec[-c(10,12)]
retourne un vecteur ne contenant pas les éléments de vec qui
étaient en position 10 et 12.
which est une fonction qui renvoie les indices des éléments répondant à une certaine
condition logique. Par exemple :
which( c( 2, 3, 1, 2) == 2) -> c( 1, 4)
alors que
c( 2, 3, 1, 2) == 2 -> c( TRUE, FALSE, FALSE, TRUE)
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 17
match est une fonction qui renvoie les positions des éléments du premier vecteur dans le
second et NA quand l’élément n’existe pas.
%in% est un opérateur binaire qui renvoie TRUE quand il a trouvé l’élément du premier
vecteur dans le second et FALSE dans le cas contraire.
Les résultats de match et %in% sont des vecteurs de longueur égale à celle du premier
vecteur.
Opérations sur les vecteurs
Toutes les opérations possibles sur les éléments d’un vecteur, donc compatibles avec le mode
du vecteur, sont possibles sur les vecteurs. Les opérations sont faites élément par élément :
vec.1 * vec.2 avec length( vec.2) == length( vec.1) équivaut à :
vec.res <- NULL
for( n in 1:length( vec.1)) vec.res <- c( vec.res, vec.1[n] * vec.2[n])
Les deux vecteurs peuvent être de longueurs différentes. R complète alors le vecteur le plus
court en le répétant autant de fois que nécessaire. Si les longueurs ne sont pas multiples les
unes des autres, R génère un avertissement (warning) mais effectue l’opération. Exemple
vec.1 <- c( 1, 3, 5)
vec.2 <- c( 2, 4, 6, 8)
alors vec.res[4] vaudra vec.2[4] * vec.1[1].
R appelle cette opération recyclage (recycling).
Quelques fonctions utilitaires
Si les éléments du vecteur sont d’un mode ordonné (nombre, chaîne de caractères), le vecteur
peut être ordonné dans le sens ascendant ou descendant par la fonction sort. Pour les nombres,
l’ordre est l’ordre naturel, pour les chaînes de caractères, c’est l’ordre alphabétique.
La fonction order permet de renvoyer les positions des éléments d’un vecteur selon leur ordre
naturel, les indices des valeurs identiques étant donnés dans un ordre quelconque. On peut
ordonner selon plusieurs vecteurs. Par exemple
order( vec.1, vec.2)
ordonnera les indices selon les valeurs de vec.1 et les valeurs identiques de vec.1 selon celles
de vec.2 (ordre dit lexicographique).
On peut ainsi trier un vecteur en fonction des valeurs d’un autre vecteur bien que cette
opération ait plus d’intérêt pour les data.frame (cf. infra).
La fonction unique retourne la liste des éléments du vecteur en supprimant tous les éléments
dupliqués dans l’ordre où ceux-ci sont rencontrés dans le vecteur :
unique( c( "a", "a")) -> "a"
rep permet de répéter plusieurs fois la même valeur.
sum fait la somme des éléments tandis que cumsum renvoie un vecteur faisant la somme
cumulé.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 18
prod renvoie le produit des éléments tandis que cumprod renvoie un vecteur faisant le produit
cumulé.
scale permet de centrer (par rapport à sa moyenne) et de réduire un vecteur (division des
valeurs par la déviation standard donc nouveau vecteur de variance unité).
rev renvoie le même vecteur dans l’ordre inverse :
rev( vec) est identique à vec[length( vec):1].
cut transforme un vecteur numérique en classes de valeurs considérées comme un facteur.
rank retourne le rang des valeurs du vecteur en gérant les répétitions de valeurs et la valeur
NA de différentes manières (argument na.last) :
rank( c( 2.8, 1.3, 5.45, 4, 3.28, 6.12)) donne 2, 1, 5, 4, 3, 6
range renvoie le minimum et le maximum du vecteur.
Parmi les autres fonctions utilitaires on peut citer :
mean
moyenne des éléments
median
médiane des éléments
quantile
calcul des quantiles
sd
écart type des éléments au sens d’un échantillon, c’est-à-dire :
sd ( vec) =
var
...
1 length ( vec )
∑ (vec[i] − mean( vec) )2
N − 1 i =1
variance (carré de sd)
La fonction all renvoie TRUE si tous les éléments du vecteur sont vrais, c’est-à-dire obéissent
à la même condition. Par exemple, si
vec.1 <- seq( 2, 12, by=2)
all( vec.1 %%2 == 0) -> TRUE
tandis que
all( vec.1 %%4 == 0) -> FALSE
La fonction any renvoie TRUE si au moins un élément remplit la condition.
Nombreuses de ces fonctions (et d’autres fonctions de R) ont un argument spécial
commençant généralement par la chaîne na. qui spécifie le comportement de la fonction face
aux valeurs NA. Exemple :
mean( c( sample( 12, replace=TRUE), NA)) renvoie NA
mean( c( sample( 12, replace=TRUE), NA), na.rm=TRUE) est équivalent à
mean( sample( 12, replace=TRUE)).
Facteurs
Le facteur (factor) est l’objet qui représente les facteurs des analyses statistiques. C’est donc
un vecteur spécial applicable aux données qualitatives. Dans R, un facteur peut être constitué
d’un ensemble de chaînes de caractères ou de nombres qui seront donc les niveaux du facteur.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 19
Constructeur
Le constructeur factor a 5 arguments dont un seul, le premier, est obligatoire. Sa signature
est :
factor( x=character(), levels, labels=levels, exclude=NA, ordered=is.ordered( x))
x
est le vecteur de données servant de facteur,
levels
est un vecteur optionnel qui contient les niveaux du facteur,
labels
est un vecteur optionnel des étiquettes des nivaux, sa longueur doit être celle du
nombre de niveaux (i.e., la longueur du vecteur levels),
exclude
est un vecteur des valeurs exclues comme niveaux, par défaut la valeur NA
(niveau non disponible) est exclue,
ordered
est une booléen signalant si le facteur est ordonné au nom.
levels indique le codage interne du facteur qui peut être sous forme de nombre (par exemple
pour faciliter l’ordonnancement, tandis que labels indique l’étiquette qui sera affichée.
Quelques exemples :
instruction
données
levels
factor( 1:3)
123
123
factor( 1:3, levels=c( 3:1))
123
321
factor( 1:3, levels=c( 3:1), labels=c( "a", "b", "c"))
cba
abc
factor( c( "1", "2", "3"), levels=c(3:1), labels=c( "a", "b", "c")) c b a
abc
factor( c( "c", "b", "a"), levels=c(3:1), labels=c( "a", "b", "c")) <NA> <NA> <NA> a b c
Les exemples ci-dessus montrent qu’il faut être très prudent quand on convertit un vecteur en
facteur, notamment pas l’instruction as.factor, et vice-versa. En effet :
as.character( factor( c( "1", "2", "3"), levels=c(3:1), labels=c( "a", "b", "c"))) donnera
c( "c", "b", "a")
alors que
unclass( factor( c( "1", "2", "3"), levels=c(3:1), labels=c( "a", "b", "c"))) donnera
c( 3, 2, 1)
avec is.integer( ) -> TRUE.
Si on ordonne les niveaux (ordered=TRUE), l’ordonnancement sera celui donné par l’ordre
des niveaux (argument levels). De plus, pour les facteurs ordonnés, la fonction order a un
sens. De même il est possible d’utiliser les opérateurs de conversions :
df$TPS <- factor( df$TPS, orderd=TRUE, levels=c( "m00", "m06", "m12", "m24", …))
L’instruction df$TPS[n.x] < df$TPS[n.y] a un sens. De même :
limite <- "m18"
df$TPS[n] >= limite.
Notons que lors des analyses statistiques, le comportement de la procédure statistique face à
des individus dont le niveau du facteur est inconnu (NA) est contrôlé par l’argument na.action
de la procédure qui peut être mis à une valeur par défaut pour la session grâce à la commande
options( na.action=...)
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 20
ou qui peut être spécifié par l’argument, généralement noté na.action, de chacune des
procédures d’analyse.
Contrastes
L’option options( contrasts=...) donne le comportement par défaut des contrastes des facteurs
non ordonnés et ordonnés. Ce comportement peut être modifié localement dans la procédure
statistique grâce à l’argument contrasts de la procédure.
On peut aussi modifier le comportement du facteur lui-même en lui associant un contraste
donné par la fonction C (ATTENTION à la majuscule qui différencie cette procédure de
celle de concaténation des vecteurs c).
La notion de contraste est très important dans les analyses statistiques. En effet, le nombre de
paramètres à estimer est toujours supérieur au nombre de degrés de liberté du problème. Par
exemple, si on fait une Anova à un facteur F1 ayant deux niveaux (F1.1 et F1.2), on peut
vouloir estimer :
y j = δ 1.1 ( j ) ϕ1.1 + δ 1.2 ( j ) ϕ1.2 + ε j
avec δ 1.i ( j ) = 1 si le niveau du facteur associé à l’individu j est F1.i, 0 autrement ; ou
y j =ϕ 0 +δ 1.1 ( j ) ϕ1.1 + δ 1.2 ( j ) ϕ1.2 + ε j
La première écriture semble plus intéressante car elle conduit à n’estimer que deux paramètres
alors que le problème à deux degrés de liberté (les deux niveaux du facteur) tandis que la
seconde est sur-paramétrée puisqu’il faut estimer trois paramètres. On pose alors comme
contrainte
ϕ1.1 + ϕ1.2 = 0
ce qui ramène au problème de l’estimation de deux paramètres. De plus, cette paramétrisation
appelée sigma-restreint dans la littérature statistique impose que :
ϕ0 = mean( y).
Supposons maintenant que le modèle comporte deux facteurs F1 et F2 à deux niveaux avec
une interaction, la première écriture donnera :
y j = δ 1.1 ( j ) ϕ1.1 + δ 1.2 ( j ) ϕ1.2 + δ 2.1 ( j ) ϕ 2.1 + δ 2.2 ( j ) ϕ 2.2 +
∑ ∑δ
k∈F 1l∈F 2
1.k
( j ) δ 1.l ( j ) ϕ12.kl +ε j
ce qui conduit à 8 paramètres à estimer (les deux liés au facteur F1, les deux liés au facteur F2
et les 4 liés aux interaction F1.1:F2.1, F1.1:F1.2, F1.2:F2.1 et F1.2:F1.2 alors qu’il n’y a que
quatre degrés de liberté. On est donc dans un modèle sur-paramétré. Il faut donc faire un
changement de variables β et imposer des contraintes pour revenir à quatre paramètres. La
seule contrainte admissible est d’imposer qu’un des nouveaux paramètres lié à l’effet
principal d’un des facteurs soit nul (par exemple β 2.1 ) et que trois des quatre paramètres
d’interaction le soient aussi. De ce fait, cette écriture qui semblait la plus intéressante avec un
seul facteur conduit à déséquilibrer l’estimation des paramètres au profit d’un facteur et à
perdre la symétrie de l’écriture. Pour rétablir l’équilibre, le modèle est réécrit :
yj =
β 0 + δ 1.1 ( j ) β 1.1 + δ 1.2 ( j ) β1.2 + ε j
avec comme contrainte qu’un des paramètres de chaque facteur soit nul.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 21
Ces contraintes de nullité conduisent donc à la construction d’une matrice du schéma
d’analyse (design matrix) ayant des colonnes nulles. Ceci rend difficile certains tests, d’autant
que les paramètres estimés, quand il y en plus de deux, ne sont pas indépendants.
Cette représentation conduit à ce que R appelle les contrastes de type traitement qui sont
implantés sous trois formes :
contr.treatment( n, base=1) : les paramètres estimés seront alors nommés par la
concaténation du nom du facteur et de celui du niveau, par exemple FN1. Le paramètre base
est le numéro du niveau servant de référence.
contr.SAS( n) qui est équivalent à contr.treatment avec base ayant pour valeur le
nombre de niveaux.
Par défaut, R utilise contr.treatment pour les facteurs non ordonnés sauf si on modifie
options( contrasts=...).
La seconde écriture conduit à :
y j = β 0 + δ 1.1 ( j ) β1.1 + δ 1.2 ( j ) β 1.2 + δ 2.1 ( j ) β 2.1 + δ 2.2 ( j ) β 2.2 +
∑ ∑δ
k∈F 1l∈F 2
1. k
( j ) δ 1.l ( j ) β 12.kl +ε j
avec :
β1.1 + β1.2 = 0
β 2.1 + β 2.2 = 0
β12.11 + β12.12 = β12.21 + β12.22 = β12.11 + β12.21 = β12.12 + β12.22 = 0
β 0 = mean( y )
ce qui définit les quatre paramètres, les deux premières contraintes signifiant que les deux
niveaux des facteurs se situent à égale distance de la moyenne et la troisième, que la moyenne
est le centre d’un carré dont les quatre coins représentent les interactions. De ce fait, la
représentation reste identique quelque soit le nombre de niveaux, de facteurs et les
interactions et conserve les symétries.
R propose trois implantations de ce type de paramétrisation sigma restreint10 :
contr.sum
qui calcule la différence au premier niveau de chaque facteur, tout en
conservant les autres contraintes énoncés ci-dessus ; le problème principal est la non
indépendance des estimations même pour les groupes équilibrés ;
contr.helmert plus difficile à interpréter mais qui a l’avantage de l’indépendance des
estimations pour les groupes équilibrés ;
contr.poly
qui n’a de sens que pour les facteurs ordonnés qui peuvent être
interprétés comme des classes d’une variable continue, également distribuées ce qui conduit
aux analyses de tendance ; en effet, ce contraste peut être assimilé à une discrétisation d’une
régression polynomiale sur le variable continue sous-jacente au facteur ; ce contraste conduit
à l’indépendance des estimations pour les groupes équilibrés.
Notons qu’on peut aussi coder les contrastes treatment, sum et helmert, contr.Treatment,
contr.Sum et contr.Helmert ce qui donnera dans des résultats des analyses statistiques plus
faciles à lire.
10
Pour les facteurs ayant plus de deux niveaux et quelque soit le type de paramétrisation, les schémas des
effets principaux et des interactions sont plus compliqués mais le principe reste le même, notamment la
coordonnée à l’origine µ est la grande moyenne.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 22
Note sur le calcul des paramètres d’un modèle linéaire dans R
Prenons l’exemple d’une analyse à un seul facteur. L’objectif est d’estimer les
valeurs moyennes de la variable dépendante dans chacun des groupes formé par
un niveau du facteur (ϕ = {ϕ {1} , L}) et de tester leur égalité.
Estimation des coefficients
R comme tous les autres logiciels statistiques ne calcule pas directement les
coefficients ϕ mais des coefficients β qui se déduisent de ϕ par l’équation :
ϕ =C β
où
C
est la matrice des contrastes aussi bien pour les effets principaux
que pour les interaction. On a toujours :
β 0 = ϕ 0 , c’est-à-dire que le premier coefficient est toujours l’ordonnée à
l’origine même quand celle-ci est forcé à zéro.
Ainsi, l’équation à résoudre n’est plus :
Y=Xϕ
où X est la matrice du schéma (design matrix)
mais
Y=XCβ
On peut revenir facilement aux coefficients d’intérêt ϕ par l’équation ci-dessus
(ou utiliser la fonction dummy.coef) ou calculer les coefficients β ( 2) dérivés d’un
contraste C(2) à partir des coefficients β (1) calculés à partir d’un contraste initial
C (1) par l’équation suivante :
β ( 2) = C ( 2) C (+1) β (1)
C (+1) l’inverse généralisée de Moore-Penrose de C (1) (calculable par la
avec
fonction ginv de R, cf. infra).
L’équation ci-dessus est plus générale car il suffit de remplacer C ( 2) par la matrice
d’intérêt pour calculer n’importe quel paramètre à condition que la matrice ait
autant de colonne que de paramètres ϕ du modèle.
Exemple : mettons nous dans le cas d’une Anova à deux facteurs F1 et F2 à deux
niveaux chacun. On veut estimer la moyenne des différents niveaux de chacun des
facteurs hors interaction, c’est-à-dire par exemple pour le niveau 1 du facteur 1,
on veut calculer :
υ1.1 = ϕ 0 + ϕ1.1 donc en ne prenant pas en compte les termes d’interactions ϕ12.11
et ϕ12.12
On voit alors facilement que :
 υ  1 1 0
υ1 =  1.1  = 
υ1.2  1 0 1
Programmer R - Version 1.2 (mars 2013) F. Aubry
β 
[0]  0 
C β
[0]  1. 
 M 
p. 23
Statistique de Wald
Quand on a estimé de nouveaux coefficients par l’équation ci-dessus, on désire
connaître leur variance comme l’analyse en elle-même donne la variance des
coefficients β initiaux (fonction summary). On calcule alors les statistiques dites
statistiques de Wald par la formule suivante :
(
var( ϑ ) = (Qβ ) Q var( β ) Q T
T
avec
) (Qβ )
−1
( A)T , la transposée de la matrice A (cf. infra, fonction t),
A −1 , l’inverse de la matrice A (cf. infra, fonction ginv puisque l’inverse
généralisée et l’inverse d’une matrice carrée inversible sont identiques).
Sous l’hypothèse Qβ = 0 , var( ϑ ) tend vers un χ2 à n degrés de liberté où n est le
rang de la matrice Q (composante rank du résultat de la fonction qr appliquée à la
matrice).
Contrastes des interactions
Sans entrer dans les détails du calcul, le contraste associé à l’interaction entre 2
facteurs F1 et F2 (dans cet ordre, donc F1:F2 pour R) se calcule par :
C12 = C 2 ⊗ C1 avec ⊗ le produit de Kronecker.
Plus généralement, l’interaction d’ordre n entre les facteurs F1 à Fn vaut :
C1...n = C n ⊗ K ⊗ C1
Dans R, la fonction qui calcule ce type de produit s’appelle kronecker.
Matrice des contrastes en cas d’interactions
Pour exemple, je donnerai le cas de 2 facteurs, l’extension à plus de deux facteurs
et à des interactions d’ordres supérieurs étant évident : effets principaux dans
l’ordre des facteurs, interactions d’ordre 2 dans l’ordre lexicographique (i.e.,
F1:F2, F1:F3, ..., F2:F3, F2:F4, ... F3:F4...) puis la même stratégie pour les
interactions d’ordres supérieurs.
β0
β1.
β2.
β12.
| Origine
|1
|0
|0
|0
|
F1
|0
| c1 <- C11( F1)
|0
|0
|
F2
|0
|0
| c2 <- C(F2)
|0
| Interaction
|0
|0
|0
| c2 ⊗ c1
Dans le document intitulé ‘Utiliser R et Statistica - notes pratiques’, je détaille
comment construire effectivement la matrice des contrastes avec R.
11
C( F) dénote ici le contraste associé au facteur F.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 24
Utilitaires
nlevels
levels
reorder
relevel
nombre de niveaux du facteur
liste des niveaux
modification de l’ordre des niveaux d’un facteur
modifie le niveau de référence (utile pur contr.treatment et contr.sum)
Matrices
Constructeur
Il existe plusieurs moyens pour construire des matrices. La plus simple est l’utilisation de la
fonction matrix qui permet de donner une valeur initiale ainsi que les dimensions.
La valeur initiale peut être une valeur atomique. Dans ce cas, toutes les cellules de la matrice
auront cette valeur. Ce peut aussi être un vecteur. S’il est de longueur insuffisante, R recycle
le vecteur. Par défaut, R remplit la matrice colonne par colonne sauf si l’argument byrow vaut
TRUE ce qui conduit à un remplissage ligne par ligne.
On peut aussi restructurer un vecteur en lui donnant le nombre de lignes et le nombre de
colonnes de la matrice résultante par la fonction dim qui sert aussi à récupérer le nombre de
lignes et de colonnes d’une matrice. Il faut alors que la longueur du vecteur soit égale au
nombre de cellules de la matrice, sinon on a une erreur :
vec <- 1:6
dim( vec) <- c( 2, 3).
ncol revoie le nombre de colonnes tandis que nrow renvoie le nombre de lignes.
On peut rajouter des colonnes à une matrice sous forme d’un vecteur de longueur le nombre
de lignes de la matrice ou d’une matrice ayant le même nombre de lignes, grâce à la fonction
cbind ou des lignes (vecteur de longueur le nombre de colonnes ou matrice ayant le même
nombre de colonnes) grâce à rbind.
Les lignes et les colonnes d’une matrice peuvent recevoir des noms :
1) par l’argument dimnames du constructeur matrix ;
2) par les fonctions rownames et colnames qui permettent aussi de récupérer ces noms.
Exemple :
rownames( vec) <- c( "ligne.1", "ligne.2")
Sélectionner des éléments d’une matrice
Les cellules de la matrice peuvent être accédées par position ou par nom. Les règles sont les
mêmes que pour un vecteur, y compris celles pour supprimer des éléments qui seront ici soit
des lignes entières, soit des colonnes entières. Pour accéder à différentes colonnes d’une ligne
complète, on écrira :
mat[,colonnesALire]
# pas de valeurs pour la ligne
et pour une colonne complète
mat[lignesALire,].
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 25
colonnesALire (resp. lignesALire) peut être un vecteur de numéros ou de noms de colonnes
(resp. lignes).
mat[lignesALire,colonnesALire] permet d’accéder à une sous matrice. On peut spécifier les
lignes et colonnes auxquelles on veut accéder comme celle que l’on désire supprimer. Dans ce
cas, on fait précéder ces numéros du signe de soustraction (-). Si les lignes et les colonnes ont
reçu des noms, on peut accéder par nom à ces lignes ou colonnes, au lieu d’y accéder par
position.
On peut aussi indicer logiquement les éléments d’une matrice :
m > 0 donnera une matrice logique de même dimension que m ;
m[m > 0] donnera un vecteur contenant la liste des éléments positifs de m.
which donnera la position des éléments de la matrice répondant à la condition logique. Si
l’argument arr.ind est FALSE (défaut), which retourne les positions de la matrice linéarisée
(équivalent à which( as.vector( m)…), sinon, il retourne les véritables indices.
On peut aussi donner un tableau d’indices dont le nombre de colonnes est 2. Ainsi
identical( m[which( m > 0, arr.ind=TRUE)], m[m > 0]) est TRUE.
Opérations
Les opérations +, -, *, / et autres opérations mathématiques comme le modulo (%%)
s’appliquent aux matrices. Les opérations sont cellule par cellule et les deux matrices doivent
être de mêmes dimensions. Par exemple :
mat.result <- mat.1 * mat.2
donne une matrice dont les cellules valent
mat.result[i,j] <- mat.1[i,j] * mat.2[i,j].
La multiplication de deux matrices est codée par l’opérateur %*%.
determinant et sa version simplifiée det calculent le déterminant de la matrice.
La transposition d’une matrice se fait grâce à la fonction t.
Le calcul de l’inverse d’une matrice se fait par la fonction ginv. Cette fonction permet de
calculer l’inverse d’une matrice carrée inversible aussi bien que l’inverse généralisée d’une
matrice rectangulaire.
Notons que R propose une fonction particulière solve pour résoudre l’équation linéaire Ax = y
où A est une matrice carrée :
x <- solve( A, y)
solve( A) calcule l’inverse de A. Si A est inversible, le résultat est identique à celui obtenu par
ginv, sinon solve donne une erreur.
svd calcule la décomposition en valeurs singulières d’une matrice quelconque. Soit B une
matrice, on a donc :
B <- svd( B)$u %*% diag( svd( B)$d) %*% t( svd( B)$v)
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 26
eigen calcule les vecteurs propres et les valeurs propres d’une matrice carrée.
Utilitaires
Pour calculer la somme ou la moyenne de colonnes ou de lignes :
colSums, colMeans, rowSums, rowMeans.
Ce sont des implantations particulières de la fonction apply qui permet de retourner un tableau
ou une liste de valeurs en appliquant une fonction donnée aux marges d’une matrice
(argument FUN). Par exemple, si on désire calculer la déviation standard des colonnes de la
matrice mat, on écrira :
apply( mat, 2, sd)
On peut aussi passer des arguments à la fonction. Supposons que mat soit le résultat d’un
bootstrap d’une Anova, les lignes sont alors les paramètres de l’Anova (effets principaux,
interactions…) et les colonnes les résultats pour les 10000 passages du bootstrap. On veut
alors obtenir les quantiles à 2,5%, 25%, 50% (médiane), 75% et 97,5% et utiliser l’algorithme
de type 8 de la fonction quantile. On utilisera alors les … pour passer les arguments à la
fonction quantile :
apply( mat, 1, quantile, probs=c( 0.025, 0.25, 0.50, 0.75, 0.975), type=8).
Si on appliquait directement les fonctions applicables aux vecteurs aux matrices, les calculs
comme la somme, le produit, la moyenne... ne se feraient pas ligne à ligne ou colonne à
colonne mais sur la matrice entière considérée comme un vecteur.
Notons que
apply( mat, c( 1, 2), …
renvoie une matrice de même dimension que mat en appliquant la fonction FUN à chaque
élément de la matrice initiale.
sweep permet d’appliquer aux éléments d’une matrice une fonction mettant en jeu des
statistiques récapitulatives.
Exercice : On désire centrer les colonnes d’une matrice (par exemple
mat <- matrix( runif( 200), ncol=10))
sur leur médiane.
1) écrire le code sans utiliser ni apply ni sweep ;
2) écrire le code en utilisant apply ;
3) écrire le code en utilisant sweep.
rowsum permet de calculer des sommes dans des colonnes en fonction d’un facteur de
groupement. rowsum est une implantation particulière d’une fonction plus générale aggregate
qui peut prendre comme premier argument une matrice et comme facteur d’agrégation un
ensemble de facteurs de groupement.
dim permet d’obtenir les dimension de la matrice. Il ne faut pas confondre cette fonction avec
la fonction length. En effet :
mat <- matrix( 0, ncol=10, nrow=20)
dim( mat)
donnera 20, 10
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 27
length( mat)
donnera 200, le nombre d’éléments de la matrice
Tableaux
Les tableaux (constructeur array) sont une extension aux dimensions supérieures à deux des
matrices. Ce qui a été dit des matrices est applicable aux tableaux. La sélection des éléments
se fait de la même manière et apply est aussi applicable, l’argument MARGIN prenant alors le
numéro (ou les numéros) des dimensions à considérer. Lire la documentation pour avoir des
détails.
À noter que nrow donne la première dimension et ncol la seconde.
L’extension de la transposition t aux tableaux s’appelle aperm.
Séries temporelles
Pour mémoire, je citerai la classe des séries temporelles construites par le constructeur ts et
qui possède des méthodes propres, par exemple pour aligner les axes, changer
l’échantillonnage…
Tables
Les tables (constructeur table) sont des matrices particulières pour représenter les tableaux de
contingences à deux ou plus de deux entrées. Elles ont des méthodes spécifiques. Voir la
documentation.
Listes
Les vecteurs permettent de regrouper des éléments de même mode dans une structure
ordonnée. Les listes au sens de R font la même chose sauf que la structure peut contenir des
éléments de classes différentes qui peuvent être eux-mêmes des listes.
Constructeur
R propose un constructeur peu utilisé à partir de la fonction vector :
vector( "list", nbElements)
qui montre la parenté entre le notion de liste de R et celle de vecteur.
Le constructeur le plus utilisé est list qui a un nombre quelconque d’argument qui seront les
éléments de cette liste rangé dans leur ordre d’apparition. De plus ce constructeur permet de
nommer les éléments comme pour les vecteurs, en utilisant la construction nom=valeur.
Les éléments d’une liste peuvent être nommés a posteriori par la fonction names :
names( ma.list) <- c( "nomElement.1", "nomElement.2", ...
Symétriquement, ils peuvent être récupérés sous forme d’un vecteur de chaînes de caractères
par la fonction names.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 28
Accès aux éléments de la liste
On peut vouloir extraire une sous-liste, c’est-à-dire créer une nouvelle liste ne contenant pas
tous les éléments de la liste initiale ou accéder à un élément particulier de la liste.
Sous liste
La sous-liste d’extrait par l’opérateur noté par les parenthèses droites [ ]. L’argument entre ces
parenthèses peut être un vecteur de nombre, les positions des éléments de la liste initiale
composant la nouvelle liste, ou de noms des éléments si ceux-ci ont été nommés :
li <- list( x="z", y=2+3, q="e")
li[c(1, 3)] est alors équivalent à li[c( "x", "q")] et donc a list(x="z", q="e")
Élément d’une liste
On ne peut extraire qu’un élément à la fois puisque les composants peuvent être de différentes
classes. Il existe deux façons d’accéder à l’élément :
1) par utilisation de l’opérateur [[ ]] qui contient aussi bien le nom que la position :
li[[1]] ou li[["x"]]
-> "z"
2) par utilisation de l’opérateur $ concaténé au nom de la liste quand les éléments sont
nommés. Par exemple, on peut écrire li$x pour accéder à l’élément de nom x de la liste li cidessus.
Extension d’une liste
Il existe de nombreuses façons pour rajouter des éléments à une liste
- par ajout de liste :
. concaténation de listes par la fonction c
. ajout de sous-listes
position <- c( )
# un vecteur
li[position] <- list( …)# liste de longueur celle du vecteur position
- par ajout d’élément
. par adressage
li[[nouvellePosition]] <- valeur
. par nom
li$nom <- valeur.
Utilitaires
La fonction unlist permet d’aplatir la liste et retourne un vecteur. Si un élément est une liste,
elle travaille récursivement jusqu’à arriver à des valeurs atomiques. Si un des éléments est
une chaîne de caractères, le résultat est un vecteur de chaînes de caractères.
Il existe une fonction utilitaire appelée lapply permettant d’appliquer itérativement une
fonction aux éléments d’une liste. Voir la documentation pour la fonction et ses dérivées.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 29
Date
R permet de manipuler des dates et des heures et de transformer des chaînes de caractères en
objets de classe Date ou date POSIX et vice-versa. Voir la documentation à partir de la page
d’aide de as.Date.
Data frame
Construction et accès aux éléments
Le data.frame est au cœur de R puisque c’est ainsi qu’est codé tout tableau de valeurs sur
lequel on effectue des analyses statistiques. Il est basé sur les listes mais avec la contrainte
que tous les éléments de la liste soient des vecteurs de valeurs atomiques de même longueur.
Ces éléments constituent les colonnes du data.frame. De plus, l’accès aux éléments utilise
aussi les parenthèses droites ([ ]) mais avec la signification qu’elles ont pour les matrices.
Le constructeur standard est la fonction data.frame avec la liste des colonnes qui peuvent être
nommées par la construction nomColonne=valeur.
Les colonnes peuvent être nommées a posteriori par la fonction names qui reçoit un vecteur
de chaîne de caractères.
Les lignes peuvent être nommées explicitement par l’argument row.names du constructeur ou
a posteriori en utilisant la fonction row.names (noter la présence du point dans le nom au
contraire de la fonction ayant le même objectif pour les matrices).
L’accès aux éléments du data.frame par l’opérateur [ ] suit les mêmes règles que pour les
matrices. On peut aussi accéder à une colonne entière par l’opérateur $.
On peut retirer des lignes ou des colonnes d’un data.frame en faisant précédent leur numéro
de position du signe moins.
On peut rajouter des lignes grâce à la méthode rbind tandis que l’ajout de colonnes se fait par
cbind.
transform permet de rajouter une colonne à un data.frame dont la valeur résulte d’un calcul
sur les autres colonnes.
Facteurs
L’option stringsAsFactors ou l’argument stringsAsFactors du constructeur permettent
d’automatiser la conversion des chaînes de caractères en facteur. Si stringsAsFactors est
TRUE et qu’on veut inhiber ce comportement pour une colonne, il faut alors utiliser la
fonction I :
t <- data.frame( g=c( "t"..., y=c( "z"..., stringsAsFactors=TRUE)
alors les colonnes g et y seront des facteurs tandis que si on écrit :
t <- data.frame( g=c( "t"..., y=I( c( "z", ...), stringsAsFactors=TRUE)
seule la colonne g contiendra un facteur.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 30
Quand on a retiré un certain nombre de lignes d’un data.frame, il est possible que certains
niveaux d’un facteur donné ne soient plus utilisés. Cette absence pourra conduire à une erreur
puisque la procédure trouvera des cellules vides, celles correspondant aux niveaux qui ne sont
plus utilisés. En effet, les procédures d’analyse se basent non pas sur les niveaux effectifs
mais sur l’attribut levels de la colonne (i.e., du facteur). Il est donc nécessaire de remettre à
jour cet attribut avant l’analyse. R propose deux méthodes pour cela
1) mesDonnees$F <- factor( mesDonnees$F)
2) mesDonnées <- droplevels( mesDonnees).
La fonction drop quant à elle supprime les dimensions d’un tableau (array) qui n’a qu’un
niveau.
Utilitaires
Il peut être intéressant de réordonner les lignes d’un data.frame en fonction d’autres données,
par exemple de certaines colonnes du data.frame afin de regrouper sur des lignes adjacentes
toutes les informations du même patient (colonne ID) et le trier dans l’ordre de leur date
d’acquisition (colonne ACQ_DATE). Cela se fait simplement :
df <- df[order( df, df$ID, df$ACQ_DATE),]
On peut aussi vouloir ajouter une ou plusieurs colonnes dérivées des autres colonnes.
L’utilisation de cbind est possible est possible mais demande de référencer exactement les
arguments qui interviennent. On peut aussi utiliser la fonction transform qui permet de
rajouter en une seule opération plusieurs colonnes en ne référençant que les noms des
colonnes impliquées. Par exemple, on dispose dans le data.frame df d’une colonne
DATE_NAISSANCE sous la forme "jj/mm/yyyy" avec mm le numéro du mois et d’une
colonne DATE_EXAMEN selon le même format. On veut alors ajouter une colonne AGE qui
contiendra l’âge à la date de l’examen.
Les deux solutions12 sont :
1) df$AGE <- as.numeric( format( df$DATE_EXAMEN, "%Y")) as.numeric( format( df$DATE_NAISSANCE, "%Y"))
2) df <- transform( df, AGE= as.numeric( format( DATE_EXAMEN, "%Y")) as.numeric( format( DATE_NAISSANCE, "%Y"))
Certaines procédures d’analyse ne savent pas gérer les valeurs inconnues (codées NA par R),
d’autres ont un comportement tel que le résultat de l’analyse dépend de la présence ou non de
ces valeurs. Il est donc nécessaire de contrôler les individus ayant de telles valeurs. Souvent,
les procédures d’analyse possèdent un argument na.action pour cela. On peut aussi créer des
data.frame dans lesquels on a contrôlé l’existence de telles valeurs. R propose quatre
fonctions à ce propos :
na.fail( object, …)
retourne une erreur si l’objet contient des valeurs NA
na.pass( object, …)
retourne l’objet inchangé
12
Il faut d’abord extraire l’année de la chaîne de caractères représentant la date (fonction format) puis
convertir la chaîne de caractères représentant l’année en nombre.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 31
na.omit( object, …)
supprime toutes les lignes d’object contenant au moins
une valeur NA et retourne cet objet nettoyé
na.exclude( object, …)
se comporte comme na.omit sauf lors de l’extraction des
résidus d’une analyse ou la prédiction de nouvelles valeurs quand on veut tenir compte de
l’existence de valeurs NA.
On peut vouloir appliquer systématiquement à certaines colonnes une fonction particulière.
Pour cela on dispose de la fonction sapply (ou lapply) qui travaille alors colonne par colonne
en considérant le data.frame comme une liste, c’est-à-dire que sapply passe à la fonction
référencée par l’argument FUN, la colonne entière. Par exemple, on veut centrer et réduire
toutes les colonnes contenant des valeurs numériques :
col.num <- sapply( df, FUN=is.numeric)
sapply( df[,col.num], FUN=function( x){ ( x - mean( x)) / sd( x)})13
La première instruction retourne TRUE pour les colonnes numériques et FALSE pour les
autres tandis que la seconde applique l’opération de centrage et de réduction sur les colonnes
numériques en utilisant une fonction anonyme14.
Sous certaines conditions, apply et sweep sont applicables aux data.frame.
Exercice : Soit un data.frame
d.f <- data.frame( RID=paste( "sujet", 1:12, sep="."), matrix( rbeta( 240, 3, 5),
nrow=12, ncol=20, dimnames=list( NULL, paste( "test", 1:20, sep="."))))
A) on veut centrer les résultats des tests sujet par sujet, sur leur moyenne ;
- écrire le code sans utiliser sweep ni boucles ;
- écrire le code en utilisant sweep et vérifier qu’on obtient les mêmes résultats dans les
deux cas toujours sans boucles for.
B) faire la même chose mais colonne par colonne.
Supposons que le tableau de données contient une colonne TEST qui référence le type de test
effectué et deux colonnes TC et ERREUR qui recense le temps pris par le patient pour
effectuer le test et le nombre d’erreurs. On voudrait connaître le temps moyen et le nombre
d’erreurs faites en moyenne par test et tranche d’âge. On dispose alors de la fonction
aggregate dont l’argument de regroupement by doit être de type list :
df <- transform( df, CLASSE_AGE=cut( AGE, seq( 0, 119, by=10))
aggregate( df[,c( "TC", "ERREUR”)], by=list( df$TEST, df$CLASS_AGE),
FUN=mean)
La première instruction crée les classes d’âge allant de 10 en 10 tandis que la seconde calcule
les moyenne en ordonnant le data.frame résultant d’abord par TEST (la colonne sera étiquetée
Group.1) puis par classe d’âge (colonne étiquetée Group.2). La colonne x recevra le tableau
ou le data.frame des valeurs calculées, si la fonction FUN retourne un vecteur.
Si l’objet sur lequel porte l’agrégation (1er argument) comprend plusieurs colonnes,
par exemple sélectionnées à partir d’un data.frame (cf. supra), d’une matrice ou construit par
13
Il serait plus rapide d’utiliser la fonction scale (sapply( df[,col.num], FUN=scale) ). J’ai codé ainsi pour
illustration de l’utilisation des fonctions anonymes.
14
Cf. infra, le codage des fonctions
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 32
cbind, alors le data.frame de retour comprendra autant de colonnes que l’objet initial a de
colonnes, en plus des colonnes Group.n des facteurs de groupement. Les noms seront donnés
soit par les noms de colonnes, soit par défaut V1, V2…
Il peut aussi être nécessaire de restructurer un tableau de données car son format n’est pas
compatible avec celui utilisé par la procédure d’analyse. Le premier cas de figure est de
mettre sur une seule ligne les données pour un sujet à un âge donné (du tableau df vers le
tableau df.ligne) :
ID
AGE TC.TEST_1 ERREUR.TEST_1 TC.TEST_2 ERREUR.TEST_2...
Le second cas de figure consiste à partir d’un tableau en ligne comme celui présenté
précédemment (tableau df.ligne) pour en faire un tableau de type le tableau df.
On peut utiliser la fonction reshape pour gérer les deux cas de figure.
Cas 1 : df.ligne <- reshape( df, timevar="TEST", idvar=c( "ID", "AGE",
"CLASSE_AGE"), direction="wide", sep=".")
timevar
nom de la colonne à mettre en ligne
idvar
nom des colonnes de regroupement, donc ne variant pas et à
conserver
direction
direction de la restructuration, ici en format "wide"
sep
le séparateur entre le nom de la variable et celui du niveau de la
variable timevar (par défaut, c’est ".")
Cas 2 : df <- reshape( df.ligne, direction="long", varying=3:..., sep=".")
direction
format "long"
varying
numéros ou noms des colonnes à éclater, les autres seront
répétées pour chaque occurrence,
sep
séparateur pour déterminer d’une part le nom de la variable
résultat et d’autre par celui de la variable d’indiçage. La convention est nomVariableResultat
(cf. supra TC ou ERREUR) suivi du séparateur suivi de la variable d’indiçage (ou
temporelle). S’il y a plusieurs variables à restructurer (ici 2 : TC et ERREUR), on doit utiliser
les mêmes noms pour l’indiçage (e.g. TEST_1, TEST_2...)
Pour le premier cas on peut aussi utiliser la fonction melt du package reshape.
Jointure de deux data.frame
J’emploie ici la terminologie des bases de données relationnelles pour qualifier cette opération
de fusion entre deux data.frame.
Le problème que résout la fonction merge est assez courant quand on dispose au cours d’une
expérience de données sous forme de tableaux venant de systèmes d’acquisition différents ou
acquis à des moments différents. Supposons deux tableaux, le premier d.psycho recense des
résultats de tests neuropsychologiques et possède les colonnes suivantes :
ID
identifiant du sujet,
VISITE
identifiant de la visite (pour le suivi) numérotée de 1 à n,
...
résultats des tests
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 33
Le second d.bio recense des résultats d’analyses biologiques et possède les colonnes
suivantes :
BID
identifiant du sujet
VISITE
identifiant de la visite (pour le suivi) numérotée de 1 à n,
...
résultats des analyses.
On suppose que les identifiants des sujets et des visites sont codées identiquement.
Le problème à résoudre est de fusionner les deux tableaux afin d’avoir pour un sujet donné et
une visite donnée, à la fois les résultats des tests neuropsychologiques et des analyses
biologiques.
L’utilisation de la fonction merge pour réaliser cette opération est aisée, on lui donne comme
premier argument la référence au premier data.frame, comme second argument, la référence
au second data.frame puis on lui donne les noms des colonnes qui serviront à la jointure,
d’abord celles du premier data.frame (argument by.x) et leur correspondant, position par
position, dans le second data.frame (argument by.y). On obtient alors l’appel :
merge( d.psycho, d.bio, by.x=c( "ID", "VISITE"), by.y=c( "BID", "VISITE"))
Si les colonnes servant à la fusion avaient les mêmes noms, l’appel aurait été plus simple en
utilisant l’argument by :
merge( d.psycho, d.bio, by=c( "ID", "VISITE"))
On peut aussi vouloir changer les noms des colonnes afin de donner les mêmes noms aux
colonnes servant à la jointure :
col.bio <- match( c("BID", "VISITE"), names( d.bio))
names( d.bio)[col.bio] <- c( "ID", "VISITE")
On peut se trouver confronté au fait que les colonnes ne sont pas codées de la même manière,
par exemple dans d.psycho, les visites seront numérotées de 1 à n et dans d.bio, elles seront
identifiées par un code alphanumérique exprimant le nombre de mois après la 1ère visite :
"0m", "6m" ... Il faut donc transcoder une des deux colonnes avant de fusionner les tableaux.
Le problème sera simple si aucune des colonnes n’est un facteur, il peut être plus complexe
dans le cas de facteurs. Dans ce dernier cas et pour éviter tout problème, on commence par
transformer la colonne du facteur en chaîne de caractères par l’instruction suivante :
as.character( d.x$facteur)
puis on rétablit le facteur après l’opération de fusion.
On commence donc d’abord par tester si les colonnes sont des facteurs :
fac.psycho <- sapply( d.psych, FUN=is.factor)
fac.bio <- sapply( d.bio, FUN=is.factor)
col.facteurs <- any( c( fac.psycho[c( "ID", "VISITE")], fac.bio[c("BID", "VISITE")]))
if( col.facteurs)
# au moins une colonne est un facteur
Ensuite on transforme les valeurs dans les nouvelles valeurs. Une méthode efficace est de
baser la transformation sur l’utilisation de la fonction switch qui prend comme premier
argument la variable de longueur 1 à transformer puis une série d’arguments de type
ancienneValeur=nouvelleValeur. À noter le l’argument nouvelleValeur sera la valeur par
défaut :
switch( a, 0, a=1, b=2)
donnera 1 si a == "a", 2 si a == "b" et zéro dans tous les autres cas.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 34
Dans le problème actuel, il ne s’agit pas d’une variable de longueur 1 mais d’un vecteur. Pour
le parcourir on utilise alors la fonction sapply. Mais quel est la fonction à donner à l’argument
FUN ? Dans notre cas, on utilisera une fonction anonyme qui s’écrira :
function( x) { switch( x, lesAlternatives)}
Le fonctionnement de sapply fera qu’il passe à chaque fois un élément du vecteur.
Nous avons un second problème dû aux valeurs que prend la colonne qu’on veut transformer,
soit c’est d.psycho$VISITE et ce sont des valeurs numériques, soit c’est d.bio$VISITE et ce
sont des valeurs qui commencent par un chiffre. Ces codes ne peuvent pas être utilisés à
gauche de symbole d’égalité dans les arguments de switch puisque R n’acceptent aucun nom
de variable commençant par un chiffre ou incluant un blanc ou un caractère spécial. Si ce ne
sont que des chiffres, une des possibilités est de préfixer la valeur par un caractère grâce à la
fonction paste :
d.psycho$VISITE <- paste( "i.", d.psycho$VISITE, sep="")
# il ne doit pas y
avoir de blanc.
L’autre possibilité est d’entourer la valeur de la paire de caractères d’échappement ` `
(apostrophe inversée). Ainsi, on pourra par exemple écrire l’instruction de transcodage ainsi :
d.bio$VISITE <- sapply( d.bio$VISITE, FUN=function( x)switch( x, `0m`=0, `6m`=1,
…))
Enfin, il est possible que certains résultats de tests neurophysiologiques soient indisponibles
alors que les analyses biologiques le sont, ou l’inverse. Dans ce cas, il n’y aura donc pas
correspondance parfaite entre les deux tableaux. On peut alors vouloir ne conserver que les
couples (sujet, visite) complet, ce qui est le comportement par défaut de merge, soit conserver
tous les résultats de tests neurophysiologiques même si les résultats des analyses biologiques
ne sont pas disponibles, ou l’inverse, ou avoir tous les résultats. Ce comportement est contrôlé
par les arguments all.x, all.y et all (raccourci pour all.x et all.y). Si all.x, all.y ou all est TRUE,
les valeurs absentes seront remplacées par la valeur NA.
Formule
La dernière classe d’objets de R que je présenterai est rarement présentée comme tel dans la
littérature bien qu’elle joue un rôle majeur dans les analyses. Cette classe est celle nommée
formula (les modèles en langage statistique) qui explicite les relations entre différents
vecteurs de données. Une formule est facilement reconnaissable par le fait qu’elle ne
comporte aucune chaîne de caractères mais qu’elle contient le caractère ~ (tilde) quelquefois
traduit par l’expression ‘est expliqué par’. En effet, la partie gauche avant le ~ référence les
données à expliquer ou dépendantes et la partie droite la structure des données indépendantes
ou explicatives. Dans certaines utilisations, la partie gauche de la formule peut être absente.
Si la formule est utilisée dans la cadre de la manipulation d’un data.frame, comme c’est
souvent le cas, la formule ne référence que les noms des colonnes, sinon elle doit référencer
des vecteurs de données de même longueur.
Je donnerai ici les principales constructions de formule. Il est nécessaire de se reporter à la
description de la procédure l’utilisant pour les détails.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 35
Construction de la partie gauche
Y
une variable dépendante, vecteur de données
cbind( Y.1, Y.2...)
ou une matrice pour une analyse multi-variées sur les
variables Y.1, Y.2, ...
Surv( ...)
pour les analyses de survie
I( …)
les opérateurs +, -, * et ^ à l’intérieur des parenthèses ne
seront pas interprétés comme des opérateurs associés aux formules mais des opérateurs
numériques ; les variables doivent donc être des variables numériques ;
f( Y)
avec f une fonction quelconque, par exemple log.
Construction générale de la partie droite
S’il n’y a qu’une variable, on la notera X :
Y~X
Y est expliqué par X
Quand il y a plusieurs variables dépendantes, on note par
+
le fait qu’on s’intéresse à leurs effets séparément
:
le fait qu’on s’intéresse à leur interaction.
Ceci donnera :
Y ~ X.1 + X.2
Y est expliqué par les effets de X.1 et de X.2 qui
n’interagissent pas entre eux ;
Y ~ X.1 + X.2 + X.1:X.2
Y est expliqué par les effets de X.1 et de X.2 et
par leur interaction, le : dénotant l’interaction.
Le dernier schéma peut être simplifié en utilisant l’opérateur * :
Y ~ X.1 * X.2
est équivalent à
Y ~ X.1 + X.2 + X.1:X.2
À noter aussi que
Y ~ (X.1 + X.2)^2 est équivalent aux écritures ci-dessus puisque
X.1:X.1 = X.1 et X.1 + X.1 = X.1
Autres opérateurs utilisables :
I( …)
les opérateurs +, -, * et ^ à l’intérieur des parenthèses ne seront pas
interprétés comme des opérateurs associés aux formules mais des opérateurs numériques ; les
variables doivent donc être des variables numériques ;
f( Y)
avec f une fonction quelconque, par exemple log
offset( …)
terme à ajouter à une prédicteur linéaire avec un coefficient connu
valant 1 au lieu d’estimer le coefficient.
Quand le modèle sous-jacent est hiérarchique, il existe un effet principal du facteur de plus
haut niveau dans la hiérarchie, puis une interaction entre le facteur immédiatement inférieur et
le facteur, puis une interaction triple au niveau suivant. Supposons donc qu’on ait trois
niveaux dans la hiérarchie : PATHOLOGIE, CENTRE, MACHINE telles qu’on s’intéresse
aux différences entre images entre différentes pathologies, que ces images sont faites dans des
centres différents mais qu’un centre ne fait des images que d’une et une seule pathologie et
que chaque centre dispose de plusieurs machines. Pour modéliser ce cas de figure on pourra
utiliser une construction à partir de l’opérateur / :
PATHOLOGIE/CENTRE/MACHINE
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 36
qui est formellement équivalente à
PATHOLOGIE + PATHOLOGIE:CENTRE + PATHOLOGIE:CENTRE:MACHINE
Dans les analyses statistiques, on suppose par défaut qu’il existe une ordonnée par origine. Il
est donc inutile de la noter. Si on veut forcer cette ordonnée à zéro, on peut utiliser l’une des
constructions suivantes :
i) Y ~ 0 + X.1 * X.2
ii) Y ~ X.1 * X.2 - 1
Note importante sur la notion d’ordonnée à l’origine nulle
Quand on reprend ce qui a été dit sur les contrastes, forcer l’ordonnée à l’origine
comme étant nulle va conduire à des résultats différents en fonction des contrastes
utilisés. Si on utilise des véritables contrastes, c’est-à-dire ceux dont la somme des
coefficients est nulle, cette contrainte est équivalente au centrage des variables
dépendantes, c’est-à-dire au fait que leur grande moyenne est nulle. Cette
contrainte n’a de sens que dans ce cas. Dans le cas d’un contraste de type
cont.Treatment, cela revient à poser que le coefficient ϕ d’un des niveaux du
facteur est imposé comme nul. En termes de significativité des coefficients β
estimés, la non significativité de l’effet principal du facteur ou des interactions
conduira à des coefficients β non significatifs (fonction summary) dans le cas des
contrastes dont la somme des coefficients est nulle. Par contre, ce ne sera pas
obligatoirement le cas pour contr.Treatment qui peut conduire à des coefficients β
très significatifs.
Pour les régressions, l’interprétation est plus simple mais peut conduire à des
résultats erronés. Pour améliorer les estimations, on peut utiliser cette contrainte
avec le centrage des variables dépendantes et indépendantes.
On peut réaliser les mêmes centrages pour les analyses de type Ancova et utiliser
la contrainte de l’ordonnée à l’origine nulle à condition de s’assurer :
1) que les variables dépendantes sont centrées sur la même valeur dans tous les
groupes ;
2) que les variables indépendantes servant de régresseurs le sont aussi.
Si ces contraintes ne sont pas respectées, les résultats peuvent être aberrants. Par
contre, si ces contraintes sont respectées, cela conduira à une estimation plus
puissante des différences entre pentes associées aux différents groupes.
Dans les régressions, on peut vouloir utiliser des transformées des variables ou forcer que les
coefficients de deux (ou plus de deux) variables soient identiques. On utilise alors la fonction
I qui inhibe l’interprétation des opérateurs au sens des formules.
Exemples :
1) régression polynomiale cubique Y ~ X + I(X^2) + I(X^3)
2) X.1 et X.2 doivent avoir le même coefficient
I( X.1 + X.2)
3) emploi de fonctions arithmétiques Y ~ I( log( X))
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 37
On peut aussi vouloir ajouter une variable qui décalera les données et dont on force le
coefficient à 1 par défaut. Cette variable est signalé par la fonction offset :
Y ~ X + offset( Z)
dans le contexte de df
est équivalent à l’analyse
Y~X
dans le contexte de transform( df, Y=Y-Z)
Si le coefficient de Z est une valeur quelconque k, on écrira :
Y ~ X + offset( k * Z)
Exercice :
# On construit un tableau de données
d.f <- data.frame( X=runif( 20, min=0, max=5))
d.f <- transform( d.f, Y=X + rnorm( 20, sd=0.2))
d.f <- transform( d.f, MEAN.Y=mean( Y))
# On compare les résultats des régressions suivantes
lm.std <- lm( Y ~ X, d.f)
lm.centre <- lm( Y - MEAN.Y ~ X, d.f)
lm.offset <- lm( Y ~ X + offset( MEAN.Y), d.f)
lm.zero <- lm(Y ~ 0 + X + MEAN.Y, d.f)
Utiliser la fonction summary pour récupérer les résultats et interprétez-les.
Autres arguments
Je ne présenterai que deux arguments qu’on rencontre souvent dans les formules.
|
est un séparateur dont la partie droite représente le (ou les facteurs) de regroupement et
la partie gauche ce qui est regroupé. Par exemple, dans le package lattice qui permet des
visualisations graphiques sophistiquées, on voudra représenter séparément Y ~ X en fonction
d’un troisième facteur Z. On écrira alors :
xyplot( Y ~ X | Z, df)
Ce séparateur est aussi utilisé dans la spécification des modèles mixtes analysé par les
fonctions lme du package nlme et lmer du package lme4.
Error permet dans la procédure aov15 de donner le modèle des erreurs et donc d’indiquer les
corrélations entre elles. Cette fonction prend comme arguments le schéma de regroupement
des erreurs. Par défaut, toutes les procédures supposent que d’une ligne à une autre du tableau
à analyser, les erreurs sont indépendantes et identiquement distribuées (iid) :
yj = β jxj +ε j
avec :
Exi x j = σ 2δ ij
Qui conduit par exemple au modèle :
TC ~ TEST * VISITE
15
aov ne peut être utilisé que pour les analyses sur des groupes équilibrés.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 38
Dans le tableau d.psycho, l’erreur sur la mesure du temps de réaction à chaque visite se
compose d’une erreur de mesure indépendante du sujet et de la visite et d’une erreur
spécifique de chaque sujet. La formulation précédente est fausse et conduira à des résultats
biaisés. Si on veut prendre en compte la relation entre les erreurs via le sujet, on ajoutera donc
au modèle un modèle d’erreur identique pour tous les sujets sous la forme :
TC ~ TEST * VISITE + Error( ID)
Ce modèle gère alors des résultats de types multi-variés mais pas de type mesures répétées car
on ne modélise pas la relation entre les visites qui est le facteur intra-sujet. La corrélation
entre les visites dépend du sujet et le schéma de corrélation entre sujet et visite est le même
pour tous les sujets. Le modèle d’erreur conduit donc à poser :
TC ~ TEST * VISITE + Error( ID/VISITE)
On peut aussi considérer qu’il existe une corrélation entre les tests chez le même individu (par
exemple, via un effet d’ordre parce que les tests sont effectués dans le même ordre chez tous
les individus) sans qu’il y ait un effet d’habituation aux tests au travers des visites et que le
schéma de corrélation est le même pour tous les sujets. Dans ce cas, le modèle devient :
TC ~ TEST * VISITE + Error( ID/(VISITE + TEST))
Pour modéliser cet effet longitudinal entre les tests, on pourra écrire:
TC ~ TEST * VISITE + Error( ID/(VISITE + VISITE:TEST))
# pas d’effet d’ordre
entre les tests
ou
TC ~ TEST * VISITE + Error( ID/(VISITE * TEST))
# tous les effets
possibles.
Dans les trois exemples ci-dessus, VISITE et TEST sont des facteurs intra-sujet et les
différentes écritures permettent de poser des modèles intra-sujet différents.
Par contre cette écriture basé sur l’utilisation d’Error ne permet pas de prendre en compte des
corrélations entre les erreurs dues indépendamment à des effets sujet et test (par exemple, lié à
la difficulté du test ou à la charge cognitive comme dans des tests de type n-back), c’est-à-dire
des modèles d’erreurs qui pourraient répondre à un modèle du type :
Error( ID + TEST)
ATTENTION : Cette construction avec Error n’est pas valable pour toutes les analyses.
Voir la documentation de la procédure. De plus, elle est rarement la plus efficace et
généralement peut être avantageusement remplacée par l’utilisation de modèles
mixtes16.
Utilitaires
Quand la formule est complexe, il est souvent commode de la construire d’abord sous forme
d’une chaîne de caractères puis de la transformer en formule. C’est le rôle du constructeur
formula.
Pour tester si une variable contient une formule, on utilisera la fonction inherits :
inherits( f, "formula")
16
L’utilisation de ce type de modèle et le codage des formules qui lui sont associées vont au-delà de ce
présent document (cf. les packages nlme et lme4 et les documentations associées)..
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 39
Les procédures d’analyse comme lm, aov ou glm utilisent les formules. Elles sont aussi
utilisées par des fonctions donnant des statistiques récapitulatives (summary statistics) comme
la fonction aggregate. Elles peuvent aussi être utilisées par les fonctions graphiques.
Il existe un certain nombre d’utilitaires permettant la manipulation des formules :
terms
décrit les composants de la formule
all.vars
liste toutes les variables utilisées par la formule
model.frame construit un data.frame spécifique de la formule
model.matrix construit la design matrix associée à la formule.
Pour les détails, voir la documentation.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 40
Scripts
Charger du code
R est un langage de programmation. On peut donc écrire des scripts ou des fonctions qui
pourront être réutilisés. Ces scripts et fonctions peuvent être regroupés au sein d’un package
mais la construction d’un package pour qu’il soit reconnu comme une library demande une
stratégie spéciale qui va au-delà de ce document.
On peut cependant créer des fichiers contenant ses propres scripts et ses fonctions utilitaires.
Ces fichiers peuvent être chargés par la commande source ou l’item sourcer du code du menu
Fichiers. Quand on charge ainsi son code, les fonctions seront mises en attente d’utilisation
tandis que les autres scripts seront exécutés au chargement.
Pour connaître les objets présents en mémoire, on peut appeler la fonction ls.
Affectation d’une valeur à une variable
Ce paragraphe ne traite que les stratégies d’affectation les plus couramment utilisées.
L’opérateur le plus souvent utilisé pour assigner une valeur à une variable est <-, composé du
caractère inférieur et moins. Il affecte la valeur placée à sa droite à la variable placée à sa
gauche. Il peut être utilisé plusieurs fois sur la même ligne :
a <- b <- c <- 0
affecte 0 à la variable c puis affecte le contenu de c à b...
Il faut éviter ce genre d’écriture qui devient facilement illisible, par exemple :
a <- ( (c <- 0) + 1)
commence déjà être difficile d’interprétation.
On peut utiliser cet opérateur quand on passe un argument à une fonction :
View( x <- maFonction( ...))
C’est particulièrement utile lors de la mise au point du programme.
Il est à noter que R accepte la notation inverse -> où c’est la valeur à gauche qui est affectée à
la variable de droite.
R accepte aussi le caractère égal (=) comme opérateur d’affectation. Il n’est cependant pas
conseillé car il est utilisé dans certains contextes avec des significations différentes. Par
exemple y <- f2( x=z) et y <- f2( x <- z) ne signifie pas la même chose. Dans le premier cas,
on affecte la valeur z au paramètre x de la fonction f2, dans le second cas, on crée (ou utilise)
une variable x dans l’environnement appelant à qui on affecte la valeur z qui est aussi passée
comme premier argument de la fonction f2.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 41
Structures de contrôle
On appelle structure de contrôle toutes les constructions qui permettent de structurer le
programme notamment de programmer des tests ou des boucles.
Tests
Nous avons vu précédemment la construction conditionnelle avec la fonction ifelse pour
affecter une valeur de longueur 1 à une variable conditionnellement à une condition logique.
La construction peut être plus complexe et on peut vouloir exécuter une suite d'instructions ou
calculer une valeur dont la longueur est supérieure à 1. ifelse est alors insuffisant pour cela et
pour exécuter une suite d’instructions conditionnellement à une condition logique, on utilise
la construction
if( condition) {
à exécuter si la condition est vraie
}
S’il n’y a qu’une instruction on peut ne pas mettre les accolades mais je les recommande pour
la lisibilité du code.
S’il y a une suite d’instructions alternatives, on écrira :
if( condition) {
à exécuter si la condition est vraie
} else {
à exécuter si la condition est fausse
}
S’il y a plusieurs conditions à tester, on utilise la construction
if( condition1) {
...
} else if( condition2) {
...
} ... {
# autres else if éventuels
} else {
…
}
Il ne faut jamais avoir de fin de ligne entre } et else sinon R peut générer une erreur de
syntaxe.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 42
Cette construction if/else if/else peut retourner toujours la dernière valeur évaluée. Ainsi, elle
est une alternative à la fonction ifelse, surtout s'il y a plus d'une condition. En effet les
écritures suivantes sont équivalentes :
if( condition1) {
var <- if( condition1) {
...
...
var <- val1
val1
} else if ( condition2) {
} else if ( condition2) {
....
....
var <- val2
val2
} ... else {
} ... else {
...
...
var <- val.def
val.def
}
}
Boucles
La principale structure de construction de boucle est basée sur l’instruction for et s’écrit
for( variableDeBoucle in vect) {
instructions pour la valeur courante de variableDeBoucle
}
La variableDeBoucle parcourt les éléments du vecteur vect dans l’ordre où ils sont rangés. Si
vect est vide, R n’entre pas dans la boucle.
while( condition) {
...
} répète les instructions entre accolades tant que condition est vrai.
repeat {
...
} répète indéfiniment les instructions entre accolades.
On peut dans les boucles vouloir sortir avant la fin de boucle (ou sortir tout court pour repeat)
sur une condition donnée. Cela se fait par l’instruction break et donne une construction du
type :
while( condition) {
...
if( conditionSortieAnticipée) {
break
}
...
}
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 43
De façon similaire, on peut vouloir ne pas exécuter certaines instructions si une condition est
réalisée. On peut le faire de deux manières différentes :
while( condition) {
while( condition) {
...
...
if( !conditionDEchappement) {
if( conditionDEchappement) {
instructionsAExécuterSiFaux
next
}
}
}
instructionsAExécuterSiFaux
}
Fonctions d’itérations
Il convient d’éviter l’utilisation de boucles de type for qui sont peu efficaces, tout comme cela
est le cas pour MALAB®. R offre des fonctions permettant de remplacer efficacement les
boucles.
La première de ces fonctions est la fonction apply applicable à une matrice ou à un tableau
(cf. supra). Elle comporte trois arguments :
X
: le tableau à traiter,
MARGIN
: la ou les dimensions du tableau sur laquelle ou lesquelles s’applique,
FUN
: la fonction,
: arguments supplémentaires à passer à FUN.
…
Cette fonction peut être une fonction déjà existante ou être une fonction anonyme
ayant au moins un argument. De plus, elle peut retourner une seule valeur (exemple, la
fonction mean) ou un vecteur de valeurs. Dans ce dernier cas, apply retourne une matrice
ayant un nombre de lignes égal à la dimension du vecteur retourné et un nombre de colonnes
égal au nombre de fois où FUN est appliquée.
Il existe aussi une fonction dérivée sweep.
Il existe aussi une série de fonctions s’appliquant à des listes (list) ou des vecteurs (vector). Je
ne présenterai ici que deux de ces fonctions : sapply et replicate qui sont les plus utilisées.
sapply est une fonction très générale qui prend 5 arguments :
X
: le vecteur ou la liste à traiter,
FUN
: la fonction à appliquer (cf. supra la description de FUN dans apply),
…
: arguments supplémentaires à passer à FUN,
simplify
: si FALSE, renvoie une liste. Si TRUE (défaut) essaie de simplifier si possible,
généralement sous forme de vecteur ou de matrice.
USE.NAMES : si TRUE et X est de type caractères, alors utiliser X pour identifier le résultat.
Puisque simplify et USE.NAMES sont après …, il faut obligatoirement les nommer.
Exemples :
sapply( X, FUN=f, FALSE)
# FALSE est considéré comme étant l’un pas paramètres
remplaçant …, c’est-à-dire qu’il est considéré comme un paramètre anonyme passé à la
fonction f
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 44
sapply( X, FUN=f, simplify=FALSE)
# on ne simplifie pas le résultat retourné car
simplify est un paramètre nommé de la fonction sapply.
Il faut noter que la valeur de l’argument simplify n’est jamais passée à la fonction f.
De ce fait, si f possède un argument de nom simplify sans valeur par défaut, celui-ci ne
recevra jamais de valeurs, ce qui provoquera un erreur. S’il a une valeur par défaut, c’est elle
qui sera toujours prise dans le corps de f.
replicate permet de répliquer n fois l’expression qui est en second arguments. ATTENTION :
il s’agit ici d’une expression et nom de la référence à une fonction. replicate a donc un
intérêt quand il s’agit de répliquer n fois une expression qui contient un aléa. Si l’expression
retourne un vecteur, replicate retourne une matrice ayant la même structure que celle
retournée par apply.
Ci-dessous quelques exemples d’utilisation de ces fonctions :
1) Calcul de statistiques sur les lignes d’une matrice
mat <- matrix( rnorm( 10000), ncol=1000)
apply( mat, 1, FUN=function( x, probs) c( MEAN=mean( x), SD=sd( x),
QUANT=quantile( x, probs)), probs=c( 0.025, 0.50, 0.975))
apply( mat, 1, median)
# Calcul de la médiane
2) Colonnes d’un data.frame qui sont des facteurs
df <- data.frame( …)
sapply( df, FUN=as.factor)
3) Ajout d’une colonne à un data.frame dont la valeur est calculée à partir de la valeur
d’autres colonnes
df <- data.frame( VAL=rnorm( 100), F=rep( c( "A", "B"), each=50))
df$S <- sapply( seq_len( nrow( df)), FUN=function( n.row)
switch( df$F[n.row], A=sign( df$VAL[n.row]), B=-sign( df$VAL[n.row])))
4) Bootstrapper la moyenne d’un vecteur
replicate( 1000000, apply( mat[,sample.int( ncol( mat), replace=TRUE)], 1, mean))
Exercice :
A) Soit un data.frame comportant deux colonnes, l’une la date de naissance, l’autre la date
d’examen sous forme de chaîne de caractères jj/mm/aa où aa est l’année sur deux chiffres.
Calculer l’âge en année de chaque sujet lors de l’examen (cf. as.Date). Comment gérer le
changement de siècle et le fait que certains sujets sont nés avant 1970 ? Comment gérer le fait
que certains sujets n’ont pas encore subi d’examen et donc que la date est codée par NA ?
B) Soit un tableau (array) à 3 dimensions mesurant plusieurs fois dans le temps divers
grandeurs (lignes), dans différentes conditions (colonnes), la répétition des mesures étant la
3ème dimension (temps) :
mes.array <- array( 0, dim=c( nb.variables, nb.conditions, nb.temps) …
vect.temps <- # Temps des mesures
i) On veut vérifier que les mesures sont stables dans le temps. On va donc faire une
régression individuelle sur la variable temps de chacune des variables dans chacune des
conditions et construire la matrice variablesXconditions de p.value de la pente de régression.
Utiliser la fonction apply pour cela.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 45
ii) On suppose que la stabilité est vérifiée, on veut alors vérifier l’effet des conditions
en considérant les mesures dans le temps comme des occurrences indépendantes de la même
mesure. On va donc construire un vecteur des p.value, un élément par variable. Utiliser aussi
apply.
N.B. : la fonction à utiliser est lm et on retourne la p.value associée à la table anova obtenue
par la fonction anova.
C) Soit une matrice de 3 colonnes et 256 lignes dont les valeurs sont comprises entre 0 et 1
correspondant à une LUT (look-up table), la première colonne correspond donc à l’intensité
du rouge, la seconde, du vert et la troisième du bleu. R code les couleur sous forme de chaines
de caractères en hexadécimal commençant par le caractère dièse ("#"), les deux caractères
suivant étant le niveau de rouge, codé de 0 à FF (équivalent du 255 décimal), ensuite on a le
niveau du vert puis celui du bleu. Écrire les lignes de code permettant de transformer la
matrice en couleurs pour R (ne pas utiliser de boucles for).
Gestion des erreurs
L’exécution d’un programme peut provoquer des messages d’avertissement (fonction
warning) ou des messages d’erreur (fonction stop). En général, dans le premier cas, il affiche
l’avertissement à la console et continue ; dans le second cas, le programme s’arrête et sort. On
peut modifier ces comportements par défaut, soit pour toute la session en modifiant les
paramètres warn et error par la fonction options (c’est-à-dire options( warn=…) ou options(
error=…)), soit localement en utilisant la construction tryCatch qui permet de définir le
comportement à suivre pour l’instruction courante et comment terminer.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 46
Visualiser les valeurs
Généralités
On peut visualiser le contenu d’une variable atomique, d’un vecteur, d’une matrice ou d’un
data.frame dans une fenêtre par la procédure View.
Tous les objets possèdent une méthode print par défaut qui est appelée quand on tape le nom
de la variable à la console. Cette méthode formate le contenu de la variable sous la forme
d’une chaîne de caractères selon un certain format, cette chaîne sera alors envoyée au
périphérique de sortie (par défaut, la console). Le format peut être différent du contenu de
l’objet tel qu’il est donné par la fonction str (structure de l’objet). Par exemple, comparer :
lm.objet <- lm( …)
str( anova( lm.objet))
et
anova( lm.objet)
ou
str( summary( lm.objet))
et
summary( lm.objet).
La méthode print associée à un objet de la classe des fonctions donne le code source de la
fonction si celui-ci est disponible.
cat est une simplification de print.
Il existe d’autres fonctions plus souples permettant l’impression de valeurs : format, sprintf…
Elles permettent des formatages plus sophistiqués au prix d’une plus grande difficulté dans la
spécification.
Lors de la mise au point d’un script, on peut utiliser ces fonctions d’affichage sur la console,
notamment pour suivre l’évolution d’une boucle. Il faut cependant prendre en compte que ces
fonctions n’écrivent pas directement sur la console mais dans un buffer, une zone spéciale de
mémoire, qui est vidé (écrit) sur la console de manière asynchrone. De ce fait, dans une
boucle, l’affichage ne se fera pas à chaque fois que la boucle trouve la fonction d’écriture
mais par rafales. Par exemple, les valeurs à afficher pour les 10 premiers passages de la
boucle pourront être affichés en une seule fois alors que la boucle en est à son quinzième
passage.
Visualiser à la console
La fonction de visualisation standard d’une donnée View, visualise celle-ci sous forme d’un
tableau. L’objet à visualiser (argument x) doit donc avoir une méthode as.data.frame qui lui
est attachée. Moyennant l’écriture de cette méthode, on peut visualiser à la console n’importe
quel objet d’une classe qu’on a créée.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 47
Exemple :
class( x)
# retour c( "MA.CLASSE", "list")
if( exists( "as.data.frame.MA.CLASSE")) {
View( x)
} else {
warning( "Visualisation par la méthode par défaut associée aux listes")
View( x)
}
Imprimer sur la console
Quand on appelle une fonction sans renvoyer son résultat dans une variable, le retour peut être
silencieux si la valeur retournée est déclarée invisible dans la fonction, sinon ce retour sera
formaté par la méthode print associée à la classe de l’objet. S’il n’existe pas de méthode print
spécifique, R utilisera la méthode par défaut.
Pour forcer l’impression sur la console d’un retour invisible, mettre l’appel de la fonction
comme argument de print.
Pour formater de manière plus générale l’objet, par exemple, pour le stocker dans un fichier
texte, on peut aussi utiliser la méthode format et créer son propre format pour une classe
donnée d’objet.
Gestion des fichiers, lire et écrire des données sur disque17
Nous avons vu précédemment comment R pouvait charger et sauver du code source (fonction
source), l’historique des commandes (loadhistory, savehistory) ou de l’environnement (load,
save, save.image). Par défaut, c’est-à-dire si on ne donne pas les chemins d’accès complets, R
utilise le répertoire de travail. La fonction getwd permet de le connaître et la fonction setwd
d’en changer. Voir le document spécifique pour connaître comment définir un répertoire par
défaut pour une session.
Système de fichiers
list.files permet de connaître la liste des fichiers dans un répertoire donné et, éventuellement,
dans ses sous-répertoires. D’autres fonctions d’accès bas niveau au système de fichier natif de
l’ordinateur sont listées dans la page files de la documentation.
file.path permet de construire un nom de fichier dont la syntaxe suit celle du système
d’exploitation. Il faut cependant noter que l’utilisation du / en tant que séparateur est reconnu
par R sous Windows.
La page files2 présente les fonctions de manipulation des répertoires et des permissions
d’accès aux fichiers.
17
Voir le manuel R Data import / export pour une description complète.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 48
Accès aux fichiers de données
On retrouve généralement des paires de fonction commençant par read (par exemple,
read.table) pour la lecture et par write (par exemple, write.table) pour l’écriture. On peut se
baser sur les codes générer par l’option Importer des données du menu Données de Rcmdr
pour coder ses propres accès.
Il est souvent avantageux de créer des fichiers texte (avec comme séparateur la
tabulation : extension .txt), ou des virgules ou des points virgules (extension .csv)
qui peuvent être facilement contrôlés grâce à n’importe quel éditeur de texte et qui
sont lus par la grande majorité des tableurs et autres logiciels statistiques et qui, de
plus, sont indépendants de la plateforme. De plus, la fonction read.table permet de
contrôler la lecture (le type de séparateur décimal, point ou virgule ; le séparateur
entre les champs ; les entêtes...)
Exemple : lecture d’un fichier avec entêtes (noms) des colonnes, séparateur
décimal codé par la virgule et séparateur de champ, la tabulation
read.table( "mon_fichier.txt", header=TRUE, sep="\t", dec=",")
ATTENTION : Si des valeurs contiennent des # ou des simples ou doubles
apostrophes (par exemple, numero d’immatriculation), read.table lira mal les
données ou générera des erreurs car par défaut # est la marque du début d’un
commentaire et les simples ou doubles apostrophes, celles de chaînes de
caractères. Il faut alors modifier les arguments comment.car et/ou quote.
Pour écrire les données dans des fichiers texte, on peut utiliser la fonction
write.table. Je conseille de toujours mettre l’argument row.names à FALSE alors
qu’il est TRUE par défaut.
Description des principaux arguments de read.table
file
: nom du fichier
. par rapport au répertoire de travail (cf. getwd, setwd)
. chemin absolu. On peut le donner en clair (par exemple, sous Unix,
/usr/maZone/dataDirectory/mesDonnees.txt), soit lui demander de le construire en lui donnant
tous les sous répertoires et le nom du fichier, grâce à la fonction file.path.
A noter : Bien que le séparateur sous Windows soit le backslash, R reconnaît sous Windows
un chemin d’accès qui utilise comme séparateur le slash. Pour être indépendant de la plateforme, je conseille de toujours utiliser le slash comme séparateur.
header
: variable logique indiquant si la première ligne du fichier contient les
noms des colonnes.
row.names
: permet d’indiquer les noms des lignes. Par défaut, pas défini donc pas
de noms de ligne stockés dans le fichier.
. vecteur de chaînes de caractères : noms à donner aux lignes. Attention : s’il y a
moins de noms que de lignes dans le fichier. Donc option déconseillée.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 49
. numéro de la colonne contenant les noms des lignes. Si header=TRUE, cette colonne
doit avoir un nom ou être un emplacement vide.
. nom de la colonne contenant les noms des lignes.
quote
: caractères servant de délimiteurs aux chaînes de caractères.
A noter : i) par défaut, toute colonne ne comportant pas des nombres, des valeurs logiques
(TRUE, FALSE) ou les chaînes définies dans l’argument na.strings sont considérées comme
des chaînes de caractères. De ce fait, il est inutile de coder ces séparateurs dans le fichier de
données. Par exemple : "Alzheimer" et Alzheimer sont tous les deux traités comme des
chaînes de caractères contenant le mot ‘Alzheimer’.
ii) à chaque fois que R rencontre un des caractères indiqués dans cet argument, il considère
comme indiquant le début d’une chaîne si c’est une occurrence impaire, la fin d’une chaîne
pour une occurrence paire. Par exemple, si quote="\"'", la chaîne ‘numero d'immatriculation’
va provoquer une erreur.
na.strings
: vecteur de chaînes de caractères qui seront traduites par R comme
dénotant l’absence de valeur. Par défaut, vaut NA.
i) une colonne vide, c’est-à-dire sans valeur entre deux séparateurs de colonnes est considéré
comme ayant la valeur NA ;
ii) si une colonne contient des nombres, quand R rencontre l’une des chaînes, il la traduit
comme NA ; si la chaîne rencontrée n’est pas dans la liste des na.strings, la colonne sera
considérée comme contenant des chaînes de caractères.
comment.char
: caractères servant de à signaler que la fin de la ligne est un
commentaire. Quand R rencontre ce caractère, il considère que la fin de la ligne est un
commentaire et arrête sa traduction, ce qui peut générer une erreur si la chaîne de caractères
contient ce caractère. Si on ne veut pas prendre en compte ce délimiteur de commentaire, on
peut écrire comment.char="".
sep
: caractère servant de délimiteur entre les colonnes, souvent la
tabulation ("\t"). Dans le format csv, c’est la virgule.
dec
: pour les nombres, délimiteur décimal, le point chez les anglo-saxons,
la virgule en France.
stringsAsFactors
: indique si R doit considérer que les colonnes contenant des chaînes de
caractères contiennent en réalité les niveaux d’un facteur ; et donc que la variable est un
facteur.
Par défaut, cet argument prend la valeur de l’option globale stringsAsFactors (cf. la
page d’aide de option pour voir comment configurer les options par défaut de R).
A noter : i) la fonction data.frame possède aussi cet argument avec la même valeur par défaut.
ii) Si toutes les colonnes contenant des chaînes caractères représentent des facteurs et qu’on
est sûr du codage des noms des niveaux, alors stringsAsFactors=TRUE est le choix à faire. Si
par contre, on doit ensuite grouper des tableaux de données provenant de différentes sources
où le même facteur est codé différemment ou si on veut recoder les niveaux, il est préférable
de mettre cet argument à FALSE et d’appeler ensuite explicitement la fonction factor. Il en va
de même si on veut ordonner les niveaux d’un facteur. En effet, par défaut, R ordonne les
niveaux selon l’ordre lexicographique.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 50
Exemple : dans un fichier les visites sont codées bs pour la première visite puis
nombre de mois suivi de m pour les visites suivantes ; dans un second fichier, elles ne sont
codées que par la lettre v suivie d’un chiffre, 1, pour la première visite, 2 pour la seconde...
alors, il est préférable d’écrire :
tab.1 <- read.table( "fic.1.txt", …, stringsAsFactors=FALSE)
tab.2 <- read.table( "fic.2.txt", …, stringsAsFactors=FALSE)
tab.1$VISITE <- sapply( tab.1$VISITE, switch, bs="m00", `6m`="m06", …)
tab.1$VISITE <- factor( tab.1$VISITE, levels=c( "m00", "m06", …), labels=c( "m00",
"m06", …), ordered=TRUE)
tab.2$VISITE <- sapply( tab.2$VISITE, switch, v1="m00", v2="m06", …)
tab.2$VISITE <- factor( tab.2$VISITE, levels=c( "m00", "m06", …), labels=c( "m00",
"m06", …), ordered=TRUE)
tab.total <- merge( tab.1, tab.2, by=c( "RID", "VISITE"...), ...)
Note : 6m n’est pas un nom valide pour R. Le fait de mettre ce nom entre deux apostrophes
inversées (cf. fonction quote) a pour but de prévenir d’une tentative d’interprétation du nom
par R. Ce serait aussi vrai si le nom était un caractère représentant un chiffre ou s’il contenait
certains caractères spéciaux (blanc, moins, plus...).
Écriture des données par write.table
La fonction qui permet d’écrire depuis R des data.frame dans des fichiers texte s’appelle
write.table. Elle a des arguments qui ressemblent à ceux de read.table, notamment dec et sep.
Elle possède deux arguments spéciaux :
row.names
: indique si on doit écrire dans le fichier les noms des lignes.
col.names
: indique si on doit écrire le nom des colonnes (relu dans read.table par
header=TRUE)
Note: si on écrit row.names=TRUE et col.names=TRUE, la première colonne du fichier sera
les noms des lignes, par contre cette colonne prendra le nom de la première colonne du
data.frame... Il y aura donc un décalage dans les noms de colonne si on oublie d’indiquer
row.names=1 dans read.table. Si col.names=NA, la première colonne contiendra les noms de
lignes et n’aura pas de nom. À la lecture, elle prendra le nom X et si une autre colonne à aussi
comme nom X, R lui donnera le nom X.1 (cf. fonctions make.names et make.unique)
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 51
Fonctions : manipuler et contrôler les arguments
Définition de la fonction
Une fonction est un objet de la classe function ayant un constructeur dénommé function. Il a
particularité d'être suivi d'une paire d'accolades équilibrées { } contenant la suite des
instructions composant la fonction. La liste des arguments formels peut contenir à n'importe
quel endroit, l'argument spécial ... permettant de passer un nombre non déterminé par avance
de valeurs. Les arguments formels peuvent aussi avoir des valeurs par défaut:
function( x, n=length( x)) { }
signifie que la fonction possède deux arguments formels
x
obligatoire
n
qui peut avoir n'importe quelle valeur mais qui aura comme valeur length( x) si
on ne lui en donne pas à l'appel.
Une fonction renvoie la dernière valeur évaluée comme le fait la structure if/else if/else. Cela
signifie que :
function( x, n=length( x)) { var <- x }
ne renvoie pas de valeur visible à la console, tandis que le retour de
function( x, n=length( x)) { x }
sera visible à la console. Cependant, pour plus de clarté dans la lecture du code, je conseille
d'utiliser l'instruction spéciale return qui permet d'indiquer explicitement le retour :
function( x, n=length( x)) { ...; return( val) }
Si on désire que la valeur de retour ne soit jamais visible à la console, on peut utiliser la
fonction invisible.
Sauf utilisation particulière comme argument d'une fonction (cf. supra, les exemples
notamment avec sapply), une fonction est utilisée en plusieurs endroits d'un programme. Il
faut donc pouvoir la réutiliser. Pour cela, on affecte à une variable une valeur qui est la
définition formelle de la fonction et par abus de langage, on donnera comme nom à la
fonction le nom de la variable. Par exemple, on parlera de la fonction var définie comme suit :
var <- function( x) { return( sum( (x - mean( x))^2) / (length( x) - 1))}
dont l'appel se fera par var( x).
Étant donné que le nom de la fonction est une variable, celle-ci peut se voir attribuer une
autre valeur. Il convient donc d’être prudent afin de ne pas écraser des définitions de
fonctions.
Note importante
Tout au long du document j’ai utilisé le symbole <- pour définir l’affection d’une
valeur (à droite du symbole) à une variable à gauche du symbole. R admet aussi
l’utilisation du signe = comme beaucoup d’autres langages. Cependant, ce signe
est aussi utilisé dans la définition des fonctions, pour définir les valeurs par défaut
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 52
ou admissibles des arguments, et dans l’appel des fonctions pour passer les
arguments par nom et non plus par position.
Exemple : appel de la fonction cut
cut( x, 3, c( "a", "b", "c"))
est équivalent à
cut( x, labels= c( "a", "b", "c"), breaks=3)
La seconde écriture est plus explicite. De plus, si on veut ordonner le résultat, il
faut mentionner ordered_result=TRUE. Un passage d’argument par position
impose de passer les valeurs des autres arguments avant ordered_result tandis
qu’un passage par nom permet d’écrire :
cut( x, labels= c( "a", "b", "c"), breaks=3, ordered_result=TRUE).
L’utilisation du signe = comme symbole d’affectation peut générer des ambiguïtés
et des comportements inattendus.
Exemple : partons de l’environnement
x <- 2
exists( "v") # retourne FALSE (v n’existe pas dans l’environnement)
fu <- function( u, v) { …}
et
comparons
les
deux
codes
res <- fu( x, v=f1( …))
res <- fu( x, v <- f1( …))
v
res <- fu( x, v <- f1( …))
v
Dans le premier cas, la variable v n’existe toujours pas après l’appel de fu puisque
le code signifie que l’on donne la valeur f1( …) à l’argument v de la fonction fu ;
dans le second elle a été créée lors de l’appel car le code équivaut à :
v <- f1( …)
res <- fu( x, v)
# ou res <- fu( v=v, u=x) ou …
L’argument spécial ...
La définition des fonctions peut contenir un argument spécial ... en n’importe quelle position
pour signifier que la fonction accepte un nombre indéterminé d’arguments non spécifiés a
priori. Cet argument est utilisé dans deux cas.
Le premier cas est quand la fonction appelle elle-même une fonction à qui on veut passer des
paramètres sans préjuger des quels comme pour la fonction aggregate. En effet, les paramètres
à passer dépendront de la fonction d’agrégation :
aggregate( Y ~ Groupe, donnees, quantile, probs=seq( 0, 0, 0.1))
ou
aggregate( donnees$Y, by=list( donnees$Groupe), mean, na.rm=TRUE)
Dans cette utilisation, les paramètres à passer doivent être de la forme :
nomDuParametre=valeur.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 53
La seconde utilisation est quand le nombre de valeur à passer à la fonction n’est pas connu à
l’avance comme par exemple pour les fonctions min et max qui acceptent soit un nombre
quelconque de vecteurs, éventuellement de longueur un, comme arguments. Il faut respecter
certaines règles dans le codage sinon le résultat est imprévisible :
i) l’argument ... est en première position ; à l’appel de la fonction quand on veut
donner des valeurs aux autres arguments, il faut donner explicitement leur nom et la valeur
qui y est associée comme pour l’argument na.rm de min ou max :
max( listeDesValeurs, na.rm=TRUE)
De plus, il est préférable de donner des valeurs par défaut pour ces arguments.
ii) l’argument ... est en dernière position, on passe alors les autres arguments par
position.
iii) l’argument ... est au milieu, cas qu’il faut éviter ; pour éviter les problèmes, il faut
mieux procéder comme dans le cas i).
On récupère dans une liste les arguments réels passés à la fonction qui correspondent à
l’argument ... :
f <- function( ...) {
f.li <- list( ...)
Ils sont dans l’ordre dans lequel ils sont cités. Les éléments de la liste sont numérotés sauf si
les éléments réels sont nommés :
appel de la fonction :
f( 1, z=2, 3)
donne
f.li
[[1]]
[1]
1
$z
[1]
2
[[3]]
[1]
3
Si tous les éléments sont atomiques, on peut passer d’une liste à un vecteur par la fonction
unlist.
Contrôler la valeur des arguments
Récupérer la liste des noms des arguments formels à qui on a attribué une valeur
Certaines procédures et notamment celles d’analyse, comportent une liste d’arguments
formels sans valeur par défaut mais à qui on peut ne pas donner de valeur. Par exemple, la
signature de la procédure lm est :
lm( formula, data, subset, weights,
# + d’autres arguments
Or, la plupart du temps, on ne donne pas de valeur à subset pour signifier qu’on analyse toutes
les données, ni à weights car on donne le même poids à toutes les observations. Cependant, il
n’y aucune valeur par défaut déclarée pour ces deux arguments. On peut cependant savoir
quels sont les arguments qui ont reçu une valeur par l’instruction suivante :
form.arg <- names( match.call())
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 54
qui renvoie un vecteur de chaînes de caractères vides pour les arguments sans nom, le premier
argument étant toujours la chaîne vide "" puisqu’il correspond au constructeur de fonction.
Donc, si la liste des arguments comporte l’argument spécial ..., on aura autant de chaînes
vides qu’il y a d’arguments, à moins que ceux-ci soient nommés :
f( 1, 2)
renvoie
"", "", ""
tandis que
f( 1, z=2)
renvoie
"", "", "z"
Si on veut éviter ce problème avec l’expansion de ..., on peut écrire :
form.arg <- names( match.call( expand.dots=FALSE))
qui retournera la chaîne "..." si l’appel comporte les arguments correspondants.
On peut alors tester les arguments présents en utilisant la fonction match avec comme premier
argument le vecteur des noms des arguments formels et comme second argument, celui des
noms des arguments réels, qui renverra la position dans la liste ou NA si l’argument n’existe
pas. Pour la fonction lm, on a donc :
mf <- match.call( expand.dots = FALSE)
m <- match( c( "formula", "data", "subset", "weights", "na.action", "offset"), names(
mf), 0L)
La fonction missing permet de vérifier si un argument a été passé. Par exemple, la signature
de la fonction sample est :
sample( x, size, replace=FALSE, prob=NULL)
Elle ne définit donc pas de taille par défaut de l’échantillon à générer à partir du vecteur x. De
ce fait, si on veut générer une permutation aléatoire du vecteur x, on devrait écrire :
sample( x, length( x)).
En réalité, on peut écrire :
sample( x)
parce que le corps de la fonction contient le code :
if( missing( size))
size <- length( x)
Une autre solution (plus élégante) aurait été d’écrire :
sample( x, size=length( x), replace=FALSE, prob=NULL).
Compléter une chaîne de caractères
Un certain nombre de procédures reçoivent un argument de type de chaîne de caractères
représentant une option possible. Pour être lisible, la signature comporte généralement les
noms en entiers, par exemple pour les tests de normalité : "shapiro-wilks", "kolmogorovsmirnov", "lillienfors", "kramer-von-mises"... Si cela facilite la lecture de la documentation,
cela devient aussi rapidement fastidieux de taper le nom entier surtout quand on travaille à la
console. R propose donc une procédure pmatch permettant de compléter les chaînes non
ambigües. En reprenant l’exemple ci-dessus, on écrira :
pmatch( typeTest, c( "shapiro-wilks", "kolmogorov-smirnov", "lillienfors", "kramervon-mises"))
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 55
qui renverra NA si typeTest n’est pas dans la liste ou est ambigu et la position dans la liste
dans le cas contraire.
Tester la valeur des arguments de type chaîne de caractères
Dans de nombreuses applications, on passe des arguments sous forme de chaîne de caractères
exprimant une option. Par exemple, la fonction cor permet de calculer la corrélation au sens
de Pearson, de Kendall ou de Spearman. Le type de corrélation est donné par l’argument
method. Si on donne une valeur autre que l’une des trois valeurs citées, la fonction retourne
une erreur. Ce test est réalisé par la fonction match.arg basée sur pmatch et qui comporte de
plus un argument several.ok qui permet les choix multiples s’il vaut TRUE.
Si l’argument formel est déclaré dans la signature de la méthode avec la liste des valeurs
possibles, il suffit d’appeler match.arg de la façon suivant :
method <- match.arg( method)
# cf. fonction cor
L’affectation du résultat de la fonction à la variable méthode permet de récupérer la chaîne
(ou les chaînes) complètes puisque match.arg est basé sur pmatch (cf. supra).
Sinon, il faut écrire
method <- match.arg( method, vecteurListeDesOptions)
Le vecteur des valeurs possibles peut contenir la valeur spéciale NA puisqu’elle signifie que
la valeur existe mais n’est pas (encore) disponible. Par contre, si elle contient la valeur NULL,
celle-ci est ignorée.
Par défaut, match.arg prend la première valeur qu’il rencontre comme valeur par défaut.
Gérer des fonctions comme argument
Certaines procédures comme apply, sapply ou aggregate possèdent des arguments à qui on
donne comme valeur une fonction soit anonyme, soit nommée. Dans la procédure elle-même,
la variable correspondant à l’argument formel prend comme valeur la définition de la
fonction. Cela signifie que de passer des fonctions en tant qu’argument d’une autre fonction
ne pose aucun problème. Donc, si on écrira :
f <- function( x, fun) {
...
res <- fun( x)
...
}
et res vaudra sin( x) si fun=sin.
Généralement, les procédures qui ont des arguments de type fonction, accepte comme valeur
soit le nom de la fonction (cf. supra) soit une chaine de caractères codant le nom. Par
exemple,
apply( mat, 1, FUN=mean)
et
apply( mat, 1, FUN="mean")
sont équivalents.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 56
Une fonction spéciale, match.fun, de R permet de faire coïncider la chaîne de caractères avec
le nom de la fonction. On codera donc :
fun <- match.fun( fun)
res <- fun( x)
Liste d’arguments de longueur indéfinie
Supposons une procédure qui doit à un moment donné ordonner les lignes d’un data.frame
selon certaines colonnes de celui-ci, le nombre et les noms des colonnes servant à
l’ordonnancement variant d’un problème à l’autre, donc de l’appel d’une procédure à un autre
appel. La procédure sera donc définie ainsi :
f <- function( df.donnees, noms.col,
# noms.cols : vecteur des noms des
colonnes servant à ordonner les lignes de df.donnees.
Si on écrit
df.ordonne <- df.donnees[order( df.donnees[noms.cols]),]
on n’obtient pas ce qu’on veut car order linéarise ses arguments. Il faudrait écrire :
df.ordonne <- df.donnees[order( df.donnees[noms.cols[1]], df.donnees[noms.cols[2]],
...),]
ce qui pose le problème de dénombrer les colonnes et de les passer comme arguments
différenciés. La fonction do.call permet de résoudre ce problème. Elle comprend en premier
argument une chaîne de caractères dénotant le nom de la fonction puis une liste dont chacun
des membres est un argument de la fonction. La solution au problème ci-dessus est donc :
df.ordonne <- df.ordonne[do.call( "order", as.list( df.donnees[noms.cols])),]
Créer de nouveaux opérateurs binaires
Il est parfois avantageux de créer un nouvel opérateur binaire au lieu d’une fonction ce qui
permet d’écrire :
a <- x %operateur% y
à la place de a <- f( x, y).
L’exemple d’opérateur binaire est celui de la multiplication de matrices %*%.
Pour définir un nouvel opérateur binaire par exemple opB, on utilise la construction suivant :
"%opB%" <- function( x, y) { corps de la fonction }
Et l’appel se fera alors comme suit :
a <- x %opB% y
On affecte donc la fonction à une chaîne de caractères commençant et finissant par le symbole
%.
Exercices :
A) Expliquez le code suivant
# rot : est la rotation en degré de l’image dans le sens trigonométrique
visuImage <- function( im, type=c( "rgb", "grey"), rot) {
nb.dims <- dim( im)
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 57
rot <- ifelse( missing( rot), 0, rot)
type <- match.arg( type)
stopifnot( nb.dims >= 2, nb.dims <= 3, is.numeric( rot))
if( type == "grey" & nb.dims == 3) {
im <- image.rgb2hsv( im)[,"value"]
imageView( im, rot, visu=TRUE, type="grey")
} else if( nb.dims == 2) {
imageView( im, rot, visu=TRUE, type="grey")
} else {
imageView( im, rot, visu=TRUE, type="rgb")
}
}
B) Soit la fonction rgb2col permettant de transformer les triplets en chaines de caractères
codant les couleurs (cf. exercices précédents), expliquez le code suivant
stopifnot( dim( im) == 3, min( im) >= 0, max( im) <= 1)
z <- rgb2col( im)
c <- sort( unique( z))
z <- match( z, c)
dim( z) <- dim( im)[c( 1, 2)]
C) En vous basant sur l’exercice de codage des couleurs RGB en chaînes de caractères et
l’exercice B, programmer les deux fonctions de l’exercice A, image.rgb2hsv et imageView.
Tenir compte du fait que les valeurs de l’image sont quelconques et non comprises entre 0 et
1. Vous pouvez pour cette partie utiliser des utilitaires de R.
La façon dont R évalue les arguments d’une fonction diffère de beaucoup d’autres langages
comme le C ou le C++. R fait de l’évaluation tardive (lazzy evaluation), c’est-à-dire qu’il
évalue la valeur d’un argument au moment où il en a besoin et non dans la procédure appelant
la fonction juste avant d’appeler la fonction. Ce comportement permet de coder de manière
simple les opérations à effectuer ou les conditions logiques dans des fonctions comme
transform, with, by, subset, ou des arguments comme le subset de nombreuses fonctions
statistiques.
Il faut se souvenir que quand un argument possède une valeur par défaut résultat d’un calcul
sur les autres arguments, le résultat peut être différent de celui qu’on attend quand on raisonne
de manière classique18 au sujet des arguments.
18
Classique est pris ici dans son sens littéral, c’est-à-dire ‘appris en classe’ dans les cours dits de
programmation qui ne sont en général que des cours d’utilisation d’un langage donné, par exemple C ou C++.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 58
Par exemple,
C
Corps de la fonction double exemple( x, y) {
x = x^2;
return( x + y);
}
Appel
u = 2;
exemple( u, sqrt( u));
Retour
5.414
R
exemple <- function( x, y=sqrt( x)) {
x <- x^2
x+y
}
u <- 2
exemple( u)
6
Explication : À l’entrée de la fonction (ligne 2), y vaut sqrt( 2) en C tandis qu’en R, y n’étant
pas encore utilisé, cette variable n’est pas évaluée. Quand elle est utilisée pour le retour, x
vaut 4, donc y (qui vaut sqrt( x)) vaut 2.
Options
Lors de l’exécution de scripts ou l’exécution pas à pas, ou lors de la visualisation, R suit des
règles implicites de comportement qui ont été définies par des options globales qui peuvent
être modifiées soit dans le script, soit dans les fichiers d’initialisation. Les lister toutes serait
trop long. N’hésitez pas à consulter la page d’aide de la procédure options.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 59
Mise au point du script
Quand on programme ligne par ligne, il est facile de mettre au point le code. Quand on
programme des scripts ou des fonctions, c’est en général plus délicat. R propose une série de
fonctions relativement simples pour suivre le déroulement d’un programme. Ce système est
assez basique et de bas niveau et ne propose pas toutes les fonctionnalités des IDE d’autres
langages.
L’utilisation d’éditeurs de type Tinn-R ou de RStudio facilite grandement cette mise au
point. Voir la documentation des éditeurs.
Gestion générale
history permet de lister les commandes précédentes.
ls permet de lister les objets en mémoire. Des arguments permettent de ne lister les objets
dont les noms correspondent à un certain motif (pattern).
rm permet de supprimer des objets de la mémoire. On peut aussi filtrer les objets à supprimer.
gc permet de restructurer et d’optimiser la mémoire après de nombreux ajouts et suppressions
d’objets. Cette fonction est surtout utile quand on dispose de peu de mémoire.
Diagnostic de sortie en erreur
traceback permet de connaître approximativement l’endroit du code ayant généré l’erreur.
Cependant, l’information fournie est souvent très imprécise.
Des fonctions plus sophistiquées permettent d’analyser l’erreur. Pour y avoir accès, il faut
configurer l’option error :
options( error=recover)
# accès à la fonction recover pour parcourir la
fonction en erreur
options( error=quote( dump.frames( dumpto, to.file)))
# pour examiner les
variables et l’environnement en erreur
La seconde option est plus puissante que la première d’autant qu’elle permet de sauver les
informations sur disque et de les examiner en différé.
Pour examiner le résultat de la première option, on utilise la fonction recover, tandis que pour
la seconde option, on utilise la fonction debugger. Se reporter à la documentation.
Diagnostic en ligne
debug( nomDeLaFonction) permet d’entrer dans un mode de mise au point ligne à ligne. On
peut aussi insérer à n’importe quel endroit de la fonction la commande browser qui permettra
d’entrer conditionnellement ou inconditionnellement dans ce mode de mise au point ligne par
ligne.
On peut aussi insérer des points d’arrêt grâce à la fonction trace.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 60
Quand on est dans le mode d’instruction ligne à ligne, on peut examiner les variables, tester
des instructions, etc. n permet de passer à l’instruction suivante, c de continuer soit jusqu’à la
rencontre d’une nouvelle instruction browser ou jusqu’à la fin de la fonction et Q de sortir.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 61
Les distributions
R dispose d’un ensemble de fonctions permettant de calculer les fonctions de densité, les
distributions cumulées ou les quantiles des principales distributions aléatoires, ainsi que de
générer des échantillons des variables aléatoires associées. Il utilise une nomenclature
standardisée pour les différents cas :
dXXX
pour les fonctions de masse (v.a. discrètes) ou de densité (v.a. continues)
pXXX
pour les distributions cumulées
qXXX
pour les quantiles
rXXX
pour les échantillons
avec XXX le nom de la distribution.
Les différentes distributions sont (valeur de XXX) :
beta
cauchy
chisq
exp
f
gamma
lnorm
norm
signrank
t
tukey
unif
weibull
wilcox
binom
geom
hyper
multinom
nbinom
pois
19
Distributions continues
Distribution béta
Distribution de Cauchy
Distribution du khi-deux
Distribution exponentielle
Distribution du F de Fisher-Snédecor
Distribution gamma
Distribution log-normale
Distribution normale (gaussienne)
Distribution du rang signé de Wilcoxon
Distribution de Student
Distribution de l’amplitude studentisé (Tukey)19
Distribution uniforme
Distribution de Weibull
Distribution de la somme des rangs de Wilcoxon
Distributions discrètes
Distribution binomiale incluant la distribution de Bernouilli
Distribution géométrique
Distribution hypergéométrique
Distribution multinomiale
Distribution binomiale négative
Distribution de Poisson
Cette distribution est utilisé pour les tests post-hoc. R propose que les fonctions ptukey et qtukey.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 62
Les graphiques
Les fenêtres graphiques
R permet de visualiser les données sous forme de graphique dans une fenêtre qui est par
défaut recyclée (c’est-à-dire, réinitialisée) à chaque appel sauf si on appelle les fonctions
permettant d’ajouter du texte ou des graphiques à la fenêtre courante.
Cependant, R permet aussi d’utiliser plusieurs fenêtres et de naviguer parmi elles. S’il existe
des fonctions particulières pour chaque système d’exploitation, il existe aussi un ensemble de
fonctions génériques qui sont regroupées dans la page dev.
Il est possible de contrôler finement l’apparence du graphique par un ensemble de paramètres
accessibles par la fonction par. De nombreux paramètres peuvent être localement contrôlés
pour une fonction donnée par des paramètres spécifiques de cette fonction.
Si par défaut, on visualise un seul graphique (qui peut être complexe, cf. les fonctions du
package lattice) par fenêtre, on peut aussi définir des sous fenêtres individualisables
(fonctions split.screen ou layout). Il existe d’autres packages classiques. On peut consulter
avec profit le site http://gallery.r-enthusiasts.com/.
Les graphiques de base
Il existe une méthode de base, la fonction plot, qui permet de représenter une courbe sous
deux formes :
explicite
plot( df$x, df$y, ...)
# df.x peut être absent
formule
plot( y ~ x, data=df, ...)
Cette fonction contrôle un certain nombre de paramètres comme l’apparence de la courbe, son
épaisseur, sa couleur...
Cette méthode de type S3 plot est aussi applicable à d’autres objets, comme les arbres issus
d’une procédure de regroupement, par exemple agnes du package cluster, les résultats d’une
analyse linéaire comme lm... On appelle plot( objet, ...) qui exécute en fait la méthode
plot.<class( objet)>( objet, ... Voir la documentation des différents objets.
On peut selon les besoins ajouter de nouveaux objets au graphique (fonction points), du texte
(text, mtext), des légendes (legend).
Il existe d’autres fonctions. Par exemple, la fonction hist permet de calculer des histogrammes
et, éventuellement, de les visualiser. Les autres principales fonctions sont :
boxplot
boîtes à moustaches
abline
pour ajouter des lignes à un graphique ; à noter abline.lm existe
rug
pour ajouter des sous divisions à un axe.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 63
Le package lattice
Le package lattice permet de tracer des graphes plus complexes notamment des graphiques
par groupe. La principale fonction est xyplot.
Supposons que df est data.frame comportant quatre colonnes poids, age, sexe, regions et qu’il
est ordonné comme suit :
df <- df[order( df$age),]
et qu’on veuille tracer le poids en fonction de l’âge et du sexe. On pourra le faire en une seule
fois par :
xyplot( poids ~ age | sexe, data=df, ...)
et on obtiendra un graphique par sexe. Et si on veut comparer les régions par sexe :
xyplot( poids ~ age | sexe, data=df, groups=regions, ...)
Attention : Quand on utilise les fonctions de lattice dans un script ou une fonction, il faut
écrire :
print( nomDeLaFonction( ...))
pour que l’affichage de fasse.
Les autres fonctions principales de lattice sont20 :
# Scatterplot matrix
splom( ~ data.frame)
bwplot( factor ~ numeric , . .)
# Box and whisker plot
qqnorm( numeric , . .)
# normal probability plots
dotplot( factor ~ numeric , . .)
# 1-dim. Display
stripplot( factor ~ numeric , . .)
# 1-dim. Display
barchart( character ~ numeric , . .)
histogram( ~ numeric , . .)
densityplot( ~ numeric , . .)
# Smoothed version of histogram
qqmath( numeric ~ numeric , . .)
# QQ plot
splom( ~ dataframe, . .)
# Scatterplot matrix
# Parallel coordinate plots
parallel( ~ dataframe, . .)
cloud( numeric ~ numeric * numeric, . .) # 3-D plot
contourplot( numeric ~ numeric * numeric, . .) # Contour plot
levelplot( numeric ~ numeric * numeric, . .) # Variation on a contour plot
20
Cf. "Using R for Data Analysis and Graphics, Introduction, Code and Commentary", J H Maindonald
(http://cran.r-project.org/doc/contrib/usingR.pdf)
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 64
Exercices récapitulatifs
Exercice récapitulatif I
Soit le tableau de données dans le fichier texte "exercice_test.txt".
Lire ce fichier ; les séparateurs de colonnes sont la tabulation et la virgule est le
séparateur décimal.
Tous les sujets sont nés au XXème siècle.
1) Manipulation de la variable 'Age'
Calculer l'âge moyen, l'âge minimum et l'âge maximum par groupe
Transformer la variable quantitative 'Age' en tranches d'âge de 10 ans, centrées sur le
milieu des dizaines, c'est-à-dire [30, 40[, [40, 50[ ... Ce facteur, qu'on nommera
CLASSE.AGE sera ordonné.
Vérifier que les sujets sont uniformément répartis dans les classes d'âge,
indépendamment de leur groupe.
2) Sujets dont les données sont incomplètes
Chercher les sujets pour lesquels on n'a pas les résultats du TEST.1 ; faire de même
pour le TEST.2. Donner les nombre de sujets par groupe sous forme de décompte puis de
tableau de contingence (réponse/non réponse).
Calculer la moyenne et l'écart type du TEST.2 par groupe.
Chercher les sujets pour lesquels on n'a pas les résultats d'au moins un des tests.
3) Imputation simplifiée des valeurs manquantes
On fait l’hypothèse d’un effet âge (au sens des classes d’âge). On remplacera alors les
valeurs manquantes par la moyenne des valeurs pour la classe d’âge du sujet dans son groupe.
ATTENTION : il s’agit d’un exercice, la méthode d’imputation des valeurs manquantes peut
être plus complexe que cela.
4) Travail sur un sous-ensemble, celui des 50 ans et plus.
Le sélectionner à partir de la variable 'Age' puis à partir du facteur CLASSE.AGE.
5) Trouver en quelle année les examens ont été effectués.
6) On suppose que le groupe "G.1" est le groupe des patients ayant développé une certaine
pathologie, que le groupe "G.3" est celui des sujets de référence (groupe de référence) et le
groupe "G.2" celui de sujets susceptible de développer la pathologie. On veut estimer dans
quelle mesure la variable SC peut prédire le risque de développer cette pathologie pour les
patients G.2. La première étape est de vérifier que cette variable permet de discriminer
correctement les patients G.1 des sujets sains G.3. Pour cela on utilisera la régression
logistique sur le sous-ensemble des sujets G.1 et G.3 et plus particulièrement la fonction glm.
On testera les performances de la discrimination par la méthode leave-one-out de validation
croisée. Cette méthode consiste à calculer la paramètres du modèle sur N-1 sujets et
d’appliquer ce modèle (cf. predict) sur le sujet non utilisé dans l’estimation du modèle en
retirant le 1er sujet, puis le second…
i) Programmer la séquence et donner le résultat sous forme d’un tableau de
contingence.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 65
ii) Tracer la courbe ROC correspondante et calculer l’aire sous la courbe et l’intervalle
de confiance (rechercher le package qui permet cela et le charger).
Exercice récapitulatif II : Opérations sur des matrices creuses
Les matrices creuses (sparse matrix) sont des matrices de grandes dimensions contenant une
majorité de valeurs nulles. Pour réduire leur taille en mémoire, on utilise généralement un
codage spécial sous forme d’un tableau à trois colonnes, la première contient les numéros de
lignes, la seconde, les numéros de colonnes et la troisième, la valeur. R propose des outils
pour traiter ces matrices. Il faut donc prendre cet exercice comme des exemples de
manipulation avancée par R et non comme des outils de manipulation des matrices creuses.
On représentera les matrices creuses par un data.frame à trois colonnes nommées
respectivement LIGNE, COLONNE, VALEUR. Les deux dernières lignes du data.frame
auront NA comme valeurs de LIGNE et COLONNE ; les VALEURs associées seront le
nombre de lignes puis de colonnes.
LIGNE
COLONNE
VALEUR
1
6
23,7
3
8
12
NA
NA
nb.lignes
NA
NA
nb.colonnes
...
Écrire les fonctions permettant
1) de récupérer les dimensions,
2) de transformer un data.frame représentant une matrice creuse en une véritable matrice,
3) de transformer une matrice en data.frame représentant une matrice creuse,
4) de calculer la trace de la matrice creuse (somme des éléments diagonaux),
5) de calculer la somme d’au moins deux matrices creuses et retournant une matrice
creuse,
6) de calculer le produit de deux matrices creuses et retournant une matrice creuse.
Éviter d’utiliser des boucles for.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 66
Solution des exercices
Exercice page 14 :
Opérations logiques avec la valeur NA
La valeur NA signifie qu'il existe une valeur inconnue, c'est-à-dire qui peut être TRUE ou
FALSE. Ainsi, pour l'opérateur | (‘ou’), on a :
FALSE | NA ⇔ { FALSE | TRUE = TRUE ; FALSE | FALSE = FALSE} = NA
TRUE | NA ⇔ { TRUE | TRUE = TRUE ; TRUE | FALSE = TRUE } = TRUE
NA | NA ⇔ { FALSE | NA, TRUE | NA} = NA
On peut appliquer les mêmes règles pour tous les opérateurs logiques.
Exercice page 26 :
Exercice : On désire centrer les colonnes d’une matrice (par exemple
mat <- matrix( runif( 200), ncol=10))
sur leur médiane.
1) écrire le code sans utiliser ni apply ni sweep ;
2) écrire le code en utilisant apply ;
3) écrire le code en utilisant sweep.
Faire la même chose avec les lignes et comparer.
#
#
Traitement des colonnes
#
med.col <- apply( mat, 2, median)
#
#
Pas d'utilisation de sweep ni apply
#A:
# => Creation d'une matrice de meme dimension que mat et dont les lignes sont les valeurs
mat.med <- matrix( med.col, nrow=nrow( mat), ncol=ncol( mat), byrow=TRUE)
mat.res.1A <- mat - mat.med
#B:
# => Boucles
mat.res.1B <- mat
for( n.row in 1:nrow( mat)) {
for( n.col in 1:ncol( mat)) {
mat.res.1B[n.row,n.col] <- mat.res.1B[n.row,n.col] - med.col[n.col]
}
}
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 67
#
#
Utilisation d'apply
mat.res.2 <- t( apply( mat, 1, FUN=function( x) x - med.col))
#
apply renvoie un tableau de dim( mat)[1] colonnes d'ou la transposition
#
#
Utilisation de sweep
mat.res.3 <- sweep( mat, 2, med.col)
#
#
Verifications - print( identical( ... si a partir d'un script
identical( mat.res.1A, mat.res.1B)
identical( mat.res.1A, mat.res.2)
identical( mat.res.1A, mat.res.3)
#
#
Traitement des lignes
#
med.row <- apply( mat, 1, median)
mat.res.4 <- mat - med.row
#
# Utilisation d'une boucle a fin de verifications
mat.res.4B <- mat
for( n.col in 1:ncol( mat)) {
for( n.row in 1:nrow( mat)) {
mat.res.4B[n.row,n.col] <- mat.res.4B[n.row,n.col] - med.row[n.row]
}
}
#
Verification
identical( mat.res.4, mat.res.4B)
Explication du code pour les lignes :
med.row est un vecteur de longueur le nombre de lignes de mat. R retire donc les valeurs
contenues dans med.row de la première colonne de mat puisque R parcourt les matrices
colonnes par colonnes. Plus généralement, R parcourt un tableau en parcourant sa première
dimension, puis sa seconde...
Quand R parcourt la seconde colonne de mat, il n'a plus de valeurs de med.row. Il
effectue alors le recyclage (recycling) des valeurs de med.row. De ce fait :
mat.res.4 <- mat - med.row
est équivalent à :
mat.res.4 <- mat - matrix( rep( med.row, ncol( mat)), nrow=nrow( mat))
ou, en utilisant le recyclage : :
mat.res.4 <- mat - matrix( med.row, nrow=nrow( mat), ncol=ncol( mat))
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 68
Donc, dans le cas des lignes, il est inutile d'utiliser apply ou sweep.
Exercice page 31 :
Soit un data.frame
d.f <- data.frame( RID=paste( "sujet", 1:12, sep="."), matrix( rbeta( 240, 3, 5),
nrow=12, ncol=20, dimnames=list( NULL, paste( "test", 1:20, sep="."))))
A) on veut centrer les résultats des tests sujet par sujet, sur leur moyenne ;
- écrire le code sans utiliser sweep ni boucles ;
- écrire le code en utilisant sweep et vérifier qu’on obtient les mêmes résultats dans les
deux cas toujours sans boucles for.
B) faire la même chose mais colonne par colonne.
Ce qui a été dit dans l’exercice précédent pour les matrices est applicable aux data.frame.
Exercice page 36 :
# On construit un tableau de données
d.f <- data.frame( X=runif( 20, min=0, max=5))
d.f <- transform( d.f, Y=X + rnorm( 20, sd=0.2))
d.f <- transform( d.f, MEAN.Y=mean( Y))
# On compare les résultats des régressions suivantes
lm.std <- lm( Y ~ X, d.f)
lm.centre <- lm( Y - MEAN.Y ~ X, d.f)
lm.offset <- lm( Y ~ X + offset( MEAN.Y), d.f)
lm.zero <- lm(Y ~ 0 + X + MEAN.Y, d.f)
Utiliser la fonction summary pour récupérer les résultats et interprétez-les.
lm.std : on estime l’ordonnée à l’origine et la pente.
lm.centre : on estime l’ordonnée à l’origine et la pente des données dépendantes centrées. On
peut d’ailleurs constater que l’ordonnée à l’origine est égale à celle de lm.std moins la
moyenne de Y.
lm.offset : donne les mêmes résultats que lm.centre.
lm.zero : on fait une régression multiple avec deux régresseurs, X et MEAN.X en forçant
l’ordonnée à l’origine à zéro. On obtient donc les pentes pour X et pour MEAN.X
Exercice pages 43 et 44 :
A) Soit un data.frame comportant deux colonnes, l’une la date de naissance, l’autre la date
d’examen sous forme de chaîne de caractères jj/mm/aa où aa est l’année sur deux chiffres.
Calculer l’âge en année de chaque sujet lors de l’examen (cf. as.Date). Comment gérer le
changement de siècle et le fait que certains sujets sont nés avant 1970 ? Comment gérer le fait
que certains sujets n’ont pas encore subi d’examen et donc que la date est codée par NA ?
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 69
B) Soit un tableau (array) à 3 dimensions mesurant plusieurs fois dans le temps divers
grandeurs (lignes), dans différentes conditions (colonnes), la répétition des mesures étant la
3ème dimension (temps) :
mes.array <- array( 0, dim=c( nb.variables, nb.conditions, nb.temps) …
vect.temps <- # Temps des mesures
i) On veut vérifier que les mesures sont stables dans le temps. On va donc faire une
régression individuelle sur la variable temps de chacune des variables dans chacune des
conditions et construire la matrice variablesXconditions de p.value de la pente de régression.
Utiliser la fonction apply pour cela.
ii) On suppose que la stabilité est vérifiée, on veut alors vérifier l’effet des conditions
en considérant les mesures dans le temps comme des occurrences indépendantes de la même
mesure. On va donc construire un vecteur des p.value, un élément par variable. Utiliser aussi
apply.
N.B. : la fonction à utiliser est lm et on retourne la p.value associée à la table anova obtenue
par la fonction anova.
C) Soit une matrice de 3 colonnes et 256 lignes dont les valeurs sont comprises entre 0 et 1
correspondant à une LUT (look-up table), la première colonne correspond donc à l’intensité
du rouge, la seconde, du vert et la troisième du bleu. R code les couleur sous forme de chaines
de caractères en hexadécimal commençant par le caractère dièse ("#"), les deux caractères
suivant étant le niveau de rouge, codé de 0 à FF (équivalent du 255 décimal), ensuite on a le
niveau du vert puis celui du bleu. Écrire les lignes de code permettant de transformer la
matrice en couleurs pour R (ne pas utiliser de boucles for).
A) Pour la première partie de la question, voir l’exercice récapitulatif. Il suffit ensuite de
transformer les deux colonnes de chaînes de caractères en date par la fonction as.Date et de
faire la différence par la fonction difftime. On obtiendra la différence en jours. Pour avoir
l’âge approximatif, on divisera cette différence par 365,25 durée approximative des années.
Gestion des NA :
date.exam.NA <- which( is.na( date.exam), arr.ind=TRUE)
date.exam[!date.exam.NA] … # cf. supra
B)
i) Stabilité des mesures dans le temps.
stabilite.mes <- apply( mes.array, c( 1, 2), FUN=function( x.t) {
lm.mes <- lm( x.t ~ vect.temps)
anova( lm.mes)["vect.temps","Pr(>F)"]
})
Commentaires :
1) anova renvoie un objet héritant de data.frame dont les colonnes sont nommées :
Df :
nombre de degrés de liberté
Sum Sq :
somme des carrés
Mean Sq :
moyenne des carrés
F:
valeur du F
Pr(>F) :
p.value associée
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 70
et les ont pour nom les variables (sous la forme X), les interactions (par exemple, X:Y pour
l’interaction entre X et Y) et la dernière ligne "Residuals", les résidus.
2) Quand on consulte la page d’aide de lm, l’appel est :
lm( formula, data…
or dans la solution ci-dessus, aucun valeur n’est donnée à l’argument data. Cela provient du
fonctionnement de R pour l’évaluation de la formule. En effet, R évalue la formule dans
l’environnement courant de l’utilisation de la formule, c’est-à-dire à l’intérieur de la fonction
lm pour notre cas.
Si l’argument data n’est pas spécifié, R va tenter de trouver les objets impliqués dans
la formule dans la fonction appelant lm puis, s’il ne les trouve pas, la fonction d’un niveau
supérieur. Donc dans notre cas, il trouve l’objet x.t dans la fonction appelant lm tandis que
vect.temps dans celle qui utilise apply. Les objets sont donc parfaitement définis pour lm.
Si data a une valeur, lm va commencer par essayer d’évaluer les objets dans
l’environnement de cette valeur. Exemple :
vect.temps <- c( …)
d.f <- data.frame( Y=…)
# Une seule colonne Y
lm.mes <- lm( Y ~ vect.temps, data=d.f)
L’objet Y existe dans l’environnement de d.f, c’est le vecteur colonne associé à la colonne Y
du data.frame, tandis que vect.temps n’existe pas. Par contre vect.temps existe dans
l’environnement d’appel. L’analyse peut donc être faite.
Il est à noter que R fonctionne exactement de la même manière pour l’argument subset
qu’on retrouve dans de nombreuses fonctions, ainsi que l’évaluation des conditions dans les
fonctions which et subset ou d’expression dans transform pour ne citer que ces fonctions.
3) L’argument MARGIN de apply est c( 1, 2), ce qui signifie qu’on passe à FUN le vecteur
mes.array[i,j,]. Cf. documentation de apply.
ii) effet des conditions de mesures
# On crée un facteur dont le nombre de niveaux est le nombre de conditions de mesures
cdn.mes <- factor( … # avec nlevels( cdn.mes == nb.conditions)
cdn.mes <- rep( cd.mes, nb.temps)
# Utilisation de apply
effet.cdn <- apply( mes.array, 1, FUN=function( mes) {
mes.vect <- as.vector( mes)
lm.mes <- lm( mes.vect ~ cdn.mes)
…
Commentaire :
apply passe à la fonction une matrice de nb.conditions lignes et nb.temps colonnes.
as.vector transforme cette matrice en un vecteur colonne par colonne. Du point de vue des
conditions, on a donc les premières mesures des nb.conditions, puis les secondes… C’est la
raison pour laquelle on crée un vecteur (facteur) de conditions en répétant nb.temps fois les
conditions.
C)
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 71
i) L’un des codes possibles est le suivant (pour en faciliter la lecture, j’ai numéroté les
lignes)
1
hexadecimal.code <- c( 0:9, "A", "B", "C", "D", "E", "F")
2
hexadecimal.table <- paste( rep( hexadecimal.code, each=16), rep( hexadecimal.code,
16), sep="")
3
im <- floor( im * 256)
4
im.col <- hexadecimal.table[im + 1]
5
dim( im.col) <- dim( im)
6
im.col <- apply( im.col, c( 1, 2), FUN=paste, collapse="")
7
im.col <- paste( "#", im.col, sep="")
8
dim( im.col) <- dim( im)[1:2]
1 et 2 : construction du vecteur des 256 codes hexadécimaux des valeurs 0 à 255.
3
: les plans couleurs sont normalisés entre 0 et 255
4
: transformation de l’image RGB en 1 vecteur de codes hexadécimaux ; le + 1 est
nécessaire puisqu’en R, les indices commencent à 1 donc le code du zéro est le premier
élément de hexadecimal.table.
5
: transformation du vecteur en un tableau de la même dimension que l’image initiale.
En général, il faut éviter ce type de codage explicite mais ici, il se justifie car on sait que
im.col est la linéarisation de im. Cependant, on aurait pu écrire :
im.col <- array( im.col, dim=dim( im))
mais cela aurait créé un nouvel objet.
6
: on crée un matrice de dimension dim( im)[c( 1, 2)] concaténant les codes des trois
couleurs.
7
: on ajoute le symbole "#", symbole préfixant les couleurs.
8
: l’utilisation de paste a de nouveau linéarisé la matrice, il faut donc recréer l’image.
ii) On peut aussi tirer partie de la linéarisation sachant que R parcourt d’abord la 1ère
dimension, puis la seconde… De ce fait, les éléments de im.col correspondant au plan rouge
vont des indices 1 à prod( dim( im)[1:2]), le plan vert va de prod( dim( im)[1:2]) + 1 à 2 *
prod( dim( im)[1:2]) et le plan bleu de 2 * prod( dim( im)[1:2]) + 1 à prod( dim( im)).
Le code remplaçant les lignes 5 à 7 ci-dessus sera alors :
pr.dim <- dim( im)[1:2])
im.col <- paste( "#",
col.im[seq( from=1, length.out=pr.dim],
col.im[seq( from=pr.dim + 1, length.out=pr.dim],
col.im[seq( from=2 * pr.dim) + 1, length.out=pr.dim], sep="").
Commentaire:
L’utilisation de l’argument length.out sans argument to ni by provoque la création d’une
séquence d’entiers partant de from et de longueur length.out. Cet appel est plus simple que si
on avait explicitement donné les valeurs de from et to.
seq peut aussi générer des séquences de nombres complexes. Cependant, certaines
combinaisons d’arguments ne sont pas valides.
Note : Il s’agit d’un exercice. R fournit les utilitaires nécessaires notamment dans le package
grDevices.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 72
Exercices pages 53 et 54 :
A) Expliquez le code suivant
# rot : est la rotation en degré de l’image dans le sens trigonométrique
1
visuImage <- function( im, type=c( "rgb", "grey"), rot) {
2
nb.dims <- dim( im)
3
rot <- ifelse( missing( rot), 0, rot)
4
type <- match.arg( type)
5
stopifnot( nb.dims >= 2, nb.dims <= 3, is.numeric( rot))
6
if( type == "grey" & nb.dims == 3) {
7
im <- image.rgb2hsv( im)[,"value"]
8
imageView( im, rot, visu=TRUE, type="grey")
9
} else if( nb.dims == 2) {
10
imageView( im, rot, visu=TRUE, type="grey")
11
} else {
12
imageView( im, rot, visu=TRUE, type="rgb")
13
}
14
}
B) Soit la fonction rgb2col permettant de transformer les triplets en chaines de caractères
codant les couleurs (cf. exercices précédents), expliquez le code suivant
1
stopifnot( dim( im) == 3, min( im) >= 0, max( im) <= 1)
2
z <- rgb2col( im)
3
c <- sort( unique( z))
4
z <- match( z, c)
5
dim( z) <- dim( im)[c( 1, 2)]
C) En vous basant sur l’exercice de codage des couleurs RGB en chaînes de caractères et
l’exercice B, programmer les deux fonctions de l’exercice A, image.rgb2hsv et imageView.
Tenir compte du fait que les valeurs de l’image sont quelconques et non comprises entre 0 et
1. Vous pouvez pour cette partie utiliser des utilitaires de R.
A) Pour faciliter la lecture, j’ai numéroté les lignes.
1
: appel de la fonction. L’argument type peut prendre deux valeurs, les autres sont
libres.
2
: on récupère les dimensions de l’argument im qui doit donc être un tableau de valeurs.
3
: si l’argument rot n’est pas défini (missing), on lui donne la valeur 0 sinon on
conserve sa valeur
4
: on teste la valeur de l’argument type (match.arg) qui dans notre cas ne peut prendre
qu’une des deux valeurs prévues. Si type n’est pas défini dans l’appel, il prend la première
valeur (grey) par défaut. Si on voulait qu’il puisse prendre plusieurs valeurs, il aurait fallu
donner la valeur TRUE à l’argument several.ok de match.arg. Sans ce test, type peut prendre
n’importe quelle valeur et vaudra c( "rgb", "grey") si on ne donne pas de valeurs à type lors
l’appel.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 73
5
: la fonction sort en erreur si rot n’est pas numérique ou si le nombre de dimension du
tableau n’est pas 2 ou 3.
6 à 8 : si le tableau est 3D et que le type est grey, on transforme l’image de type RGB et
image de type HSV et on ne garde que la composante brillance (en anglais value) qui donne
les niveaux de gris et on visualise l’image.
9 et 10 : si le nombre de dimension est 2, on a une image en niveaux de gris et on force
l’affichage selon ce mode.
12
: image 3D donc RGB. On force l’affichage dans ce mode.
B) On suppose que la fonction rgb2col est celle codée dans l’exercice précédent.
1
: on teste qu’il s’agit d’une image 3D codée entre 0 et 1.
2
: on la transforme en image 2D des couleurs
3
: on cherche les différentes couleurs utilisées de manière non ambigüe (unique) et on
les trie ; le tri est celui de l’ordre alphanumérique mais comme les chiffres sont codés avant
les lettres, c’est aussi l’ordre de valeurs. On construit ainsi la table des couleurs à utiliser
(LUT ou look-up table).
4
: on cherche la position de chaque pixel dans la LUT et on crée ainsi une table des
index.
5
: la table des index est un vecteur résultant de la linéarisation de l’image, on rétablit
alors l’image.
C)
i) Trouver les packages et les fonctions qui pourront servir.
Localement : ??mot.cle
ou ??"mot cle" si le mot clé contient des blancs ou des
caractères pouvant être interprétés autrement (par exemple, des opérateurs ou des caractères
spéciaux).
Sur le site de R : RSiteSearch( "mot cle")
ii) Code possible de image.rgb2hsv :
image.rgb2hsv <- function( r, g, b, i.min, i.max) {
require( grDevices)
stopifnot(
length( dim( r)) %in% c( 2, 3),
switch( length( dim( r)), NA, !missing( g) & !missing( b), missing( g) &
missing( b)))
if( length( dim( r) == 2)) {
stopifnot( length( dim( g)) == 2, identical( dim( r), dim( g)),
length( dim( b)) == 2, identical( dim( r), dim( b)))
im<- array( 0, c( dim( r), 3))
im[,,1] <- r; im[,,2] <- g; im[,,3] <- b
} else {
im <- r
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 74
}
if( missing( i.min)) i.min <- min( im)
if( missing( i.max)) i.max <- max( im)
i.min <- rep( i.min, 3)[1:3]; i.max <- rep( i.max, 3)[1:3]
# Valeurs normalisées : floor( 256 * (x - min) / (max -min))
im <- sweep( im, c( 1, 2), i.min)
im <- floor( 256 * sweep( im, c( 1, 2), i.max - i.min, "/")
res <- rgb2hsv( im)
names( res) <- c( "hue", "saturation", "value")
res
}
Pour imageView, on peut le coder comme suit :
- Attention, il ne s’agit que d’une proposition
imageView <- function( im, rot, visu=TRUE, type=c( "rgb", "grey"), fill=0) {
# Ce code est un exemple. Il n’est pas optimum et ne doit pas être utilisé tel quel
#
dans les applications réelles
.simple.rot <- function( image, rot, fill=0) {
mat.rot <- matrix( c( cos( rot), sin( rot), -sin( rot), cos( rot)), ncol=2, nrow=2)
dim.im <- dim( image)
x.centre <- (dim.im[1] - 1) / 2
y.centre <- (dim.im[2] - 1) / 2
im.rot <- array( fill, dim=dim.im)
if( length( dim.im) == 1) image <- array( image, c( dim.im, 1))
for( y in 1:dim.im[2]) {
for( x in 1:dim.im[1]) {
new.coord <- round( mat.rot %*% c( x - 1 -x.centre, y - 1 y.centre)) + 1
if( any( new.coord < 1) || any( new.coord > dim.im[1:2])) next
for( z in 1:dim.im[3]) {
im.rot[new.coor[1], new.coord[2], z] <- image[x, y, z]
}
}
}
dim( im.rot) <- dim.im
}
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 75
type <- match.arg( type)
if( !missing( rot) && rot != 0) im <- .simple.rot( im, rot, fill)
im <- im / max( im)
if( type == "rgb") {
if( length( dim( im)) == 2) {
im <- rep( im, 3)
dim( im) <- c( dim( im), 3)
}
} else {
if( dim( im) == 3) {
im <- rgb2hsv( im)[,,"v"]
}
}
im <- rgb2col( im)
if( visu) {
lut.c <- sort( unique( im))
z.im <- match( im, lut.c)
dim( z.im) <- dim( im)[c( 1, 2)]
image( z.im, col=lut.c)
}
invisible( im)
}
Exercice récapitulatif I
Soit le tableau de données dans le fichier texte "exercice_test.txt".
Lire ce fichier ; les séparateurs de colonnes sont la tabulation et la virgule est le
séparateur décimal.
Tous les sujets sont nés au XXème siècle.
1) Manipulation de la variable 'Age'
Calculer l'âge moyen, l'âge minimum et l'âge maximum par groupe
Transformer la variable quantitative 'Age' en tranches d'âge de 10 ans, centrées sur le
milieu des dizaines, c'est-à-dire [30, 40[, [40, 50[ ... Ce facteur, qu'on nommera
CLASSE.AGE sera ordonné.
Vérifier que les sujets sont uniformément répartis dans les classes d'âge,
indépendamment de leur groupe.
2) Sujets dont les données sont incomplètes
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 76
Chercher les sujets pour lesquels on n'a pas les résultats du TEST.1 ; faire de même
pour le TEST.2. Donner les nombre de sujets par groupe sous forme de décompte puis de
tableau de contingence (réponse/non réponse).
Calculer la moyenne et l'écart type du TEST.2 par groupe.
Chercher les sujets pour lesquels on n'a pas les résultats d'au moins un des tests.
3) Imputation simplifiée des valeurs manquantes
On fait l’hypothèse d’un effet âge (au sens des classes d’âge). On remplacera alors les
valeurs manquantes par la moyenne des valeurs pour la classes d’âge du sujet dans son
groupe.
ATTENTION : il s’agit d’un exercice, la méthode d’imputation des valeurs manquantes peut
être plus complexe que cela.
4) Travail sur un sous-ensemble, celui des 50 ans et plus.
Le sélectionner à partir de la variable 'Age' puis à partir du facteur CLASSE.AGE.
5) Trouver en quelle année les examens ont été effectués.
6) On suppose que le groupe "G.1" est le groupe des patients ayant développé une certaine
pathologie, que le groupe "G.3" est celui des sujets de référence (groupe de référence) et le
groupe "G.2" celui de sujets susceptible de développer la pathologie. On veut estimer dans
quelle mesure la variable SC peut prédire le risque de développer cette pathologie pour les
patients G.2. La première étape est de vérifier que cette variable permet de discriminer
correctement les patients G.1 des sujets sains G.3. Pour cela on utilisera la régression
logistique sur le sous-ensemble des sujets G.1 et G.3 et plus particulièrement la fonction glm.
On testera les performances de la discrimination par la méthode leave-one-out de validation
croisée. Cette méthode consiste à calculer la paramètres du modèle sur N-1 sujets et
d’appliquer ce modèle (cf. predict) sur le sujet non utilisé dans l’estimation du modèle en
retirant le 1er sujet, puis le second…
i) Programmer la séquence et donner le résultat sous forme d’un tableau de
contingence.
ii) Tracer la courbe ROC correspondante et calculer l’aire sous la courbe et l’intervalle
de confiance (rechercher le package qui permet cela et le charger).
Lecture du tableau :
tab <- read.table( file="exercice_test.txt", sep="\t", dec=",", header=TRUE, quote='"',
comment.char="")
En effet, il est nécessaire de modifier l’argument quote puisque les commentaires
contiennent des apostrophes, comme il faut aussi modifier l’argument comment.char
puisqu’un des commentaires contient un dièse.
Il faut noter qu’on aurait pu écrire quote="\"", le backslash étant un caractère
d’échappement.
# On peut s’assurer que les colonnes sont du type correct
sapply( tab, class)
# On peut formater correctement chaque colonne :
tab$Groupe <- as.factor( tab$Groupe)
tab$COMMENTAIRE <- as.character( tab$COMMENTAIRE)
tab$DDN <- as.character( tab$DDN) # cf. infra pour convertir en Date
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 77
N.B. : Personnellement, je conseille de toujours mettre dans read.table, l’argument
stringsAsFactor à FALSE pour éviter la conversion des chaînes de caractères en facteur, à
moins qu’on soit sûr que toutes les chaînes de caractères du fichier correspondent à des
niveaux de facteur.
Dans le cas de notre, exemple, ceci est faux pour les colonnes COMMENTAIRE et
DDN.
1) Manipulation de la variable ‘Age’
Calcul de la moyenne, du minimum et du maximum par groupe
. Solution 1 :
age.moyen <- aggregate( tab$Age, by=list( tab$Groupe), mean)
# ecriture equivalent utilisant une formule
age.moyen <- aggregate( Age ~ Groupe, tab, mean)
# On fait alors de même pour l’$age minimum avec la fonction min et l’âge maximum avec
max
. Solution 2
# calcul en une seule fois
age.desc <- aggregate( tab$Age, by=list( tab$Groupe), FUN=function( x) c(
MOYEN=mean( x), R=range( x)))
Notons alors que le résultat nous donne :
> str( age.desc)
'data.frame': 3 obs. of 2 variables:
$ Group.1: chr "G.1" "G.2" "G.3"
$x
: num [1:3, 1:3] 49.5 48.6 51 37 20 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : NULL
.. ..$ : chr "MOYEN" "R1" "R2"
C’est-à-dire qu’on obtient in data.frame ayant deux colonnes, l’une Group.1 (et plus
généralement n + 1, dont les premières sont nommées de Group.1 à Group.n quand il y a n
facteurs de groupement) et la dernière nommée x contenant le résultat.
Il est donc préférable de reformater la sorte et de renommer les colonnes des facteurs
de groupement :
age.desc <- cbind( age.desc[,"Group.1"], as.data.frame( age.desc$x))
names( age.desc)[1] <- "Groupe"
Transformation en facteurs de classes d’âge
dizaine.min <- signif( min( tab$Age), 1)
dizaine.max <- 10 * ceiling( max( tab$Age) / 10)
cut.points <- seq( dizaine.min, dizaine.max, by=10)
labels.age <- paste( "CA", (tail( cut.points, -1) + head( cut.points, -1)) / 2, sep=".")
tab <- transform( tab, CLASSE.AGE=cut( Age, breaks=cut.points, labels=labels.age,
include.lowest=TRUE, right=FALSE, ordered=TRUE))
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 78
Répartition uniforme par classe d’âge
nb.classes.age <- nlevels( tab$CLASSE.AGE)
prop.test( as.vector( table( tab$CLASSE.AGE)), rep( nrow( tab), nb.classes.age),
correct=TRUE)
2) Données manquantes
. Quels sujets n’ont pas de résultat pour TEST.1
#
#
Méthode 1
#
# Indicateur logique
na.test.1 <- which( is.na( tab$TEST.1))
# Numéro ligne
na.test.1 <- which( is.na( tab$TEST.1), arr.ind=TRUE)
# Rid coorespondants
rid.na.1 <- tab$RID[na.test.1]
#
#
Méthode 2
#
rid.na.1 <- tab$RID[which( is.na( tab$TEST.1))]
On fait de même pour TEST.2.
À noter qu’on ne peut pas utiliser le test logique tab$TEST.1 == NA mais qu’on emploie une
fonction spéciale is.na car l’égalité n’a pas de sens avec NA puisque NA signifie n’importe
quelle valeur.
De même, il est impossible d’écrire x == NULL mais qu’il faut utiliser une fonction
spéciale is.null ; il existe aussi des fonctions spéciale pour tester si un nombre est fini
(is.finite), infini (is.infinite) ou si une variable n’est pas un nombre (is.nan).
. Décompte du nombre de valeurs manquantes par groupement
nb.na.1 <- aggregate( tab$TEST.1, by=list( tab$Groupe), FUN=function( x) sum(
is.na( x)))
Note : on peut utiliser la formulation d’aggregate avec une formule mais dans ce cas, il faut
lui indiquer que faire avec les valeurs NA qui sont supprimées par défaut. Il faut donc écrire :
nb.na.1 <- aggregate( TEST.1 ~ Groupe, tab, FUN=function( x) sum( is.na( x)),
na.action=na.pass)
# décompte par table de contingence
table( tab$Groupe, is.na( tab$TEST.1))
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 79
. Moyenne et écart type par groupe pour le TEST.2
Il faut tenir compte des éventuelles valeurs manquantes.
# Utilisation d’aggregate avec formule
des.test.2 <- aggregate( TEST.2 ~ Groupe, tab, FUN=function( x) c(
MOYENNE=mean( x), ET=sd( x)))
# Utilisation d’aggregate par liste de facteurs de groupement
des.test.2 <- aggregate( tab$TEST.2, by=list( tab$Groupe), FUN=function( x) c(
MOYENNE=mean( x, na.rm=TRUE), ET=sd( x, na.rm=TRUE)))
Dans le cas de l’utilisation de la formule, on utilise le comportement par défaut d’aggregate
face aux valeurs manquantes (na.omit). Sur la structure du résultat, cf. ce qui a été dit
précédemment, à la différence que les colonnes des facteurs de groupement ont le nom du
facteur et non un nom par défaut.
Dans l’agrégation par liste, aggregate passe à la fonction toutes les valeurs dont les
NA, il faut donc dire aux fonctions de traitement (mean et sd) quoi faire de ces valeurs. En
effet, logiquement la moyenne, la variance, la médiane... d’un vecteur contenant des NA n’est
pas définie et vaut NA.
#
. Sujets dont au moyen un des tests n’a pas de valeur
Conditon logique
sujets.no.val <- is.na( tab$TEST.1) | is.na( tab$TEST.2)
#
sujets.val <- complete.cases( tab$TEST.1, tab$TEST.2)
#
# Vérification
identical( sujets.no.val, !sujets.val)
Note : R propose une fonction complete.cases qui permet de rechercher les lignes d’un
data.frame qui n’ont aucune valeur manquante.
3) Imputation des valeurs manquantes
#
#
#
Calcul des moyennes
mean.TEST.1 <- aggregate( TEST.1 ~ Groupe + CLASSE.AGE, tab, mean)
# Calcul de la table réelle
#
Création d’un data.frame contenant toutes les combinaisons
tab.mean <- expand.grid( Groupe=levels( tab$Groupe), CLASSE.AGE=levels(
tab$CLASSE.AGE))
# On ajoute les lignes (combinaisons de facteurs) manquantes
#
On fusionne les deux data.frame en utilisant les groupes et les classes d’âge
comme pivot
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 80
#
On indique de conserver les lignes de tab.mean (suffixe .y dans les arguments
de merge) si elles n’existent pas dans le premier data.frame
mean.TEST.1 <- merge( mean.TEST.1, tab.mean, by=c( "Groupe", "CLASSE.AGE"),
all.y=TRUE)
# Construction de la matrice
mean.TEST.1 <- matrix( mean.TEST.1$TEST.1, ncol=nlevels( tab$Groupe),
dimnames=list( levels( tab$CLASSE.AGE), levels( tab$Groupe)))
#
#
Imputation des valeurs
#
# Indices des sujets dont la valeur de TEST.1 est NA
id.NA <- which( is.na( tab$TEST.1), arr.ind=TRUE)
# Imputation des valeurs
tab$TEST.1[id.NA] <- sapply( id.NA, FUN=function( n.sujet) with( tab,
mean.TEST.1[CLASSE.AGE[n.sujet],Groupe[n.sujet]]))
Identique pour TEST.2
4) Sélection des sujets de 50 ans et plus
tab.suj.p50 <- subset( tab, CLASSE.AGE >= "C.55")
Le facteur CLASSE.AGE étant ordonnée, les opérateurs de comparaison d’ordre (<, >, <= et
>=) ont un sens. Si le facteur n’était pas ordonné, seuls les opérateurs d’égalité (==) ou de
différence (!=) des valeurs pourraient être utilisés.
5) Année des examens
La solution qui semble la plus simple pour traduire les chaînes de caractères exprimant la date
de naissance en objet Date serait d’utiliser la fonction de conversion as.Date et donnant
comme format %d/%m/%y. Malheureusement, celle-ci se base sur les normes POSIX 2004 et
2008 qui stipulent que pour les années comprises entre 00 et 68, l’année est préfixée par 20 et
au-delà, elle est préfixée par 19. Donc, 59 donne l’année 2059 tandis que 88 donne l’année
1988.
Il faut donc commencer par corriger les dates de naissance.
# Ajout du prefixe 19 à l’année
tab$DDN <- sapply( strsplit( tab$DDN, "/"), FUN=function( ddn) {
ddn[3] <- paste( "19", ddn[3] , sep="")
paste( ddn, collapse="/")
})
# Transformation en objet de type Date
tab$DDN <- as.Date( tab$DDN)
# Nombre de jours approximatifs entre naissance et examen
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 81
nb.jours <- tab$Age * 365 + floor( tab$Age / 4)
# Ajout des jours à la date de naissance
#
on ajoute le nombre de jours à la date de naissance
date.exam <- tab$DDN + as.difftime( nb.jours, units="days")
# Récupération de l’année et transformation en entier
tab$DATE.EXAM <- as.integer( as.character( date.exam, "%Y"))
6) i) Prédiction
# Selection du sous ensemble à analyse
tab.11 <- droplevels( subset( tab, Groupe %in% c( "G.1", "G.3")))
# On veut s’assurer que le niveau de référence sera G.3
tab.11$Groupe <- relevel( tab.11$Groupe, ref="G.3")
# leave-one-out + prediction
perf.SC <- sapply( 1:nrow( tab.11), FUN=function( n.row) {
res.glm <- glm( Groupe ~ SC, family=binomial, data=tab.11[-n.row,])
c( LOGIT=predict( res.glm, newdata=tab.11[n.row,], type="link"),
RESPONSE=predict(res.glm, newdata=tab.11[n.row,], type="r"))
})
perf.SC <- data.frame( tab.11[,c( "RID", "Groupe")], as.data.frame( t( perf.SC)))
# La colonne LOGIT.1 contient la valeur de la fonction de lien (logit)
#
Elle est positive si le sujet est estimé appartenir à G.1 (groupe d’intérêt) et négative
autrement
# La colonne RESPONSE.1 donne la probabilité estimée d’appartenir au groupe G.1
perf.SC <- transform( perf.SC, Groupe.Estime=ifelse( perf.SC$LOGIT.1 > 0, "G.1",
"G.3"))
# On dit de Groupe.estime est un facteur dont les niveaux sont dans le même sens de Groupe
perf.SC$Groupe.Estime <- factor(perf.SC$Groupe.Estime, levels=c( "G.3", "G.1"),
labels=c("G.3", "G.1"))
#
# Construction de la table de contingence
#
with( perf.SC, table( Groupe, Groupe.Estime))
ii) courbe ROC
require( pROC)
# 3 manières équivalentes de construire la courbe
roc.1 <- roc( Groupe ~ LOGIT.1, perf.SC)
roc.2 <- roc( perf.SC$Groupe, perf.SC$LOGIT.1)
roc.3 <- roc( controls=subset( perf.SC, Groupe == "G.3")$LOGIT.1, cases= subset(
perf.SC, Groupe == "G.1")$LOGIT.1)
#
# Calcul de l’aire sous la courbe
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 82
#
Puisque l’argument auc de roc est par défaut TRUE
auc.perf <- as.numeric( roc.1$auc)
#
# Intervalle de confiance
#
# On peut écrire ci=TRUE dans l’appel de roc et éventuellement donner la méthode en
argument supplémentaire ou écrire
ci.perf <- ci.auc( roc.1)
# donner éventuellement une méthode
Exercice récapitulatif II : Opérations sur des matrices creuses
Les matrices creuses (sparse matrix) sont des matrices de grandes dimensions contenant une
majorité de valeurs nulles. Pour réduire leur taille en mémoire, on utilise généralement un
codage spécial sous forme d’un tableau à trois colonnes, la première contient les numéros de
lignes, la seconde, les numéros de colonnes et la troisième, la valeur. R propose des outils
pour traiter ces matrices. Il faut donc prendre cet exercice comme des exemples de
manipulation avancée par R et non comme des outils de manipulation des matrices creuses.
On représentera les matrices creuses par un data.frame à trois colonnes nommées
respectivement LIGNE, COLONNE, VALEUR. Les deux dernières lignes du data.frame
auront NA comme valeurs de LIGNE et COLONNE ; les VALEURs associées seront le
nombre de lignes puis de colonnes.
LIGNE
COLONNE
VALEUR
1
6
23,7
3
8
12
NA
NA
nb.lignes
NA
NA
nb.colonnes
...
Écrire les fonctions permettant
1) de récupérer les dimensions,
2) de transformer un data.frame représentant une matrice creuse en une véritable matrice,
3) de transformer une matrice en data.frame représentant une matrice creuse,
4) de calculer la trace de la matrice creuse (somme des éléments diagonaux),
5) de calculer la somme d’au moins deux matrices creuses et retournant une matrice
creuse,
6) de calculer le produit de deux matrices creuses et retournant une matrice creuse.
Éviter d’utiliser des boucles for.
#
# Récupération des dimensions
#
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 83
dim.mc <- function( mc) {
tail( mc, 2)$VALEUR
}
#
# Transformation d’une matrice creuse en matrice
#
mc2matrix <- function( mc) {
dim.mat <- dim.mc( mc)
mat <- matrix( 0, nrow=dim.mat[1], ncol=dim.mat[2])
idx.mat <- as.matrix( head( mc, -2))[,c( 1, 2)]
mat[idx.mat] <- head( mc, -2)$VALEUR
mat
}
#
# Transformation inverse
#
matrix2mc <- function( mat) {
dim.mat <- dim( mat)
idx.mat <- which( mat != 0, arr.ind=TRUE)
mc <- cbind.data.frame( as.data.frame( idx.mat), as.vector( mat[idx.mat]))
mc <- rbind( mc, as.data.frame( matrix( c( NA, NA, NA, NA, dim.mat),
ncol=3, dimnames=list( NULL, colnames( mc)))))
names( mc) <- c( "LIGNE", "COLONNE", "VALEUR")
mc
}
#
# Calcul de la trace de la matrice
#
trace.mc <- function( mc) {
tr.mc <- subset( head( mc, -2), LIGNE == COLONNE)
ifelse( nrow( tr.mc) == 0, 0, sum( tr.mc$VALEUR))
}
#
# Somme d’au moins deux matrices creuses
#
somme.mc <- function( mc1, mc2, ...) {
liste.mc <- c( list( mc2), list( ...))
dim.list <- sapply( liste.mc, FUN=dim.mc)
dim.mc1 <- dim.mc( mc1)
id.dim <- all( dim.list[1,] == dim.mc1[1]) & all( dim.list[2,] == dim.mc1[2])
if( !id.dim) {
stop( "Les matrices ne sont pas toutes de même dimension")
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 84
}
mc.res <- head( mc1, -2)
for( n.mc in seq.int( length( liste.mc))) {
mc.res <- merge( mc.res, head( liste.mc[[n.mc]], -2), by=c( "LIGNE",
"COLONNE"), all=TRUE)
}
mc.res[is.na( mc.res)] <- 0
mc.res$VALEUR <- apply( mc.res, 1, FUN=function( x) sum( x[3:ncol(
mc.res)]))
rbind( mc.res[,c( "LIGNE", "COLONNE", "VALEUR")], data.frame(
LIGNE=c( NA, NA), COLONNE=c( NA, NA), VALEUR=dim.mc1))
}
#
# Multiplication de deux matrices creuses
#
mult.mc <- function( mc1, mc2) {
dim.mc1 <- dim.mc( mc1)
dim.mc2 <- dim.mc( mc2)
stopifnot( dim.mc2[1] == dim.mc1[2])
names( mc1) <- c( "LIGNE", "PIVOT", "VALEUR.x")
names( mc2) <- c( "PIVOT", "COLONNE", "VALEUR.y")
mc.res <- merge( head( mc1, -2), head( mc2, -2), by="PIVOT")
# Il est inutile de passer les valeurs NA qui correspondent à des zéros puisque le résultat de la
multiplication donnera zéro
mc.res <- aggregate( I( VALEUR.x * VALEUR.y) ~ LIGNE + COLONNE,
mc.res, sum)
names( mc.res) <- c( "LIGNE", "COLONNE", "VALEUR")
rbind( mc.res, data.frame( LIGNE=c( NA, NA), COLONNE=c( NA, NA),
VALEUR=c( dim.mc1[1], dim.mc2[2])))
}
# Vérifications
df.1 <- data.frame( LIGNE=c( sample( 1:30, size=12), NA, NA), COLONNE=c( sample(
1:20, size=12), NA, NA), VALEUR= c( rnorm( 12), 30, 20))
df.2 <- data.frame( LIGNE=c( sample( 1:20, size=8), NA, NA), COLONNE=c( sample( 1:20,
size=8), NA, NA), VALEUR= c( rnorm( 8), 20, 20))
mat.1 <- mc2matrix( df.1)
arr.1 <- which( mc2matrix( df.1) != 0, arr.ind=TRUE)
pos.1 <- as.matrix( head( df.1[,1:2], -2))
pos.1 <- pos.1[order( pos.1[,2], pos.1[,1]),]
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 85
all( arr.1 == pos.1)
df.1.inv <- matrix2mc( mat.1)
all( dim.mc( df.1) == dim.mc( df.1.inv))
df.m.1 <- merge( df.1, df.1.inv, by=c( "LIGNE", "COLONNE"))
identical( df.m.1$VALEUR.X, df.m.1$VALEUR.Y)
df.3 <- data.frame( LIGNE=c( 1, 2, 2, NA, NA), COLONNE=c( 1, 1, 2, NA, NA),
VALEUR=c( 1:3, 3, 3))
trace.mc( df.3)
df.4 <- data.frame( LIGNE=c( 1, 1, 3, NA, NA), COLONNE=c( 1, 2, 2, NA, NA),
VALEUR=c( 2, 2, 2, 3, 3))
somme( df.1, df.3)
s23 <- somme.mc( df.3, df.4)
all( mc2matrix( s23) == mc2matrix( df.3) + mc2matrix( df.4))
mult.mc( df.1, df.4)
m14 <- mult.mc( df.1, df.2)
all( mc2matrix( m14) == mc2matrix( df.1) %*% mc2matrix( df.2))
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 86
Les packages essentiels
R propose par défaut un certains nombres de packages qui constituent le cœur :
Fonctions de base.
base
compiler Compilateur.
Jeux de données de base.
datasets
grDevices Gestion des dispositifs graphiques.
graphics Fonctions pour les graphiques de base.
Présentation des graphiques et interactions.
grid
methods Outils de base pour la programmation objet dans R.
Support pour la programmation parallèle (multicore).
parallel
Fonctions spline et régression spline.
splines
Fonctions statistiques de base.
stats
Fonctions statistiques de base.
stats4
Interface avec le langage Tcl/Tk pour la programmation des interfaces utilisateur.
tcltk
Outils pour le développement de package et leur administration.
tools
Functions utilitaires.
utils
Si ces packages permettent de pratiquer l’essentiel des analyses, les packages suivants21 sont
un complément utiles aux packages de base mais ce ne sont pas les seuls :
FactoMineR22
MASS
Matrix
R.matlab
Rcmdr
boot
car
class
cluster
Analyse des données ‘à la française’ (ACP,
AFC, AFM…)
Fonctions et jeux de données développés par
Venables et Ripley, “Modern Applied Statistics
with S”.
Manipulations avancées de matrices
Interface avec Malab ; lecture et écriture de
fichier .mat.
Interface R Commander
Fonctions et jeux de données pour réaliser des
analyses bootstrap (“Bootstrap Methods and
Their Applications” by A. C. Davison and D. V.
Hinkley, 1997, Cambridge University Press.)
Fonctions supplémentaires pour la régression
Fonctions de classification (k-nearest neighboor
et LVQ).
Fonctions pour l’analyse des cluster.
21
Le chargements de ces packages peuvent conduire au chargement d’autres packages dont ils dépendent.
Par exemple, si on charge car, il est inutile de charger MASS puisque car en dépend et le charge
automatiquement.
22
Ce package est développé par l’équipe de J. Pagès à Rennes, équipe à l’origine de beaucoup de ces
méthodes ; il existe un autre package à peu près équivalent ade4 développé par une équipe lyonnaise.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 87
coin
foreign
klaR
igraph
lattice
moments
multcomp
nlme23
nnet
oro.nifti
oro.dicom
pROC
rpart
survival
Zelig
23
Inférences conditionnelles dans le cadre des
tests de permulation
Fonctions pour écrire et lire des données aux
format d’autres logiciels statistiques comme
Minitab, S, SAS, SPSS, Stata, Systat, etc.
Classification et visualisation de la
classification
Analyse et visualisation de graphes
Fonctions graphiques avancées.
Calculs et tests des moments d’une distribution
Tests post-hoc et comparaisons multiples.
Modèles mixtes linéaires et non-linéaires.
Perceptrons mono-couche et modèle loglinéaires multinomiaux.
Accès aux fichiers images en format Nifti,
Analyze et Dicom.
Courbes ROC
Partitions et arbres de régression / de décision.
Analyse de survie.
Interface console standardisée pour un certain
nombre de fonctions d’analyse statistique
Le package lme4 reprend et étend les fonctions développées dans ce package.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 88
Quelques références
Site de R : http://cran.r-project.org/
Documents accessibles à partir du site de R
Manuels édités par R :
http://cran.r-project.org/manuals.html
Documents tiers :
http://cran.r-project.org/other-docs.html (documents en différentes langues dont
certains en français)
http://www.r-project.org/other-docs.html (documents différents des précédents)
Questions :
http://cran.r-project.org/faqs.html
Journal d’information de R : journal.r-project.org
Autres documents, blogs, FAQ…
Série de présentation en français :
http://rug.mnhn.fr/semin-r/
Tutoriels :
http://pairach.com/2012/02/26/r-tutorials-from-universities-around-the-world/
Généralités
http://rdatamining.wordpress.com/2011/05/29/which-r-documents-to-read-and-whichr-packages-to-use/
http://www.ats.ucla.edu/stat/r/
R project mailing list :
http://web.archiveorange.com/archive/project/r-project.org/
R bloggers:
http://www.r-bloggers.com/
R graphs gallery:
http://gallery.r-enthusiasts.com/
R statistics blog :
http://www.r-statistics.com/all-articles/
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 89
Groupe francophone des utilisateurs de R:
http://forums.cirad.fr/logiciel-R/index.php?sid=634ad0a67ce9f1d128ffa000eb49844c
Et n’hésitez surtout pas d’utiliser les moteurs de recherche. Pour vous assurer que vous
filtrez bien les documents parlant de R, utiliser les mots-clés CRAN et R et non
simplement R.
Programmer R - Version 1.2 (mars 2013) F. Aubry
p. 90