Introduction non élémentaire au logiciel R
en 4 demi-journées
2. Manipulations élémentaires de données avec R
gilles.hunault "at" univ-angers.fr
Table des matières cliquable
1. Répertoires, scripts et fonctions (gH)
2. Lecture de fichiers textes, bureautique et gestion des accents
3. Listes en R
4. Variables quantitatives et qualitatives
5. Récupération de tableaux HTML
6. Regroupement et fusion de données
7. Indiçage, filtrage et sélection de données
8. Conversion de structures de données en R
9. Gestion des données manquantes
10. Export de données en HTML et LaTeX
11. R et SQL
12. Non présentation (!) des packages base, gdata, tools, foreign et utils
Il est possible d'afficher toutes les solutions via ?solutions=1.
1. Répertoires, scripts et fonctions (gH)
Comment dire à R qu'on veut changer de répertoire ? Et connaitre le répertoire en cours ? Qu'est-ce qu'un script ? Faut-il programmer pour écrire un script ? A quoi servent les fonctions source() et sink() ? Comment charger les fonctions (gH) ?
Solution : masquer la solution
Pour changer de répertoire, il faut utiliser la fonction setwd() et pour connaitre le répertoire en cours, on utilise getwd().
Un script est juste un ensemble de commandes R écrites dans un fichier, comme celles dans les encadrés en bleu clair de cette page. Il n'y a pas besoin de savoir programmer pour écrire des scripts élémentaires. Pour éxécuter un script dans une session, on utilise la fonction source() avec éventuellement les paramètres echo, verbose, print.eval etc. La fonction sink() permet de gérer la sortie des affichages, voire de la rediriger vers un fichier, pour conserver une trace de ce qui a été exécuté.
Voici un exemple de script, nommé (!) prog.r encodé en ISO8859-1.
# exemple de script R annee <- 2013 essai <- c(1,8) cat(" tout va bien pour l'année ",2013,".\n Voici la date et l'heure :") date() essai # est-ce affiché ? nba <- 10 # nombre de valeurs aléatoires cat("voici ",nba," nombres entiers \"au hasard\" (enfin, presque)\n") print( round(runif(n=nba,min=1,max=50)) )Voici ce qui se passe si on essaie de l'exécuter sous Linux (qui utilise par défaut un encodage UTF).
> source("prog.r") Erreur dans source("prog.r") : caractères multioctets incorrects dans l'analyse de code (parser) à la ligne 6 De plus : Messages d'avis : 1: In grepl("\n", lines, fixed = TRUE) : la chaîne de caractères entrée 6 est incorrecte dans cet environnement linguistique 2: In grepl("\n", lines, fixed = TRUE) : la chaîne de caractères entrée 9 est incorrecte dans cet environnement linguistique 3: In grepl("\n", lines, fixed = TRUE) : la chaîne de caractères entrée 11 est incorrecte dans cet environnement linguistiqueIl faut donc utiliser le paramètre encoding ici :
> source("prog.r",encoding="latin1") tout va bien pour l'année 2013 . Voici la date et l'heure :voici 10 nombres entiers "au hasard" (enfin, presque) [1] 36 22 7 24 39 48 18 8 37 22C'est déjà mieux, sauf pour la sortie... Par contre, on ne voit pas quelles affectations ont été exécutées. Essayons avec le paramètre echo :
> source("prog.r",encoding="latin1",echo=TRUE,print.eval=FALSE) > # exemple de script R > > annee <- 2013 > essai <- c(1,8) > cat(" tout va bien pour l'année ",2013,".\n Voici la date et l'heure :") tout va bien pour l'année 2013 . Voici la date et l'heure : > date() > essai # est-ce affiché ? > nba <- 10 # nombre de valeurs aléatoires > cat("voici ",nba," nombres entiers \"au hasard\" (enfin, presque)\n") voici 10 nombres entiers "au hasard" (enfin, presque) > print( round(runif(n=nba,min=1,max=50)) ) [1] 8 23 17 35 26 35 38 45 41 2Ce n'est pas cela, mais au moins, on voit quelle instruction est exécutée. La solution doit sans doute utiliser le paramètre print.eval :
> source("prog.r",encoding="latin1",echo=TRUE,print.eval=TRUE) > # exemple de script R > > annee <- 2013 > essai <- c(1,8) > cat(" tout va bien pour l'année ",2013,".\n Voici la date et l'heure :") tout va bien pour l'année 2013 . Voici la date et l'heure : > date() [1] "Sun Nov 10 10:19:35 2013" > essai # est-ce affiché ? [1] 1 8 > nba <- 10 # nombre de valeurs aléatoires > cat("voici ",nba," nombres entiers \"au hasard\" (enfin, presque)\n") voici 10 nombres entiers "au hasard" (enfin, presque) > print( round(runif(n=nba,min=1,max=50)) ) [1] 40 5 37 36 9 15 36 35 20 25Enfin, on peut avoir encore plus de détails avec le paramètre verbose :
> source("prog.r",encoding="latin1",echo=TRUE,print.eval=TRUE,verbose=TRUE) 'envir' chosen:<environment: R_GlobalEnv> l'encodage 'encoding = "latin1" est sélectionné --> parsed 8 expressions; now eval(.)ing them: >>>> eval(expression_nr. 1 ) ================= > # exemple de script R > > annee <- 2013 curr.fun: symbol <- .. after expression(annee <- 2013) >>>> eval(expression_nr. 2 ) ================= > essai <- c(1,8) curr.fun: symbol <- .. after expression(essai <- c(1,8)) >>>> eval(expression_nr. 3 ) ================= > cat(" tout va bien pour l'année ",2013,".\n Voici la date et l'heure :") tout va bien pour l'année 2013 . Voici la date et l'heure :curr.fun: symbol cat .. after expression(cat(" tout va bien pour l'année ",2013,".\n Voici la date et l'heure :")) >>>> eval(expression_nr. 4 ) ================= > date() curr.fun: symbol date [1] "Sun Nov 10 10:21:56 2013" .. after expression(date()) >>>> eval(expression_nr. 5 ) ================= > essai # est-ce affiché ? [1] 1 8 .. after expression(essai) >>>> eval(expression_nr. 6 ) ================= > nba <- 10 # nombre de valeurs aléatoires curr.fun: symbol <- .. after expression(nba <- 10) >>>> eval(expression_nr. 7 ) ================= > cat("voici ",nba," nombres entiers \"au hasard\" (enfin, presque)\n") voici 10 nombres entiers "au hasard" (enfin, presque) curr.fun: symbol cat .. after expression(cat("voici ",nba," nombres entiers \"au hasard\" (enfin, presque)\n")) >>>> eval(expression_nr. 8 ) ================= > print( round(runif(n=nba,min=1,max=50)) ) [1] 39 5 43 6 14 24 19 24 26 50 curr.fun: symbol print .. after expression(print( round(runif(n=nba,min=1,max=50)) ))Les fonctions (gH) sont un ensemble de fonctions qui calculent puis affichent en français disponibles sur internet, à l'adresse statgh.r. Une page d'explication nommée statgh.php leur est consacrée et une interface les montre à l'adresse statghfns.php. Nous conseillons d'apprendre à utiliser cats(), catss() et surtout sinksrc() pour travailler sereinement avec les scripts. C'est grâce à cette dernière fonction que nous avons produit toutes les sorties de ces pages Web.
2. Lecture de fichiers textes, bureautique et gestion des accents
Comment lire un fichier-texte de valeurs numériques comme elf.txt avec R ? Et un fichier Excel comme elf.xls ou elf.xlsx ? Et un fichier Open Office Calc comme elf.ods ? Et un fichier avec des valeurs caractères comme elf2.txt ? Et un fichier CSV ou un fichier délimité avec des points-virgules ?
R sait-il lire des fichiers avec des accents Windows ? des accents ISO, UTF ?
Comment lire du texte "brut" ?
Questions spécialisées :
«Mes données ont parfois des lignes vides et des commentaires, comme ci-dessous (fichier dataspec.txt) :
-- ## Entete standard (5 lignes) -- ## Projet -- ## Date -- ## Auteur -- ## Version # data manip 1 A35 617 552 1 A37 417 652 1 # sous-série 1A A38 410 565 1A A39 390 700 1A A40 500 540 1A # sous-série 1B A60 500 540 1B A61 480 408 1B # data manip 2 A92 480 408 2 A93 480 408 2 A94 480 408 2 A95 480 408 2 A96 480 408 2 A97 480 408 2 A98 480 408 2 A99 480 408 2Comment les lire avec R ?»
«Moi, j'utilise souvent des données issues du site web PLACE et mes données ressemblent à
Web Signal Scan Program Database Searched: PLACE This is the sequence you submitted >nc|dna|Medtr4g055610_upstream parent=4 chrom=4 range=17080451-17081950 length=1500 strand=1 sp=Medicago_truncatula, 1500 bases, A9ED66C3 checksum. AAACAAAATAAATTTACAGTCAGCGTGCCACATTAGCGAAAATGCGCATT CAGATGACCTATGGGGTATTTTGAAACAATTTTCTTTTACAAGAACCGAA TTGAATTTTTTTTATAGGAGGCAAACCAGGAAAAAAACTATATATTATAG GAGGG... RESULTS OF YOUR SIGNAL SCAN SEARCH REQUEST ../../tmp/sigscan//signaldone.10566: 1500 base pairs Signal Database File: user.dat Factor or Site Name Loc.(Str.) Signal Sequence SITE # _____________________________________________________________________________________ -300ELEMENT site 84 (-) TGHAAARK S000122 2SSEEDPROTBANAPA site 1149 (+) CAAACAC S000143 2SSEEDPROTBANAPA site 589 (-) CAAACAC S000143 ... WRKY71OS site 55 (+) TGAC S000447 WRKY71OS site 201 (+) TGAC S000447 WRKY71OS site 835 (+) TGAC S000447 WRKY71OS site 1075 (+) TGAC S000447 WRKY71OS site 1206 (+) TGAC S000447 WRKY71OS site 1437 (+) TGAC S000447 WRKY71OS site 19 (-) TGAC S000447 WRKY71OS site 287 (-) TGAC S000447 WRKY71OS site 322 (-) TGAC S000447 WRKY71OS site 814 (-) TGAC S000447 WRKY71OS site 987 (-) TGAC S000447 WRKY71OS site 1003 (-) TGAC S000447 WRKY71OS site 1040 (-) TGAC S000447 WRKY71OS site 1058 (-) TGAC S000447 WRKY71OS site 1319 (-) TGAC S000447 ------------------------------------------- o If you use this program in published research, please cite: - Higo, K., Y. Ugawa, M. Iwamoto and T. Korenaga (1999) Plant cis-acting regulatory DNA elements (PLACE) database:1999. Nucleic Acids Research Vol.27 No.1 pp. 297-300. - Prestridge, D.S. (1991) SIGNAL SCAN: A computer program that scans DNA sequences for eukaryotic transcriptional elements. CABIOS 7, 203-206.Comment récupérer juste les indications de comptage et de localisation de site ?»
Solution : masquer la solution
Comme nous l'avons vu dans la séance 1, R dispose avec la fonction read.table() de nombreuses options pour lire les fichiers-textes. Cette fonction read.table() appartient au package utils.
Les fichiers qui contiennent des données numériques pour traitements statistiques ont en général une structure particulière, à savoir un nombre constant de "mots-valeur" par ligne, ce qui permet de parler de colonnes de chiffres ou variables statistiques. Ces valeurs sont souvent délimitées ou séparées par un même symbole, parfois répété, comme l'espace, le caractère de tabulation, la virgule ou le point virgule. En particulier le format dit CSV est très prisé par les statisticien(nes). R sait le traiter avec la fonction read.csv(). Comme les américains utilisent un point pour les décimales, la virgule peut servir de séparateur. En France, la virgule sert pour les décimales et on utilise donc plutôt le point-virgule comme séparateur, d'où les fonctions read.csv() et read.csv2(), mais il est possible, bien sûr, d'indiquer son propre délimiteur. Cette notion de structure en colonne définit ce que R nomme des data frame. Utiliser read.table() renvoie un data frame. Les data frame ressemblent à des matrices, avec des lignes et des colonnes, mais ce ne sont pas des matrices car ce sont des listes.
# attention au typage que R choisit (1) ( vec1 <- c(1,5,8) ) ( vec <- c(1,"5",8) ) # attention au typage que R choisit (2) ( mat1 <- matrix(1:12,nrow=4) ) mat1[2,2] <- 50 print(mat1) mat2 <- mat1 mat2[2,2] <- "50" print(mat2) # on notera au passage le nom des colonnes df <- data.frame(mat1) df[2,2] <- "50" print(df)> # attention au typage que R choisit (1) > > ( vec1 <- c(1,5,8) ) [1] 1 5 8 > ( vec <- c(1,"5",8) ) [1] "1" "5" "8" > # attention au typage que R choisit (2) > > ( mat1 <- matrix(1:12,nrow=4) ) [,1] [,2] [,3] [1,] 1 5 9 [2,] 2 6 10 [3,] 3 7 11 [4,] 4 8 12 > mat1[2,2] <- 50 > print(mat1) [,1] [,2] [,3] [1,] 1 5 9 [2,] 2 50 10 [3,] 3 7 11 [4,] 4 8 12 > mat2 <- mat1 > mat2[2,2] <- "50" > print(mat2) [,1] [,2] [,3] [1,] "1" "5" "9" [2,] "2" "50" "10" [3,] "3" "7" "11" [4,] "4" "8" "12" > # on notera au passage le nom des colonnes > > df <- data.frame(mat1) > df[2,2] <- "50" > print(df) X1 X2 X3 1 1 5 9 2 2 50 10 3 3 7 11 4 4 8 12L'intérêt du format CSV est d'être un format-texte et qu'on peut donc voir les données avec un simple navigateur ou même un éditeur de texte (par exemple pour effectuer des Edition/Remplacer tout si besoin est). Les logiciels Excel et Open Office Write savent bien exporter dans ces formats.
Pour lire le fichier elf.txt, un simple appel de read.table() suffit. Il n'y a pas d'indication de nom de colonne en ligne 1, donc le paramètre head doit être mis à faux (FALSE en anglais). Le mot 1 de chaque ligne semble être un identifiant. Si tel est le cas, il faut utiliser row.names=1 sinon, pas besoin de préciser row.names. La fonction colnames() permet de définir ou de réfinir tout ou partie des noms de colonnes.
# lecture de elf.txt, le mot 1 de chaque ligne est considéré comme # étant un identifiant, la ligne 1 ne donne pas le nom des colonnes # début du fichier à lire : # M001 1 62 1 2 2 3 # M002 0 60 9 3 4 1 # M003 1 31 9 4 4 1 # M004 1 27 8 4 1 1 # M005 0 22 8 4 1 2 # M006 1 70 4 1 1 1 dataElf <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.txt",header=FALSE,row.names=1) print( dim(dataElf) ) print( head(dataElf) ) print( tail(dataElf) ) # autre lecture, pas de traitement particulier du mot 1 # après lecture, on définit le nom des colonnes dataElf <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.txt",header=FALSE) print( dim(dataElf) ) print( tail(dataElf) ) colnames(dataElf) <- c("NOM","codeSEXE","AGE","PROF","ETUD","AXIO1","AXIO2") print( tail(dataElf) )> # lecture de elf.txt, le mot 1 de chaque ligne est considéré comme > # étant un identifiant, la ligne 1 ne donne pas le nom des colonnes > > # début du fichier à lire : > # M001 1 62 1 2 2 3 > # M002 0 60 9 3 4 1 > # M003 1 31 9 4 4 1 > # M004 1 27 8 4 1 1 > # M005 0 22 8 4 1 2 > # M006 1 70 4 1 1 1 > dataElf <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.txt",header=FALSE,row.names=1) > print( dim(dataElf) ) [1] 21 6 > print( head(dataElf) ) V2 V3 V4 V5 V6 V7 M001 1 62 1 2 2 3 M002 0 60 9 3 4 1 M003 1 31 9 4 4 1 M004 1 27 8 4 1 1 M005 0 22 8 4 1 2 M006 1 70 4 1 1 1 > print( tail(dataElf) ) V2 V3 V4 V5 V6 V7 M016 1 50 1 1 4 2 M017 0 49 7 4 4 1 M096 1 17 12 3 1 0 M097 1 39 1 2 1 0 M098 0 62 6 3 1 0 M100 1 48 9 4 2 0 > # autre lecture, pas de traitement particulier du mot 1 > # après lecture, on définit le nom des colonnes > > dataElf <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.txt",header=FALSE) > print( dim(dataElf) ) [1] 21 7 > print( tail(dataElf) ) V1 V2 V3 V4 V5 V6 V7 16 M016 1 50 1 1 4 2 17 M017 0 49 7 4 4 1 18 M096 1 17 12 3 1 0 19 M097 1 39 1 2 1 0 20 M098 0 62 6 3 1 0 21 M100 1 48 9 4 2 0 > colnames(dataElf) <- c("NOM","codeSEXE","AGE","PROF","ETUD","AXIO1","AXIO2") > print( tail(dataElf) ) NOM codeSEXE AGE PROF ETUD AXIO1 AXIO2 16 M016 1 50 1 1 4 2 17 M017 0 49 7 4 4 1 18 M096 1 17 12 3 1 0 19 M097 1 39 1 2 1 0 20 M098 0 62 6 3 1 0 21 M100 1 48 9 4 2 0Pour lire le fichier Excel .xls correspondant, il faut avoir installé le package gdata. On dispose alors de la fonctions read.xls() alors que pour lire la version .xlsx du fichier, il faut avoir installé le package xlsx qui fournit la fonction read.xlsx() dont le paramètre sheetIndex est requis, sans valeur par défaut. On peut aussi utiliser le package XLConnect, mais on ne peut lire que des fichiers locaux. XLConnect écrit aussi des fichiers Excel. La lecture de XLConnect.pdf est conseillée.
# lecture de elf.xls et elf.xlsx library(gdata) # pour read.xls dataElf <- read.xls("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.xls") print( head(dataElf) ) library(xlsx) # pour read.xlsx # écrire # # dataElf <- read.xlsx("elf.xlsx") # # échoue et affiche # # << Erreur dans read.xlsx("elf.xlsx") : # Please provide a sheet name OR a sheet index. >> dataElf <- read.xlsx("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.xlsx",sheetIndex=1) print( head(dataElf) )> # lecture de elf.xls et elf.xlsx > > library(gdata) # pour read.xls > dataElf <- read.xls("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.xls") > print( head(dataElf) ) NUM SEXE AGE PROF ETUD REGI USAG 1 M001 1 62 1 2 2 3 2 M002 0 60 9 3 4 1 3 M003 1 31 9 4 4 1 4 M004 1 27 8 4 1 1 5 M005 0 22 8 4 1 2 6 M006 1 70 4 1 1 1 > library(xlsx) # pour read.xlsx > # écrire > # > # dataElf <- read.xlsx("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf.xlsx") > # > # échoue et affiche > # > # << Erreur dans read.xlsx("elf.xlsx") : > # Please provide a .... [TRUNCATED] > print( head(dataElf) ) NUM SEXE AGE PROF ETUD REGI USAG 1 M001 1 62 1 2 2 3 2 M002 0 60 9 3 4 1 3 M003 1 31 9 4 4 1 4 M004 1 27 8 4 1 1 5 M005 0 22 8 4 1 2 6 M006 1 70 4 1 1 1Il n'est pas forcément aisé de lire des fichiers .ods car la fonction read.gnumeric.sheet() du package gnumeric requiert que le logiciel gnumeric soit aussi installé, à cause de sa commande ssconvert. Nous conseillons plutôt d'enregistrer le fichier .ods en .xls et de lire comme ci-dessus. Il n'est pas conseillé d'enregistrer en .xlsx parce que cela produit des fichiers plus gros, plus complexes (les fichiers .xlsx sont en fait des archives qui contiennent des fichiers XML), ce qui rend la lecture plus lente.
Lorsqu'on lit des données caractères, R en fait automatiquement par défaut des facteurs, c'est-à-dire des variables qualitatives dont les modalités sont les divers mots rencontrés. Pour modifier ce comportement, il faut utiliser le paramètre as.is de read.table().
# lecture de elf2.txt, le mot 1 de chaque ligne est considéré comme # étant un identifiant, la ligne 1 donne le nom des colonnes # début du fichier à lire : # NUM SEXE AGE PROF ETUD REGI USAG # M001 Femme 62 1 2 2 3 # M002 Homme 60 9 3 4 1 # M003 Femme 31 9 4 4 1 # M004 Femme 27 8 4 1 1 # M005 Homme 22 8 4 1 2 dataElf <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf2.txt",header=TRUE,row.names=1) cat("début des données (1)\n") print( dim(dataElf) ) print( class(dataElf$SEXE) ) print( head(dataElf$SEXE) ) # la colonne 2 (SEXE) est caractère et on veut la garder comme telle dataElf2 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf2.txt",header=TRUE,row.names=1,as.is=TRUE) cat("début des données (2)\n") print( dim(dataElf2) ) print( class(dataElf$SEXE) ) print( head(dataElf2$SEXE) )> # lecture de elf2.txt, le mot 1 de chaque ligne est considéré comme > # étant un identifiant, la ligne 1 donne le nom des colonnes > > # début du f .... [TRUNCATED] > cat("début des données (1)\n") début des données (1) > print( dim(dataElf) ) [1] 99 6 > print( class(dataElf$SEXE) ) [1] "factor" > print( head(dataElf$SEXE) ) [1] Femme Homme Femme Femme Homme Femme Levels: Femme Homme > # la colonne 2 (SEXE) est caractère et on veut la garder comme telle > > dataElf2 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf2.txt",header=TRUE,row.names=1,as.is=TRUE) > cat("début des données (2)\n") début des données (2) > print( dim(dataElf2) ) [1] 99 6 > print( class(dataElf$SEXE) ) [1] "factor" > print( head(dataElf2$SEXE) ) [1] "Femme" "Homme" "Femme" "Femme" "Homme" "Femme"La prise en compte des accents se fait avec le paramètre encoding='latin1'. On peut aussi utiliser Encoding() et iconv().
# lecture de elf3.txt, le mot 1 de chaque ligne est considéré comme # étant un identifiant, la ligne 1 donne le nom des colonnes # il y a des accents dans le fichier, encodage ISO8859-15 ("latin1") # début du fichier à lire : # NUM SEXE AGE PROF études REGI USAG # M001 1 62 1 2 2 très_sûr # M002 0 60 9 3 4 hésitant # M003 1 31 9 4 4 hésitant # M004 1 27 8 4 1 hésitant # M005 0 22 8 4 1 sûr # ne pas utiliser # # dataElf <- read.table("elf3.txt",header=TRUE,row.names=1) # # car cela provoque l'erreur # # << Erreur dans make.names(col.names, unique = TRUE) : # chaîne de charactères multioctets incorrecte 7 à '<e9>tudes' >> dataElf <- read.table("elf3.txt",header=TRUE,row.names=1,encoding="latin1") > cat("début des données \n") début des données > print( dim(dataElf) ) [1] 21 6 > print( head(dataElf) ) SEXE AGE PROF études REGI USAG M001 1 62 1 2 2 très_sûr M002 0 60 9 3 4 hésitant M003 1 31 9 4 4 hésitant M004 1 27 8 4 1 hésitant M005 0 22 8 4 1 sûr M006 1 70 4 1 1 hésitant # suivant la configuration, la lecture de caractères accentués # peut "presque bien" se passer : > dataElf <- read.table("elf4.txt",header=TRUE,row.names=1) # (la colonne 4 est nommée ETUD) > print( head(dataElf) ) SEXE AGE PROF ETUD REGI USAG M001 1 62 1 2 2 tr\xe8s_s\xfbr M002 0 60 9 3 4 h\xe9sitant M003 1 31 9 4 4 h\xe9sitant M004 1 27 8 4 1 h\xe9sitant M005 0 22 8 4 1 s\xfbr M006 1 70 4 1 1 h\xe9sitantPour lire un fichier-texte "littéraire" comme cigale.txt, on utilise readLines(), sachant qu'en mode interactif, readline() (sans "s" et avec un "l" minuscule) assure la lecture au clavier.
> options(width=50) # sinon plusieurs strophes sur une même ligne > fable <- readLines("cigale.txt",encoding="latin1") > print(head(fable)) [1] " Une fable de Jean de la Fontaine (1621-1695) : << La Cigale et la Fourmi >>" [2] " ----------------------------------------------------------------------------" [3] "" [4] " La cigale, ayant chanté" [5] " Tout l'été," [6] " Se trouva fort dépourvue" > print(tail(fable),quote=FALSE) [1] - Nuit et jour à tout venant [2] Je chantais, ne vous déplaise. [3] - Vous chantiez ? j'en suis fort aise. [4] Eh bien : dansez maintenant." [5] ** [6] > options(width=1024) # plus confortable pour la suiteRéponses aux questions spécialisées :
Les lignes vides et les commentaires ne sont pas un problème car R a des options pour cela. Voici les instructions R correspondantes à la lecture de videcmt.txt. Tout le travail est fait par read.table().
data <- read.table( file="http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/videcmt.txt", skip=5, comment.char="#", blank.lines.skip=TRUE, row.names=1, nrows=8 ) # fin de read.table colnames(data) <- c("Duree","Bolus","Serie") print(data)Et le résultat de leur exécution :
Duree Bolus Serie A35 617 552 1 A37 417 652 1 A38 410 565 1A A39 390 700 1A A40 500 540 1A A60 500 540 1B A61 480 408 1B A92 480 408 2Par contre la lecture des fichiers pour le logiciel PLACE ne peut pas se faire de façon simple. Il faut écrire un programme pour parcourir les lignes, ignorer ce qu'on lit tant qu'on n'a pas vu la première ligne de tirets, puis découper les lignes et s'arrêter lorsque qu'on rencontre la seconde ligne de tirets. Il faut donc apprendre à programmer en R pour y arriver, ce qui est le sujet d'une autre session de cours...
3. Listes en R
Quelle est la différence entre une liste et un vecteur ? Comment utilise-t-on les listes en R ?
Solution : masquer la solution
Une liste se crée avec list(). Comme un vecteur, c'est un conteneur, mais les divers éléments peuvent être de nature différente. Il est d'usage de parler des éléments d'un vecteur et des composant[e]s d'une liste. Eléments et composants peuvent être nommés, mais l'usage veut qu'on nomme surtout les composants. On accède aux composants par leur nom et/ou leur position. On peut appliquer une fonction à tous les composants d'une liste avec lapply(), qui renvoie une liste, et avec sapply, qui renvoie un vecteur. Le nombre de composants s'obtient avec la fonction length().
# un vecteur se doit d'être homogène au niveau des éléments ( vec1 <- c(1,2,3,4) ) vec1[3] <- "3" print(vec1) # on peut ajouter des éléments vec1[5] <- 8 print(vec1) # écrire : # vec1[3] <- c(3,4) # produirait l'erreur # Message d'avis : # In vec1[3] <- c(3, 4) : # le nombre d'objets à remplacer n'est pas multiple de la taille du remplacement # une liste est un peu comme un vecteur... ( lst1 <- list(1,2,3,4) ) # mais rien n'oblige les composants à être homogènes lst1[3] <- "3" # identique à lst1[[3]] <- "3" avec des doubles crochets ? print(lst1) # et une liste peut contenir une autre liste # ou un vecteur, ou... lst1[[3]] <- c(31,32) # là, il faut des doubles crochets print(lst1) lst1[[5]] <- list(51,52) print(lst1) # il est d'usage de nommer les composants des listes id <-list(prenom="Jean",nom="DUPONT") # on peut alors utiliser les composants nommés via $ cat("Monsieur ",id$nom,"\n") # mais on peut aussi nommer les éléments d'un vecteur vec2 <- c(8,5,3) print(vec2) names(vec2) <- c("premier","deuxième","dernier") print(vec2) # mais utiliser vec2$dernier # produirait l'erreur # Erreur dans vec2$dernier : $ operator is invalid for atomic vectors # utilisation courante lst2 <- list(debut=c(3,5),option="aucune",resultat=c(17,61,54,11)) # lapply applique une fonction à tous les composants d'une liste lst3 <- list( ageshommes <- c(10,20,30), agesfemmes <- c(12,15,18,31,42) ) # fin de list lst4 <- lapply(X=lst3,FUN=length) print(lst4) # sapply aussi, mais simplifie et renvoie un vecteur vec3 <- sapply(X=lst3,FUN=length) print(vec3) # vérifions le type des éléments : print( class(lst4) ) print( class(vec3) ) # au passage, on écrirait plutôt print( sapply(X=list(lst4,vec3),FUN=class) )> # un vecteur se doit d'être homogène au niveau des éléments > > ( vec1 <- c(1,2,3,4) ) [1] 1 2 3 4 > vec1[3] <- "3" > print(vec1) [1] "1" "2" "3" "4" > # on peut ajouter des éléments > > vec1[5] <- 8 > print(vec1) [1] "1" "2" "3" "4" "8" > # écrire : > # vec1[3] <- c(3,4) > # produirait l'erreur > # Message d'avis : > # In vec1[3] <- c(3, 4) : > # le nombre d'objets à remplace .... [TRUNCATED] [[1]] [1] 1 [[2]] [1] 2 [[3]] [1] 3 [[4]] [1] 4 > # mais rien n'oblige les composants à être homogènes > > lst1[3] <- "3" # identique à lst1[[3]] <- "3" avec des doubles crochets ? > print(lst1) [[1]] [1] 1 [[2]] [1] 2 [[3]] [1] "3" [[4]] [1] 4 > # et une liste peut contenir une autre liste > # ou un vecteur, ou... > > lst1[[3]] <- c(31,32) # là, il faut des doubles crochets > print(lst1) [[1]] [1] 1 [[2]] [1] 2 [[3]] [1] 31 32 [[4]] [1] 4 > lst1[[5]] <- list(51,52) > print(lst1) [[1]] [1] 1 [[2]] [1] 2 [[3]] [1] 31 32 [[4]] [1] 4 [[5]] [[5]][[1]] [1] 51 [[5]][[2]] [1] 52 > # il est d'usage de nommer les composants des listes > > id <-list(prenom="Jean",nom="DUPONT") > # on peut alors utiliser les composants nommés via $ > > cat("Monsieur ",id$nom,"\n") Monsieur DUPONT > # mais on peut aussi nommer les éléments d'un vecteur > > vec2 <- c(8,5,3) > print(vec2) [1] 8 5 3 > names(vec2) <- c("premier","deuxième","dernier") > print(vec2) premier deuxième dernier 8 5 3 > # mais utiliser vec2$dernier > # produirait l'erreur > # Erreur dans vec2$dernier : $ operator is invalid for atomic vectors > > > # utilisatio .... [TRUNCATED] > # lapply applique une fonction à tous les composants d'une liste > > lst3 <- list( + ageshommes <- c(10,20,30), + agesfemmes <- c(12,15,18,31,4 .... [TRUNCATED] > lst4 <- lapply(X=lst3,FUN=length) > print(lst4) [[1]] [1] 3 [[2]] [1] 5 > # sapply aussi, mais simplifie et renvoie un vecteur > > vec3 <- sapply(X=lst3,FUN=length) > print(vec3) [1] 3 5 > # vérifions le type des éléments : > > print( class(lst4) ) [1] "list" > print( class(vec3) ) [1] "integer" > # au passage, on écrirait plutot > > print( sapply(X=list(lst4,vec3),FUN=class) ) [1] "list" "integer"
4. Variables quantitatives et qualitatives
Comment R gère-t-il les variables qualitatives et quantitatives ? Qu'obtient-on quand on lit des données dans un fichier, du quantitatif ou du qualitatif ?
Solution : masquer la solution
R n'a qu'une seule règle : tout ce qui est numérique est quantitatif. Et donc, comme corollaire, tout ce qui est caractère est qualitatif. Pour convertir de quantitatif à qualitatif, il faut passer par la fonction factor() et indiquer les labels des modalités. Dans le vocabulaire R, les variables qualitatives sont dénommées facteurs. Par contre, pour convertir dans l'autre sens, si l'on connait des bornes de découpage, on peut utiliser ifelse() et cut().
# lecture des données, toutes numériques elf <- lit.dar("http://www.info.univ-angers.fr/~gh/Datasets/elf.dar") # utilisation de la donnée CODE-SEXE, colonne numéro 1 ou "SEXE" # 0 correspond à Homme, 1 à Femme selon le descriptif fourni à la page # http://forge.info.univ-angers.fr/~gh/Datasets/elf.htm sexe <- elf$SEXE # comme sexe est numérique, c'est a priori une variable quantitatie cat("moyenne ",mean(sexe)," pour ",length(sexe)," personnes\n") cat(" classe d'objets associée : ",class(sexe),"\n\n") # transformation en une variable qualitative sexeql <- factor(sexe,levels=0:1,labels=c("Homme","Femme")) # vérification par les types print( sapply(X=list(sexe,sexeql),FUN=class) ) # désormais le calcul de mean(sexeql) renvoie l'erreur : # << l'argument n'est ni numérique, ni logique : renvoi de NA >> cat("sexeql, classe d'objets associée : ",class(sexeql),"\n\n") # par contre la fonction table réalise le tri à plat avec les labels print(table(sexeql)) # pour une version américaine, il n'y a qu'à changer les labels sexeqlUS <- factor(sexeql,labels=c("Male","Female")) print(table(sexeqlUS)) # on peut alors utiliser la notation ~ ("en fonction de") pour les calculs et tracés print( t.test(elf$AGE~sexeql) ) boxplot(elf$AGE~sexeql,notch=TRUE,col="yellow",main="Boite à moustaches")> # lecture des données, toutes numériques > > elf <- lit.dar("http://www.info.univ-angers.fr/~gh/Datasets/elf.dar") > # utilisation de la donnée CODE-SEXE, colonne numéro 1 ou "SEXE" > # 0 correspond à Homme, 1 à Femme selon le descriptif fourni à la page > # http:/ .... [TRUNCATED] > # comme sexe est numérique, c'est a priori une variable quantitatie > > cat("moyenne ",mean(sexe)," pour ",length(sexe)," personnes\n") moyenne 0.6464646 pour 99 personnes > cat(" classe d'objets associée : ",class(sexe),"\n\n") classe d'objets associée : integer > # transformation en une variable qualitative > > sexeql <- factor(sexe,levels=0:1,labels=c("Homme","Femme")) > # vérification par les types > > print( sapply(X=list(sexe,sexeql),FUN=class) ) [1] "integer" "factor" > # désormais le calcul de mean(sexeql) renvoie l'erreur : > # << l'argument n'est ni numérique, ni logique : renvoi de NA >> > > cat("sexeql, clas ..." ... [TRUNCATED] sexeql, classe d'objets associée : factor > # par contre la fonction table réalise le tri à plat avec les labels > > print(table(sexeql)) sexeql Homme Femme 35 64 > # pour une version américaine, il n'y a qu'à changer les labels > > sexeqlUS <- factor(sexeql,labels=c("Male","Female")) > print(table(sexeqlUS)) sexeqlUS Male Female 35 64 > # on peut alors utiliser la notation ~ ("en fonction de") pour les calculs et tracés > > print( t.test(elf$AGE~sexeql) ) Welch Two Sample t-test data: elf$AGE by sexeql t = 0.2431, df = 74.04, p-value = 0.8086 alternative hypothesis: true difference in means is not equal to 0 95 percent confidence interval: -6.363368 8.132118 sample estimates: mean in group Homme mean in group Femme 36.40000 35.51562 > boxplot(elf$AGE~sexeql,notch=TRUE,col="yellow",main="Boite à moustaches")Remarque : si on veut garder toutes les colonnes caractères sous forme de chaines de caractères, il faut préciser as.is=TRUE dans la lecture des fichiers ou utiliser stringsAsFactors=FALSE.
5. Récupération de tableaux HTML
Si on lit la page du wiki anglais sur les acides aminés, soit [...]wiki/Amino_acid les tableaux 2 (classification) et 4 (propriétés) semblent très intéressants. Comment récupérer leur contenu dans un data frame ?
Reprendre avec le tableau 4 (propriétés) de la page correspondante du wiki français.
Solution : masquer la solution
Avec la fonction readHTMLTable() du package XML, rien de plus simple, la preuve :
# chargement du package XML library(XML) # url des pages Web à utiliser adrEN <- "https://en.wikipedia.org/wiki/Amino_acid" adrFR <- "https://fr.wikipedia.org/wiki/Acide_amin%C3%A9" # tableau EN numéro 2 cats("Classification of Amino Acids") tabaa1 <- readHTMLTable(adrEN,which=2) print(tabaa1,right=FALSE) # tableau EN numéro 4 cats("Table of standard amino acid abbreviations and properties") tabaa2 <- readHTMLTable(adrEN,which=4,encoding="UTF-8") print(tabaa2,right=FALSE) # tableau FR numéro 2 cats("Principales données caractéristiques des 22 acides aminés") tabaa3 <- readHTMLTable(adrFR,which=4,encoding="UTF-8") print(tabaa3,right=FALSE)Résultats d'exécution :
Classification of Amino Acids ============================= Class Name of the amino acids 1 Aliphatic Glycine, Alanine, Valine, Leucine, Isoleucine 2 Hydroxyl or Sulfur-containing Serine, Cysteine, Threonine, Methionine 3 Cyclic Proline 4 Aromatic Phenylalanine, Tyrosine, Tryptophan 5 Basic Histidine, Lysine, Arginine 6 Acidic and their Amide Aspartate, Glutamate, Asparagine, Glutamine Table of standard amino acid abbreviations and properties ========================================================= Amino Acid 3-Letter[112] 1-Letter[112] Side-chain polarity[112] Side-chain charge (pH 7.4)[112] Hydropathy index[113] Absorbance λmax(nm)[114] ε at λmax (x10−3 M−1 cm−1)[114] 1 Alanine Ala A nonpolar neutral 1.8 2 Arginine Arg R Basic polar positive −4.5 3 Asparagine Asn N polar neutral −3.5 4 Aspartic acid Asp D acidic polar negative −3.5 5 Cysteine Cys C nonpolar neutral 2.5 250 0.3 6 Glutamic acid Glu E acidic polar negative −3.5 7 Glutamine Gln Q polar neutral −3.5 8 Glycine Gly G nonpolar neutral −0.4 9 Histidine His H Basic polar positive(10%)\nneutral(90%) −3.2 211 5.9 10 Isoleucine Ile I nonpolar neutral 4.5 11 Leucine Leu L nonpolar neutral 3.8 12 Lysine Lys K Basic polar positive −3.9 13 Methionine Met M nonpolar neutral 1.9 14 Phenylalanine Phe F nonpolar neutral 2.8 257, 206, 188 0.2, 9.3, 60.0 15 Proline Pro P nonpolar neutral −1.6 16 Serine Ser S polar neutral −0.8 17 Threonine Thr T polar neutral −0.7 18 Tryptophan Trp W nonpolar neutral −0.9 280, 219 5.6, 47.0 19 Tyrosine Tyr Y polar neutral −1.3 274, 222, 193 1.4, 8.0, 48.0 20 Valine Val V nonpolar neutral 4.2 Principales données caractéristiques des 22 acides aminés ========================================================= Code Abrév. Acide aminé Masse\nmolaire pI pKa\n(α-COOH) pKb\n(α-NH3) pKc\n(chaîne\nlatérale) Nature Codons\nd'ARN messager Occurrence\n(% protéines\nhumaines) Abondance\n×106 molécules\nchez E. coli 1 A Ala Alanine 89,0940 6,01 2,35 9,87 - Apolaire, aliphatique GCU, GCC, GCA, GCG 7,8 x 290 2 C Cys Cystéine 121,1540 5,05 1,92 10,70 8,18 Polaire UGU, UGC 1,9 52 3 D Asp Acide aspartique 133,1038 2,85 1,99 9,90 3,90 Acide GAU, GAC 5,3 140 4 E Glu Acide glutamique 147,1307 3,15 2,10 9,47 4,07 Acide GAA, GAG 6,3 150 5 F Phe Phénylalanine 165,1918 5,49 2,20 9,31 - Apolaire, aromatique UUU, UUC 3,9 110 6 G Gly Glycine 75,0671 6,06 2,35 9,78 - Apolaire, aliphatique GGU, GGC, GGA, GGG 7,2 350 7 H His Histidine 155,1563 7,60 1,80 9,33 6,04 Basique, aromatique CAU, CAC 2,3 54 8 I Ile Isoleucine 131,1746 6,05 2,32 9,76 - Apolaire, aliphatique AUU, AUC, AUA 5,3 170 9 K Lys Lysine 146,1893 9,60 2,16 9,06 10,54 Basique AAA, AAG 5,9 200 10 L Leu Leucine 131,1746 6,01 2,33 9,74 - Apolaire, aliphatique UUA, UUG, CUU, CUC, CUA, CUG 9,1 260 11 M Met Méthionine 149,2078 5,74 2,13 9,28 - Apolaire AUG 2,3 88 12 N Asn Asparagine 132,1190 5,41 2,14 8,72 - Polaire AAU, AAC 4,3 140 13 O Pyl Pyrrolysine 255,3134 Polaire UAG associé à un élément PYLIS - - 14 P Pro Proline 115,1319 6,30 1,95 10,64 - Apolaire CCU, CCC, CCA, CCG 5,2 130 15 Q Gln Glutamine 146,1459 5,65 2,17 9,13 - Polaire CAA, CAG 4,2 150 16 R Arg Arginine 174,2027 10,76 1,82 8,99 12,48 Basique CGU, CGC, CGA, CGG, AGA, AGG 5,1 170 17 S Ser Sérine 105,0934 5,68 2,19 9,21 - Polaire UCU, UCC, UCA, UCG, AGU, AGC 6,8 120 18 T Thr Thréonine 119,1203 5,60 2,09 9,10 - Polaire ACU, ACC, ACA, ACG 5,9 150 19 U Sec Sélénocystéine 168,053 5,73 Polaire UGA associé à un élément SECIS - - 20 V Val Valine 117,1478 6,00 2,39 9,74 - Apolaire, aliphatique GUU, GUC, GUA, GUG 6,6 240 21 W Trp Tryptophane 204,2284 5,89 2,46 9,41 - Apolaire, aromatique UGG (et UGA chez les mycoplasmes) 1,4 33 22 Y Tyr Tyrosine 181,1912 5,64 2,20 9,21 10,46 Polaire, aromatique UAU, UAC 3,2 79Mais bien sûr un peu de "ménage" ne fait pas de mal :
library(stringr) # pour la fonctin str_replace() # aménagement du tableau tabaa2 cats("Table of standard amino acid abbreviations and properties (improved)") colnames(tabaa2) <- c( "Amino Acid", "3-Letter", "1-Letter", "Side-chain polarity", "Side-chain charge (pH 7.4)", "Hydropathy index", "Absorbance", "Eps at lambda max" ) # fin de c() vdc <- as.character(tabaa2$"Hydropathy index") # le symbole bizarre est notamment dans l'élément 2 comme premier caractère syb <- substr(vdc[2],1,1) # on le remplace par un "vrai" symbole - ("moins") tabaa2$"Hydropathy index" <- as.numeric(str_replace(vdc,paste("[",syb,"]",sep=""),"-")) print(tabaa2,right=FALSE) # aménagement du tableau tabaa3 cats("Principales données caractéristiques des 22 acides aminés (amélioré)") colnames(tabaa3) <- c( "Code", "Abrév.", "Acide aminé", "Masse molaire", "pI", "pKa", "pKb", "pKc", "Nature", "Codons ARNm", "Occurrence", "Abondance" ) # fin de c() print(tabaa3,right=FALSE) cat("Rem : Occurrence = % protéines humaines\n") cat(" Abondance = x 10**6 mol chez E. coli\n")Résultats d'exécution :
Table of standard amino acid abbreviations and properties (improved) ==================================================================== Amino Acid 3-Letter 1-Letter Side-chain polarity Side-chain charge (pH 7.4) Hydropathy index Absorbance Eps at lambda max 1 Alanine Ala A nonpolar neutral 1.8 2 Arginine Arg R Basic polar positive -4.5 3 Asparagine Asn N polar neutral -3.5 4 Aspartic acid Asp D acidic polar negative -3.5 5 Cysteine Cys C nonpolar neutral 2.5 250 0.3 6 Glutamic acid Glu E acidic polar negative -3.5 7 Glutamine Gln Q polar neutral -3.5 8 Glycine Gly G nonpolar neutral -0.4 9 Histidine His H Basic polar positive(10%)\nneutral(90%) -3.2 211 5.9 10 Isoleucine Ile I nonpolar neutral 4.5 11 Leucine Leu L nonpolar neutral 3.8 12 Lysine Lys K Basic polar positive -3.9 13 Methionine Met M nonpolar neutral 1.9 14 Phenylalanine Phe F nonpolar neutral 2.8 257, 206, 188 0.2, 9.3, 60.0 15 Proline Pro P nonpolar neutral -1.6 16 Serine Ser S polar neutral -0.8 17 Threonine Thr T polar neutral -0.7 18 Tryptophan Trp W nonpolar neutral -0.9 280, 219 5.6, 47.0 19 Tyrosine Tyr Y polar neutral -1.3 274, 222, 193 1.4, 8.0, 48.0 20 Valine Val V nonpolar neutral 4.2 Principales données caractéristiques des 22 acides aminés (amélioré) ==================================================================== Code Abrév. Acide aminé Masse molaire pI pKa pKb pKc Nature Codons ARNm Occurrence Abondance 1 A Ala Alanine 89,0940 6,01 2,35 9,87 - Apolaire, aliphatique GCU, GCC, GCA, GCG 7,8 290 2 C Cys Cystéine 121,1540 5,05 1,92 10,70 8,18 Polaire UGU, UGC 1,9 52 3 D Asp Acide aspartique 133,1038 2,85 1,99 9,90 3,90 Acide GAU, GAC 5,3 140 4 E Glu Acide glutamique 147,1307 3,15 2,10 9,47 4,07 Acide GAA, GAG 6,3 150 5 F Phe Phénylalanine 165,1918 5,49 2,20 9,31 - Apolaire, aromatique UUU, UUC 3,9 110 6 G Gly Glycine 75,0671 6,06 2,35 9,78 - Apolaire, aliphatique GGU, GGC, GGA, GGG 7,2 350 7 H His Histidine 155,1563 7,60 1,80 9,33 6,04 Basique, aromatique CAU, CAC 2,3 54 8 I Ile Isoleucine 131,1746 6,05 2,32 9,76 - Apolaire, aliphatique AUU, AUC, AUA 5,3 170 9 K Lys Lysine 146,1893 9,60 2,16 9,06 10,54 Basique AAA, AAG 5,9 200 10 L Leu Leucine 131,1746 6,01 2,33 9,74 - Apolaire, aliphatique UUA, UUG, CUU, CUC, CUA, CUG 9,1 260 11 M Met Méthionine 149,2078 5,74 2,13 9,28 - Apolaire AUG 2,3 88 12 N Asn Asparagine 132,1190 5,41 2,14 8,72 - Polaire AAU, AAC 4,3 140 13 O Pyl Pyrrolysine 255,3134 Polaire UAG associé à un élément PYLIS - - 14 P Pro Proline 115,1319 6,30 1,95 10,64 - Apolaire CCU, CCC, CCA, CCG 5,2 130 15 Q Gln Glutamine 146,1459 5,65 2,17 9,13 - Polaire CAA, CAG 4,2 150 16 R Arg Arginine 174,2027 10,76 1,82 8,99 12,48 Basique CGU, CGC, CGA, CGG, AGA, AGG 5,1 170 17 S Ser Sérine 105,0934 5,68 2,19 9,21 - Polaire UCU, UCC, UCA, UCG, AGU, AGC 6,8 120 18 T Thr Thréonine 119,1203 5,60 2,09 9,10 - Polaire ACU, ACC, ACA, ACG 5,9 150 19 U Sec Sélénocystéine 168,053 5,73 Polaire UGA associé à un élément SECIS - - 20 V Val Valine 117,1478 6,00 2,39 9,74 - Apolaire, aliphatique GUU, GUC, GUA, GUG 6,6 240 21 W Trp Tryptophane 204,2284 5,89 2,46 9,41 - Apolaire, aromatique UGG (et UGA chez les mycoplasmes) 1,4 33 22 Y Tyr Tyrosine 181,1912 5,64 2,20 9,21 10,46 Polaire, aromatique UAU, UAC 3,2 79 Rem : Occurrence = % protéines humaines Abondance = x 10**6 mol chez E. coli
6. Regroupement et fusion de données
On veut fusionner horizontalement (dans le sens des lignes) les fichiers datah1.txt et datah2.txt. Quel code R faut-il écrire ? Et si on veut fusionner verticalement (dans le sens des colonnes) les fichiers datav1.txt et datav2.txt ?
Plus généralement, comment fusionner des données selon une colonne commune, par exemple pour partie1.txt et partie2.txt ?
Solution : masquer la solution
La fusion horizontale de données de même structure est réalisée par rbind() alors que la fusion verticale de données avec les mêmes identifiants de ligne est réalisée par cbind(). On notera que cbind() produit une matrice, ce qui force souvent l'affichage en colonne, ce qui est bien pratique et que la fonction names() se comporte dans certains cas comme colnames().
# fusion horizontale h1 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/datah1.txt",head=TRUE) h2 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/datah2.txt",head=TRUE) h1et2 <- rbind(h1,h2) print( sapply(X=list(h1,h2,h1et2),FUN=dim) ) # fusion verticale v1 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/datav1.txt",head=TRUE) v2 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/datav2.txt",head=TRUE) v1et2 <- cbind(v1,v2) print( sapply(X=list(v1,v2,v1et2),FUN=dim) ) # print(cbind(names est très pratique : elf <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/elf2.txt",header=TRUE,row.names=1) print(head(elf)) print(cbind(names(elf))) e1 <- elf e2 <- names(elf) e3 <- cbind(names(elf)) print( cbind( sapply(X=list(e1,e2,e3),FUN=class) ) )> # fusion horizontale > > h1 <- read.table("datah1.txt",head=TRUE) > h2 <- read.table("datah2.txt",head=TRUE) > h1et2 <- rbind(h1,h2) > print( sapply(X=list(h1,h2,h1et2),FUN=dim) ) [,1] [,2] [,3] [1,] 20 79 99 [2,] 7 7 7 > # fusion verticale > > v1 <- read.table("datav1.txt",head=TRUE) > v2 <- read.table("datav2.txt",head=TRUE) > v1et2 <- cbind(v1,v2) > print( sapply(X=list(v1,v2,v1et2),FUN=dim) ) [,1] [,2] [,3] [1,] 99 99 99 [2,] 3 4 7 > # print(cbind(names est très pratique : > > elf <- read.table("elf2.txt",header=TRUE,row.names=1) > print(head(elf)) SEXE AGE PROF ETUD REGI USAG M001 Femme 62 1 2 2 3 M002 Homme 60 9 3 4 1 M003 Femme 31 9 4 4 1 M004 Femme 27 8 4 1 1 M005 Homme 22 8 4 1 2 M006 Femme 70 4 1 1 1 > print(cbind(names(elf))) [,1] [1,] "SEXE" [2,] "AGE" [3,] "PROF" [4,] "ETUD" [5,] "REGI" [6,] "USAG" > e1 <- elf > e2 <- names(elf) > e3 <- cbind(names(elf)) > print( cbind( sapply(X=list(e1,e2,e3),FUN=class) ) ) [,1] [1,] "data.frame" [2,] "character" [3,] "matrix"En cas d'erreur sur les structures, R affiche des messages pas toujours compréhensibles :
> rbind(h1,v1) Erreur dans rbind(deparse.level, ...) : les nombres de colonnes des arguments ne correspondent pas > cbind(h1,v1) Erreur dans data.frame(..., check.names = FALSE) : les arguments impliquent des nombres de lignes différents : 20, 99La fusion générale se fait sur clé d'appariement via merge(). Il y a bien sûr des options pour préciser cette clé, ce qu'il faut faire des données orphelines à droite et à gauche...
> (p1 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/partie1.txt",head=TRUE) ) CLEAP NOM PRENOM AGE 1 1 DUPONT Jean 30 2 2 HUGO Victorine 25 3 3 ZOLA Emile 30 > (p2 <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Introduction_R/partie2.txt",head=TRUE) ) CLEAP POIDS POINTS 1 1 56 300 2 3 50 600 3 5 58 150 > ( communs <- merge(p1,p2,by="CLEAP") ) CLEAP NOM PRENOM AGE POIDS POINTS 1 1 DUPONT Jean 30 56 300 2 3 ZOLA Emile 30 50 600 > ( toutp1 <- merge(p1,p2,by="CLEAP",all.x=TRUE) ) CLEAP NOM PRENOM AGE POIDS POINTS 1 1 DUPONT Jean 30 56 300 2 2 HUGO Victorine 25 NA NA 3 3 ZOLA Emile 30 50 600 > ( toutp2 <- merge(p1,p2,by="CLEAP",all.y=TRUE) ) CLEAP NOM PRENOM AGE POIDS POINTS 1 1 DUPONT Jean 30 56 300 2 3 ZOLA Emile 30 50 600 3 5 <NA> <NA> NA 58 150 > ( toustous <- merge(p1,p2,by="CLEAP",all=TRUE) ) CLEAP NOM PRENOM AGE POIDS POINTS 1 1 DUPONT Jean 30 56 300 2 2 HUGO Victorine 25 NA NA 3 3 ZOLA Emile 30 50 600 4 5 <NA> <NA> NA 58 150 > names(p2)[1] <- "ID" > ( communs <- merge(p1,p2,by.x="CLEAP",by.y="ID") ) CLEAP NOM PRENOM AGE POIDS POINTS 1 1 DUPONT Jean 30 56 300 2 3 ZOLA Emile 30 50 600 > ( communs <- merge(p1,p2,by.x=1,by.y=1) ) CLEAP NOM PRENOM AGE POIDS POINTS 1 1 DUPONT Jean 30 56 300 2 3 ZOLA Emile 30 50 600
7. Indiçage, filtrage et sélection de données
Comment obtenir la valeur du n-ième élément d'une structure ? le maximum ? que les valeurs positives ?
Solution : masquer la solution
L'indexation avec les crochets droits [ et ] permet de désigner le n-ième élément d'une structure. Un seul indice suffit pour les vecteurs alors qu'il en faut deux pour les matrices, séparés par une virgule. Pour les listes, on peut utiliser les crochets simples ou doubles. Dans le premier cas, la structure de liste est conservée.
# 1. un vecteur monVect <- c(12,1:20,8,-5,2) print(monVect) # premier élément print( monVect[1] ) # dernier élément print( monVect[ length(monVect) ] ) # avant-dernier élément print( monVect[ length(monVect) -1 ] ) # début des données (1) print( head( monVect ) ) # début des données (2) print( head( monVect,n=10 ) ) # début des données (3) print( head( monVect[1:10] ) ) # 2. une matrice maMat <- matrix( monVect, nrow=12, ncol=4 ) # fin de matrice print( maMat ) # attention print( maMat[1] ) print( maMat[23] ) # début des données print( head(maMat) ) # fin des données print( tail(maMat) ) # la ligne 1 print( maMat[1,] ) # la colonne 1 print( maMat[,1] ) # la colonne 1 sans perdre la structure de matrice print( maMat[,1,drop=FALSE] ) # l'élément en ligne 2, colonne 3 print( maMat[2,3] ) # l'élément en ligne 2, colonne 3 sans perdre la structure de matrice print( maMat[2,3,drop=FALSE] ) # 3. une liste maLst <- list("bonjour",monVect,list(1:5),maMat) print( maLst ) # l'élément 1, crochets doubles print( x1 <- maLst[[1]] ) # l'élément 1, crochets simples print( x2 <- maLst[1] ) # quelle différence ? print( sapply(X=list(x1,x2),FUN=class) )> # 1. un vecteur > > monVect <- c(12,1:20,8,-5,2) > print(monVect) [1] 12 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 8 -5 2 > # premier élément > > print( monVect[1] ) [1] 12 > # dernier élément > > print( monVect[ length(monVect) ] ) [1] 2 > # avant-dernier élément > > print( monVect[ length(monVect) -1 ] ) [1] -5 > # début des données (1) > > print( head( monVect ) ) [1] 12 1 2 3 4 5 > # début des données (2) > > print( head( monVect,n=10 ) ) [1] 12 1 2 3 4 5 6 7 8 9 > # début des données (3) > > print( head( monVect[1:10] ) ) [1] 12 1 2 3 4 5 > # 2. une matrice > > maMat <- matrix( + monVect, + nrow=12, ncol=4 + ) # fin de matrice > print( maMat ) [,1] [,2] [,3] [,4] [1,] 12 12 12 12 [2,] 1 13 1 13 [3,] 2 14 2 14 [4,] 3 15 3 15 [5,] 4 16 4 16 [6,] 5 17 5 17 [7,] 6 18 6 18 [8,] 7 19 7 19 [9,] 8 20 8 20 [10,] 9 8 9 8 [11,] 10 -5 10 -5 [12,] 11 2 11 2 > # attention > > print( maMat[1] ) [1] 12 > print( maMat[23] ) [1] -5 > # début des données > > print( head(maMat) ) [,1] [,2] [,3] [,4] [1,] 12 12 12 12 [2,] 1 13 1 13 [3,] 2 14 2 14 [4,] 3 15 3 15 [5,] 4 16 4 16 [6,] 5 17 5 17 > # fin des données > > print( tail(maMat) ) [,1] [,2] [,3] [,4] [7,] 6 18 6 18 [8,] 7 19 7 19 [9,] 8 20 8 20 [10,] 9 8 9 8 [11,] 10 -5 10 -5 [12,] 11 2 11 2 > # la ligne 1 > > print( maMat[1,] ) [1] 12 12 12 12 > # la colonne 1 > > print( maMat[,1] ) [1] 12 1 2 3 4 5 6 7 8 9 10 11 > # la colonne 1 sans perdre la structure de matrice > > print( maMat[,1,drop=FALSE] ) [,1] [1,] 12 [2,] 1 [3,] 2 [4,] 3 [5,] 4 [6,] 5 [7,] 6 [8,] 7 [9,] 8 [10,] 9 [11,] 10 [12,] 11 > # l'élément en ligne 2, colonne 3 > > print( maMat[2,3] ) [1] 1 > # l'élément en ligne 2, colonne 3 sans perdre la structure de matrice > > print( maMat[2,3,drop=FALSE] ) [,1] [1,] 1 > # 3. une liste > > maLst <- list("bonjour",monVect,list(1:5),maMat) > print( maLst ) [[1]] [1] "bonjour" [[2]] [1] 12 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 8 -5 2 [[3]] [[3]][[1]] [1] 1 2 3 4 5 [[4]] [,1] [,2] [,3] [,4] [1,] 12 12 12 12 [2,] 1 13 1 13 [3,] 2 14 2 14 [4,] 3 15 3 15 [5,] 4 16 4 16 [6,] 5 17 5 17 [7,] 6 18 6 18 [8,] 7 19 7 19 [9,] 8 20 8 20 [10,] 9 8 9 8 [11,] 10 -5 10 -5 [12,] 11 2 11 2 > # l'élément 1, crochets doubles > > print( x1 <- maLst[[1]] ) [1] "bonjour" > # l'élément 1, crochets simples > > print( x2 <- maLst[1] ) [[1]] [1] "bonjour" > # quelle différence ? > > print( sapply(X=list(x1,x2),FUN=class) ) [1] "character" "list"Pour trouver le maximum d'une structure, on utilise en général la fonction max(), mais qu'on applique différemment suivant les structures. Pour un vecteur et une matrice, max() renvoie le maximum de tous les éléments rencontrés. Si on veut les maxima en ligne ou en colonne, il faut utiliser apply(max... en indiquant la marge (MARGIN) : 1 pour les lignes et 2 pour les colonnes. Pour une liste qui contient seulement des vecteurs ou des matrices, lapply(max... et sapply(max... sont des solutions acceptables. S'il y a des listes dans la liste, il faut forcer un appel récursif avec rapply(max....
La position du maximum s'obtient avec which.max() mais ne renvoie qu'une seule position. Un test direct avec ==max(... permet de trouver toutes les positions du maximum, si on applique son résultat aux indices de la structure. Tout test logique sur une structure permet de filtrer une structure. Voir ci-dessous. Au passage, on notera que which.max() est une forme particulière de which().
# les objets ( monVect <- c(12,1:20,8,-5,2) ) ( maMat <- matrix( monVect, nrow=12, ncol=4 ) ) # fin de matrice ( maLst <- list("bonjour",monVect,list(1:5),maMat) ) ( vct <- c(5,8,6,2,4,17,8,1,2,8,4) ) ( autrev <- c(5,-8,6,-2,4,-17,-8,-1,2,8,4) ) # les calculs max(monVect) max(maMat) apply(X=maMat,FUN=max,MARGIN=1) apply(X=maMat,FUN=max,MARGIN=2) #lapply(X=maLst,FUN=max) #Erreur dans FUN(X[[3L]], ...) : 'type' (list) de l'argument incorrect lapply(X=maLst[-3],FUN=max) sapply(X=maLst[-3],FUN=max) rapply(object=maLst,f=max) which.max(vct) vct==max(vct) vct[vct==max(vct)] (1:length(vct))[vct==max(vct)] which(autrev<0) length( which(autrev<0) ) autrev[ which(autrev<0) ] # comme R est vectoriel, on peut se contenter de : autrev[ autrev<0 ] # un peu de rédaction ne nuit pas # avec un filtre direct flt <- (autrev<0) # flt : filtre nbn <- sum( flt ) lesneg <- autrev[ flt ] cat(" il y a ",nbn," valeurs négatives, à savoir ",paste(lesneg,collapse=" "),"\n")> # les objets > > ( monVect <- c(12,1:20,8,-5,2) ) [1] 12 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 8 -5 2 > ( maMat <- matrix( + monVect, + nrow=12, ncol=4 + ) ) # fin de matrice [,1] [,2] [,3] [,4] [1,] 12 12 12 12 [2,] 1 13 1 13 [3,] 2 14 2 14 [4,] 3 15 3 15 [5,] 4 16 4 16 [6,] 5 17 5 17 [7,] 6 18 6 18 [8,] 7 19 7 19 [9,] 8 20 8 20 [10,] 9 8 9 8 [11,] 10 -5 10 -5 [12,] 11 2 11 2 > ( maLst <- list("bonjour",monVect,list(1:5),maMat) ) [[1]] [1] "bonjour" [[2]] [1] 12 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 8 -5 2 [[3]] [[3]][[1]] [1] 1 2 3 4 5 [[4]] [,1] [,2] [,3] [,4] [1,] 12 12 12 12 [2,] 1 13 1 13 [3,] 2 14 2 14 [4,] 3 15 3 15 [5,] 4 16 4 16 [6,] 5 17 5 17 [7,] 6 18 6 18 [8,] 7 19 7 19 [9,] 8 20 8 20 [10,] 9 8 9 8 [11,] 10 -5 10 -5 [12,] 11 2 11 2 > ( vct <- c(5,8,6,2,4,17,8,1,2,8,4) ) [1] 5 8 6 2 4 17 8 1 2 8 4 > ( autrev <- c(5,-8,6,-2,4,-17,-8,-1,2,8,4) ) [1] 5 -8 6 -2 4 -17 -8 -1 2 8 4 > # les calculs > > max(monVect) [1] 20 > max(maMat) [1] 20 > apply(X=maMat,FUN=max,MARGIN=1) [1] 12 13 14 15 16 17 18 19 20 9 10 11 > apply(X=maMat,FUN=max,MARGIN=2) [1] 12 20 12 20 > #lapply(X=maLst,FUN=max) > #Erreur dans FUN(X[[3L]], ...) : 'type' (list) de l'argument incorrect > > lapply(X=maLst[-3],FUN=max) [[1]] [1] "bonjour" [[2]] [1] 20 [[3]] [1] 20 > sapply(X=maLst[-3],FUN=max) [1] "bonjour" "20" "20" > rapply(object=maLst,f=max) [1] "bonjour" "20" "5" "20" > which.max(vct) [1] 6 > vct==max(vct) [1] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE > vct[vct==max(vct)] [1] 17 > (1:length(vct))[vct==max(vct)] [1] 6 > which(autrev<0) [1] 2 4 6 7 8 > length( which(autrev<0) ) [1] 5 > autrev[ which(autrev<0) ] [1] -8 -2 -17 -8 -1 > # comme R est vectoriel, on peut se contenter de : > autrev[ autrev<0 ] [1] -8 -2 -17 -8 -1 > # un peu de rédaction ne nuit pas > # avec un filtre direct > > flt <- (autrev<0) # flt : filtre > nbn <- sum( flt ) > lesneg <- autrev[ flt ] > cat(" il y a ",nbn," valeurs négatives, à savoir ",paste(lesneg,collapse=" "),"\n") il y a 5 valeurs négatives, à savoir -8 -2 -17 -8 -1
8. Conversion de structures de données en R
Quelles sont les différentes structures de données en R et comment passe-t-on des unes aux autres ?
Solution : masquer la solution
Les principales structures de données élémentaires sont les listes, les vecteurs, les matrices et les "data frames", construites directement par list(), vector(), matrix() et data.frame() ou indirectement par des fonctions comme seq(), rep(), range(), which()...
Les passerelles sont construites avec "as". comme as.vector(), as.matrix(), as.list(), as.data.frame()... mais aussi avec unlist().
On connait souvent le type d'une variable à l'aide de la fonction class(), même si certains objets peuvent hériter de plusieurs classes.
monVect <- c(12,1:20,8,-5,2) maMat <- matrix( monVect, nrow=12, ncol=4 ) # fin de matrice unDf <- as.data.frame( maMat ) uneLst <- list(a="oui",b=1:5,v=monVect,m=maMat,c=list(1,"non",3:5)) class(monVect) class(maMat) class(unDf) class(uneLst) sapply(X=uneLst,FUN=class) str(uneLst) data(DNase) help(DNase) # data obtained during development of an ELISA assay for the recombinant protein DNase in rat serum. class(DNase) library(cluster) hc <- hclust(dist(USArrests), "ave") par(ask=FALSE) plot(hc) class(hc)> monVect <- c(12,1:20,8,-5,2) > maMat <- matrix( + monVect, + nrow=12, ncol=4 + ) # fin de matrice > unDf <- as.data.frame( maMat ) > uneLst <- list(a="oui",b=1:5,v=monVect,m=maMat,c=list(1,"non",3:5)) > class(monVect) [1] "numeric" > class(maMat) [1] "matrix" > class(unDf) [1] "data.frame" > class(uneLst) [1] "list" > sapply(X=uneLst,FUN=class) a b v m c "character" "integer" "numeric" "matrix" "list" > str(uneLst) List of 5 $ a: chr "oui" $ b: int [1:5] 1 2 3 4 5 $ v: num [1:24] 12 1 2 3 4 5 6 7 8 9 ... $ m: num [1:12, 1:4] 12 1 2 3 4 5 6 7 8 9 ... $ c:List of 3 ..$ : num 1 ..$ : chr "non" ..$ : int [1:3] 3 4 5 > data(DNase) > help(DNase) > # data obtained during development of an ELISA assay for the recombinant protein DNase in rat serum. > > class(DNase) [1] "nfnGroupedData" "nfGroupedData" "groupedData" "data.frame" > library(cluster) > hc <- hclust(dist(USArrests), "ave") > par(ask=FALSE) > plot(hc) > class(hc) [1] "hclust"De façon plus fine, les data frames sont des listes. Les matrices peuvent avoir des rownames et des colnames() (sans point) alors que les listes peuvent avoir des names() et que les data frames peuvent avoir des row.names() (avec un point), des names().
m1 <- matrix(15+(1:12),nrow=4,ncol=3) print(m1) colnames(m1) <- c("Janvier","Mars","Juillet") rownames(m1) <- paste("L",1:nrow(m1),sep="_") print(m1) print(m1[,"Janvier"]) # équivalent à print(m1[,1]) # incorrect : print(m1$Janvier) d2 <- as.data.frame(m1) print(d2) names(d2) <- paste("Col",sprintf("%02d",1:ncol(d2)),sep="") # hum ! row.names(d2) <- paste("Lig",sprintf("%02d",1:nrow(d2)),sep="_") # re-hum ! print(d2) print(d2[,"Col01"]) # équivalent à print(d2[,1]) print(d2$Col01) # équivalent à print(d2[,1]) attach(d2) print(Col01) # équivalent à print(d2$Col01) detach(d2) # print(Col01) est désormais incorrect with(d2,print(Col01)) # un "attach local" # pour les listes uneLst <- list(a="oui",b=1:5,v=monVect,m=maMat,c=list(1,"non",3:5)) print( names(uneLst) ) names(uneLst)[1] <- "réponse" print( names(uneLst) )> m1 <- matrix(15+(1:12),nrow=4,ncol=3) > print(m1) [,1] [,2] [,3] [1,] 16 20 24 [2,] 17 21 25 [3,] 18 22 26 [4,] 19 23 27 > colnames(m1) <- c("Janvier","Mars","Juillet") > rownames(m1) <- paste("L",1:nrow(m1),sep="_") > print(m1) Janvier Mars Juillet L_1 16 20 24 L_2 17 21 25 L_3 18 22 26 L_4 19 23 27 > print(m1[,"Janvier"]) # équivalent à print(m1[,1]) L_1 L_2 L_3 L_4 16 17 18 19 > # incorrect : print(m1$Janvier) > > d2 <- as.data.frame(m1) > print(d2) Janvier Mars Juillet L_1 16 20 24 L_2 17 21 25 L_3 18 22 26 L_4 19 23 27 > names(d2) <- paste("Col",sprintf("%02d",1:ncol(d2)),sep="") # hum ! > row.names(d2) <- paste("Lig",sprintf("%02d",1:nrow(d2)),sep="_") # re-hum ! > print(d2) Col01 Col02 Col03 Lig_01 16 20 24 Lig_02 17 21 25 Lig_03 18 22 26 Lig_04 19 23 27 > print(d2[,"Col01"]) # équivalent à print(d2[,1]) [1] 16 17 18 19 > print(d2$Col01) # équivalent à print(d2[,1]) [1] 16 17 18 19 > attach(d2) > print(Col01) # équivalent à print(d2$Col01) [1] 16 17 18 19 > detach(d2) > # print(Col01) est désormais incorrect > > with(d2,print(Col01)) # un "attach local" [1] 16 17 18 19 > # pour les listes > > uneLst <- list(a="oui",b=1:5,v=monVect,m=maMat,c=list(1,"non",3:5)) > print( names(uneLst) ) [1] "a" "b" "v" "m" "c" > names(uneLst)[1] <- "réponse" > print( names(uneLst) ) [1] "réponse" "b" "v" "m" "c"Rappel : il y a coercion de type pour les vecteurs et les matrices, c'est-à-dire que R contraint les éléments à être de même type.
( v1 <- c(3,5,8) ) ( v2 <- c(3,"5",8) ) m1 <- matrix(1:12,nrow=4,ncol=3) print(m1) m1[3,2] <- "?" print(m1) m2 <- as.data.frame( matrix(1:12,nrow=4,ncol=3) ) print(m2) m2[3,2] <- "?" print(m2)> ( v1 <- c(3,5,8) ) [1] 3 5 8 > ( v2 <- c(3,"5",8) ) [1] "3" "5" "8" > m1 <- matrix(1:12,nrow=4,ncol=3) > print(m1) [,1] [,2] [,3] [1,] 1 5 9 [2,] 2 6 10 [3,] 3 7 11 [4,] 4 8 12 > m1[3,2] <- "?" > print(m1) [,1] [,2] [,3] [1,] "1" "5" "9" [2,] "2" "6" "10" [3,] "3" "?" "11" [4,] "4" "8" "12" > m2 <- as.data.frame( matrix(1:12,nrow=4,ncol=3) ) > print(m2) V1 V2 V3 1 1 5 9 2 2 6 10 3 3 7 11 4 4 8 12 > m2[3,2] <- "?" > print(m2) V1 V2 V3 1 1 5 9 2 2 6 10 3 3 ? 11 4 4 8 12
9. Gestion des données manquantes
Il arrive quand on importe des données, qu'il ait des "trous", ce qui aboutit à des données manquantes. Comment fait-on pour les gérer en R ?
Y a-t-il un impact sur les calculs ?
Solution : masquer la solution
Oui, malheureusement les valeurs NA (not available en anglais) ont un fort impact sur les calculs. Les NA empêchent de calculer les sommes, donc les moyennes, les variances... De plus un test logique avec NA renvoie NA, ce n'est pas très pratique ! Pour les chaines de caractères, les données manquantes sont notées <NA>.
# un vecteur avec des données manquantes v <- c(3,8,10,NA,5,2,7,NA,6) # les NA interdisent de calculer la somme cat(" somme (1) des élément de v : ", sum(v) ,"\n") # solution 1 cat(" somme (2) des élément de v : ", sum(v,na.rm=TRUE) ,"\n") # solution 2 cat(" somme (3) des élément de v : ", sum(na.omit(v)) ,"\n")> # un vecteur avec des données manquantes > > v <- c(3,8,10,NA,5,2,7,NA,6) > # les NA interdisent de calculer la somme > > cat(" somme (1) des élément de v : ", sum(v) ,"\n") somme (1) des élément de v : NA > # solution 1 > > cat(" somme (2) des élément de v : ", sum(v,na.rm=TRUE) ,"\n") somme (2) des élément de v : 41 > # solution 2 > > cat(" somme (3) des élément de v : ", sum(na.omit(v)) ,"\n") somme (3) des élément de v : 41Face à plusieurs NA dans une matrice ou un data frame, il y a plusieurs stratégies. Soit on supprime toutes les lignes avec des NA à l'aide de na.omit() et on peut alors utiliser toutes les fonctions directement, y compris colSums() et colMeans(), soit on utilise les fonctions avec le paramètre na.rm=TRUE. La première stratégie élimine parfois trop de données. Voir un exemple détaillé avec la question 1 de notre cours EDA et sa solution détaillée avec la discussion sur les NA.
10. Export de données en HTML et LaTeX
Comment faire pour exporter un data frame dans un fichier-texte ? Et dans un fichier Excel (Open Office Calc) ? Et au format LaTeX ?
Et dans une page Web ? Et au format Word (Open Office Write) ?
Et si ce n'est pas un data frame mais juste une liste de calculs, ou un vecteur ?
Solution : masquer la solution
De la même façon qu'on utilise read.table() et consor, soit read.csv() et read.csv2() en entrée, on dispose de write.table() en sortie, avec ses avatars write.csv() et write.csv2().
L'utilisation du package R2HTML est d'une grande simplicité pour générer du code HTML (le fichier produit est ici :
library(R2HTML) HTMLStart(filename="dynamic",echo=TRUE) cat("voici le data frame \"trees\" \n") ; print(trees) webadr <- HTMLStop() browseURL(url=webadr,browser="firefox")De plus ce package R2HTML contient peu de fonctions :
Objets du package R2HTML ======================== il y a 29 objets dans ce package name functionClass other obj_001 as.latex function obj_002 as.title function obj_003 HTML function obj_004 HTML2clip function obj_005 HTMLbr function obj_006 HTMLChangeCSS function obj_007 HTML.cormat function obj_008 HTMLCSS function obj_009 HTMLEndFile function obj_010 HTMLgrid function obj_011 HTMLgrid_inline function obj_012 HTMLgrid_references function obj_013 HTMLgrid_summary function obj_014 HTMLhr function obj_015 HTMLInitFile function obj_016 HTMLInsertGraph function obj_017 HTMLli function obj_018 HTMLplot function obj_019 HTMLStart function obj_020 HTMLstem function obj_021 HTMLStop function obj_022 HTML.title function obj_023 RweaveHTML function obj_024 RweaveHTMLFinish function obj_025 RweaveHTMLOptions function obj_026 RweaveHTMLRuncode function obj_027 RweaveHTMLSetup function obj_028 RweaveHTMLWritedoc function obj_029 SweaveSyntaxHTML SweaveSyntaxIl faut juste regretter que le code produit ne soit pas XHTML valide, que ce soit pour la grammaire Strict ou Trans, mais cela fournit déjà une base sérieuse de code HTML. Dans la mesure où Microsoft Word et Open Office Write savent lire les fichiers HTML pour en faire des documents de traitement de texte, produire du HTML est suffisant pour produire du Word, dans un premier temps.
Un autre package qui permet de créer du HTML est hwriter et ses fonctions openPage(), hwrite() et closePage. Le code est produit est XHTML valide pour la grammaire Trans :
library(hwriter) fp <- "demoHwriter.html" p <- openPage(fp) ; hwrite(trees,p) closePage(p) # et voilà : browseURL(url=fp,browser="firefox")Le fichier produit est demoHwriter.html. On peut s'affranchir du texte de création en base de page avec le paramètre splash dans closePage. Pour aller encore plus loin avec les exports, le package hwriterPlus est une extension intéressante de hwriter.
Pour produire du code LaTeX, on peut utiliser le package xtable et sa fonction xtable() ou le package Hmisc et sa fonction latex() qui a l'avantage de créer un fichier de même nom que la structure, soit ici trees.tex (copie locale en .txt ici).
data(trees) library(xtable) print( xtable(trees) ) library(Hmisc) latex(trees) # crée le fichier trees.tex et visualise sa compilation en dvi> data(trees) > library(xtable) > print( xtable(trees) ) % latex table generated in R 3.0.2 by xtable 1.7-1 package % Tue Oct 8 12:47:09 2013 \begin{table}[ht] \centering \begin{tabular}{rrrr} \hline & Girth & Height & Volume \\ \hline 1 & 8.30 & 70.00 & 10.30 \\ 2 & 8.60 & 65.00 & 10.30 \\ 3 & 8.80 & 63.00 & 10.20 \\ 4 & 10.50 & 72.00 & 16.40 \\ 5 & 10.70 & 81.00 & 18.80 \\ 6 & 10.80 & 83.00 & 19.70 \\ 7 & 11.00 & 66.00 & 15.60 \\ 8 & 11.00 & 75.00 & 18.20 \\ 9 & 11.10 & 80.00 & 22.60 \\ 10 & 11.20 & 75.00 & 19.90 \\ 11 & 11.30 & 79.00 & 24.20 \\ 12 & 11.40 & 76.00 & 21.00 \\ 13 & 11.40 & 76.00 & 21.40 \\ 14 & 11.70 & 69.00 & 21.30 \\ 15 & 12.00 & 75.00 & 19.10 \\ 16 & 12.90 & 74.00 & 22.20 \\ 17 & 12.90 & 85.00 & 33.80 \\ 18 & 13.30 & 86.00 & 27.40 \\ 19 & 13.70 & 71.00 & 25.70 \\ 20 & 13.80 & 64.00 & 24.90 \\ 21 & 14.00 & 78.00 & 34.50 \\ 22 & 14.20 & 80.00 & 31.70 \\ 23 & 14.50 & 74.00 & 36.30 \\ 24 & 16.00 & 72.00 & 38.30 \\ 25 & 16.30 & 77.00 & 42.60 \\ 26 & 17.30 & 81.00 & 55.40 \\ 27 & 17.50 & 82.00 & 55.70 \\ 28 & 17.90 & 80.00 & 58.30 \\ 29 & 18.00 & 80.00 & 51.50 \\ 30 & 18.00 & 80.00 & 51.00 \\ 31 & 20.60 & 87.00 & 77.00 \\ \hline \end{tabular} \end{table} > library(Hmisc) > latex(trees) # crée le fichier trees.tex et visualise sa compilation en dvi This is pdfTeX, Version 3.1415926-1.40.10 (TeX Live 2009/Debian) entering extended mode (/tmp/Rtmp9SSnYS/filec5d57fce5a0.tex LaTeX2e <2009/09/24> Babel <v3.8l> and hyphenation patterns for english, usenglishmax, dumylang, noh yphenation, loaded. (/usr/share/texmf-texlive/tex/latex/base/report.cls Document Class: report 2007/10/19 v1.4h Standard LaTeX document class (/usr/share/texmf-texlive/tex/latex/base/size10.clo)) (/usr/share/texmf-texlive/tex/latex/geometry/geometry.sty (/usr/share/texmf-texlive/tex/latex/graphics/keyval.sty) (/usr/share/texmf-texlive/tex/generic/oberdiek/ifpdf.sty) (/usr/share/texmf-texlive/tex/generic/oberdiek/ifvtex.sty)) No file filec5d57fce5a0.aux. *geometry auto-detecting driver* *geometry detected driver: dvips* [1] (./filec5d57fce5a0.aux) ) Output written on filec5d57fce5a0.dvi (1 page, 1752 bytes). Transcript written on filec5d57fce5a0.log.
11. R et SQL
Peut-on et faut-il interfacer R et SQL ?
Solution : masquer la solution
Oui et non. Oui, car on peut interfacer R et SQL, mais non parce qu'à notre avis au moins dans un premier temps il vaut mieux séparer les activités. D'autant que la connectivité via RODBC ou DBI, par exemple, demande des installations techniques... Dans la mesure où R sait bien lire les fichiers-texte, le mieux est d'exporter les données SQL qui sont presque toujours, obtenues par la commande SELECT donc qui correspond à un "beau" tableau rectangulaire lisible par read.table() du package utils.
12. Non présentation (!) des packages base, gdata, tools, foreign et utils
Que contiennent les packages base, gdata, tools et utils ?
Quelles en sont les fonctions les plus importantes ? Quels jeux de données sont fournis par ces packages ?
Solution : masquer la solution
La fonction ls() liste les objets de l'environnement courant, mais elle peut aussi indiquer les objets dans un package, lorsqu'il est chargé. Le résultat pour le package base est sans appel : avec 1167 objets dont 1162 fonctions, il est illusoire de penser connaitre ce package en quelques minutes. Les autres packages cités sont relativement moins importants.
# nombre d'objets dans les packages cat(" il y a ",length(ls("package:base"))," objets dans le package base\n") # il faut charger les packages avant de pouvoir utiliser ls sur ces packages # sauf pour base et utils qui sont chargés automatiquement library(gdata) library(tools) opg <- length(ls("package:gdata")) opt <- length(ls("package:tools")) opu <- length(ls("package:utils")) # on peut aussi nommer les composantes des vecteurs cats("pour les autres packages :") nbo <- c(opg,opt,opu) names(nbo) <- c("gdata","tools","utils") print(nbo)il y a 1167 objets dans le package base pour les autres packages : ========================== gdata tools utils 64 97 198On trouvera dans le tableau suivant des liens vers la liste des objets de ces packages.
Package Nb_objets Lien base 1167 lls_base.sor gdata 64 lls_gdata.sor tools 97 lls_tools.sor utils 198 lls_utils.sor Dans le package utils, les principales fonctions à connaitre sont certainement les suivantes, regroupées par thème :
Thème Fonctions aide apropos() browseVignettes() help() help.search() help.start() RSiteSearch() vignette() packages install.packages() installed.packages() update.packages() web browseURL() URLencode() URLdecode() url.show() affichage head() ls.str() prompt() str() tail(), history() fichiers read.csv() read.csv2() read.table() write.csv() write.csv2() write.table() Dans le package gdata, il faut certainement connaitre les fonctions combine(), keep(), ll(), nobs(), rename.vars(), trim() et bien sûr read.xls().
Enfin, dans le package tools, il faut certainement connaitre les fonctions file_ext(), HTMLheader(), Rd2HTML(), showNonASCII(), toHTML().
Il n'y a aucun jeu de données dans les packages tools et utils. Il n'y a plus aucun jeu de données dans le package base parce qu'ils ont tous été transférés dans le package datasets. Voici les jeux de données présents dans ce package (version juillet 2013) :
Jeux de données du package base =============================== name class length nrow ncol name2 ---- ----- ------ ---- ---- ----- dataset 001 AirPassengers ts 144 dataset 002 BJsales ts 150 dataset 003 BJsales.lead ts 150 BJsales dataset 004 BOD data.frame 6 2 dataset 005 CO2 nfnGroupedData nfGroupedData groupedData data.frame 5 84 5 dataset 006 ChickWeight nfnGroupedData nfGroupedData groupedData data.frame 4 578 4 dataset 007 DNase nfnGroupedData nfGroupedData groupedData data.frame 3 176 3 dataset 008 EuStockMarkets mts ts matrix 7440 1860 4 dataset 009 Formaldehyde data.frame 6 2 dataset 010 HairEyeColor table 32 4 4 dataset 011 Harman23.cor list 3 dataset 012 Harman74.cor list 3 dataset 013 Indometh nfnGroupedData nfGroupedData groupedData data.frame 3 66 3 dataset 014 InsectSprays data.frame 72 2 dataset 015 JohnsonJohnson ts 84 dataset 016 LakeHuron ts 98 dataset 017 LifeCycleSavings data.frame 50 5 dataset 018 Loblolly nfnGroupedData nfGroupedData groupedData data.frame 3 84 3 dataset 019 Nile ts 100 dataset 020 Orange nfnGroupedData nfGroupedData groupedData data.frame 3 35 3 dataset 021 OrchardSprays data.frame 64 4 dataset 022 PlantGrowth data.frame 30 2 dataset 023 Puromycin data.frame 23 3 dataset 024 Seatbelts mts ts 1536 192 8 dataset 025 Theoph nfnGroupedData nfGroupedData groupedData data.frame 5 132 5 dataset 026 Titanic table 32 4 2 dataset 027 ToothGrowth data.frame 60 3 dataset 028 UCBAdmissions table 24 2 2 dataset 029 UKDriverDeaths ts 192 dataset 030 UKgas ts 108 dataset 031 USAccDeaths ts 72 dataset 032 USArrests data.frame 50 4 dataset 033 USJudgeRatings data.frame 43 12 dataset 034 USPersonalExpenditure matrix 5 5 dataset 035 VADeaths matrix 5 4 dataset 036 WWWusage ts 100 dataset 037 WorldPhones matrix 7 7 dataset 038 ability.cov list 3 dataset 039 airmiles ts 24 dataset 040 airquality data.frame 153 6 dataset 041 anscombe data.frame 11 8 dataset 042 attenu data.frame 182 5 dataset 043 attitude data.frame 30 7 dataset 044 austres ts 89 dataset 045 beaver1 data.frame 114 4 beavers dataset 046 beaver2 data.frame 100 4 beavers dataset 047 cars data.frame 50 2 dataset 048 chickwts data.frame 71 2 dataset 049 co2 ts 468 dataset 050 crimtab table 924 42 22 dataset 051 discoveries ts 100 dataset 052 esoph data.frame 88 5 dataset 053 euro numeric 11 dataset 054 euro.cross matrix 11 11 euro dataset 055 eurodist dist 210 dataset 056 faithful data.frame 272 2 dataset 057 fdeaths ts 72 UKLungDeaths dataset 058 freeny data.frame 39 5 dataset 059 freeny.x matrix 39 4 freeny dataset 060 freeny.y ts 39 freeny dataset 061 infert data.frame 248 8 dataset 062 iris data.frame 150 5 dataset 063 iris3 array 600 50 4 dataset 064 islands numeric 48 dataset 065 ldeaths ts 72 UKLungDeaths dataset 066 lh ts 48 dataset 067 longley data.frame 16 7 dataset 068 lynx ts 114 dataset 069 mdeaths ts 72 UKLungDeaths dataset 070 morley data.frame 100 3 dataset 071 mtcars data.frame 32 11 dataset 072 nhtemp ts 60 dataset 073 nottem ts 240 dataset 074 npk data.frame 24 5 dataset 075 occupationalStatus table 64 8 8 dataset 076 precip numeric 70 dataset 077 presidents ts 120 dataset 078 pressure data.frame 19 2 dataset 079 quakes data.frame 1000 5 dataset 080 randu data.frame 400 3 dataset 081 rivers numeric 141 dataset 082 rock data.frame 48 4 dataset 083 sleep data.frame 20 3 dataset 084 stack.loss numeric 21 stackloss dataset 085 stack.x matrix 21 3 stackloss dataset 086 stackloss data.frame 21 4 dataset 087 state.abb character 50 state dataset 088 state.area numeric 50 state dataset 089 state.center list 2 state dataset 090 state.division factor 50 state dataset 091 state.name character 50 state dataset 092 state.region factor 50 state dataset 093 state.x77 matrix 50 8 state dataset 094 sunspot.month ts 2988 dataset 095 sunspot.year ts 289 dataset 096 sunspots ts 2820 dataset 097 swiss data.frame 47 6 dataset 098 treering ts 7980 dataset 099 trees data.frame 31 3 dataset 100 uspop ts 19 dataset 101 volcano matrix 87 61 dataset 102 warpbreaks data.frame 54 3 dataset 103 women data.frame 15 2 dataset 104 AirPassengers ts 144 dataset 105 BJsales ts 150 dataset 106 BJsales.lead ts 150 BJsales dataset 107 BOD data.frame 6 2 dataset 108 CO2 nfnGroupedData nfGroupedData groupedData data.frame 5 84 5 dataset 109 ChickWeight nfnGroupedData nfGroupedData groupedData data.frame 4 578 4 dataset 110 DNase nfnGroupedData nfGroupedData groupedData data.frame 3 176 3 dataset 111 EuStockMarkets mts ts matrix 7440 1860 4 dataset 112 Formaldehyde data.frame 6 2 dataset 113 HairEyeColor table 32 4 4 dataset 114 Harman23.cor list 3 dataset 115 Harman74.cor list 3 dataset 116 Indometh nfnGroupedData nfGroupedData groupedData data.frame 3 66 3 dataset 117 InsectSprays data.frame 72 2 dataset 118 JohnsonJohnson ts 84 dataset 119 LakeHuron ts 98 dataset 120 LifeCycleSavings data.frame 50 5 dataset 121 Loblolly nfnGroupedData nfGroupedData groupedData data.frame 3 84 3 dataset 122 Nile ts 100 dataset 123 Orange nfnGroupedData nfGroupedData groupedData data.frame 3 35 3 dataset 124 OrchardSprays data.frame 64 4 dataset 125 PlantGrowth data.frame 30 2 dataset 126 Puromycin data.frame 23 3 dataset 127 Seatbelts mts ts 1536 192 8 dataset 128 Theoph nfnGroupedData nfGroupedData groupedData data.frame 5 132 5 dataset 129 Titanic table 32 4 2 dataset 130 ToothGrowth data.frame 60 3 dataset 131 UCBAdmissions table 24 2 2 dataset 132 UKDriverDeaths ts 192 dataset 133 UKgas ts 108 dataset 134 USAccDeaths ts 72 dataset 135 USArrests data.frame 50 4 dataset 136 USJudgeRatings data.frame 43 12 dataset 137 USPersonalExpenditure matrix 5 5 dataset 138 VADeaths matrix 5 4 dataset 139 WWWusage ts 100 dataset 140 WorldPhones matrix 7 7 dataset 141 ability.cov list 3 dataset 142 airmiles ts 24 dataset 143 airquality data.frame 153 6 dataset 144 anscombe data.frame 11 8 dataset 145 attenu data.frame 182 5 dataset 146 attitude data.frame 30 7 dataset 147 austres ts 89 dataset 148 beaver1 data.frame 114 4 beavers dataset 149 beaver2 data.frame 100 4 beavers dataset 150 cars data.frame 50 2 dataset 151 chickwts data.frame 71 2 dataset 152 co2 ts 468 dataset 153 crimtab table 924 42 22 dataset 154 discoveries ts 100 dataset 155 esoph data.frame 88 5 dataset 156 euro numeric 11 dataset 157 euro.cross matrix 11 11 euro dataset 158 eurodist dist 210 dataset 159 faithful data.frame 272 2 dataset 160 fdeaths ts 72 UKLungDeaths dataset 161 freeny data.frame 39 5 dataset 162 freeny.x matrix 39 4 freeny dataset 163 freeny.y ts 39 freeny dataset 164 infert data.frame 248 8 dataset 165 iris data.frame 150 5 dataset 166 iris3 array 600 50 4 dataset 167 islands numeric 48 dataset 168 ldeaths ts 72 UKLungDeaths dataset 169 lh ts 48 dataset 170 longley data.frame 16 7 dataset 171 lynx ts 114 dataset 172 mdeaths ts 72 UKLungDeaths dataset 173 morley data.frame 100 3 dataset 174 mtcars data.frame 32 11 dataset 175 nhtemp ts 60 dataset 176 nottem ts 240 dataset 177 npk data.frame 24 5 dataset 178 occupationalStatus table 64 8 8 dataset 179 precip numeric 70 dataset 180 presidents ts 120 dataset 181 pressure data.frame 19 2 dataset 182 quakes data.frame 1000 5 dataset 183 randu data.frame 400 3 dataset 184 rivers numeric 141 dataset 185 rock data.frame 48 4 dataset 186 sleep data.frame 20 3 dataset 187 stack.loss numeric 21 stackloss dataset 188 stack.x matrix 21 3 stackloss dataset 189 stackloss data.frame 21 4 dataset 190 state.abb character 50 state dataset 191 state.area numeric 50 state dataset 192 state.center list 2 state dataset 193 state.division factor 50 state dataset 194 state.name character 50 state dataset 195 state.region factor 50 state dataset 196 state.x77 matrix 50 8 state dataset 197 sunspot.month ts 2988 dataset 198 sunspot.year ts 289 dataset 199 sunspots ts 2820 dataset 200 swiss data.frame 47 6 dataset 201 treering ts 7980 dataset 202 trees data.frame 31 3 dataset 203 uspop ts 19 dataset 204 volcano matrix 87 61 dataset 205 warpbreaks data.frame 54 3 dataset 206 women data.frame 15 2Le package gdata contient un seul jeu de données, MedUnist, dont l'aide indique «Table of conversions between Intertional Standard (SI) and US 'Conventional' Units for common medical measurements.». Le contenu de cette table de conversion est ici.
Le package foreign est intéressant parce que ses fonctions lisent (et écrivent parfois) les fichiers de données des autres grands logiciels statistiques comme Sas, Spss..
Objets du package foreign ========================= il y a 17 objets dans ce package name functionClass other obj_001 data.restore function obj_002 lookup.xport function obj_003 read.arff function obj_004 read.dbf function obj_005 read.dta function obj_006 read.epiinfo function obj_007 read.mtp function obj_008 read.octave function obj_009 read.S function obj_010 read.spss function obj_011 read.ssd function obj_012 read.systat function obj_013 read.xport function obj_014 write.arff function obj_015 write.dbf function obj_016 write.dta function obj_017 write.foreign function
Code-source php de cette page ; code javascript utilisé. Retour à la page principale du cours.
Retour à la page principale de (gH)