Tests et exécutions conditionnelles, boucles, fonctions de boucles implicites. Traitement des chaînes de caractères.

Table des matières

;; M-: flyspell-generic-check-word-predicate
  (defadvice org-mode-flyspell-verify
    (after my-org-mode-flyspell-verify activate)
    "Don't spell check src blocks."
    (setq ad-return-value
          (and ad-return-value
               (not (org-in-src-block-p))
               (not (member 'org-block-begin-line (text-properties-at (point))))
               (not (member 'org-block-end-line (text-properties-at (point)))))))

(unless (find "beamer-xetex-fr" org-export-latex-classes :key 'car
              :test 'equal)
  (add-to-list 'org-export-latex-classes
               '("beamer-xetex-fr"
                 "\\documentclass[hyperref={xetex, colorlinks=true, urlcolor=blue, plainpages=false, pdfpagelabels, bookmarksnumbered}]{beamer}
                  \\usepackage{xunicode,fontspec,xltxtra}
                  \\usepackage[french]{babel}
                  \\usepackage{graphicx,longtable,url,rotating}
                  \\definecolor{lightcolor}{gray}{.55}
                  \\definecolor{shadecolor}{gray}{.95}
                  \\usepackage{minted}
                  \\newminted{common-lisp}{fontsize=\\footnotesize}
                  \\setromanfont[Mapping=text-text]{Liberation Serif}
                  \\setsansfont[Mapping=text-text]{Liberation Sans}
                  \\setmonofont[Mapping=text-text]{Liberation Mono}
                  [NO-DEFAULT-PACKAGES]
                  [EXTRA]"
org-beamer-sectioning)))
(add-to-list 'org-export-latex-minted-langs
'(R "r"))  
  (setq org-export-latex-minted-options
        '(("bgcolor" "shadecolor")
          ("fontsize" "\\scriptsize")))
(setq org-latex-to-pdf-process
      '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
library(Cairo)
CairoFonts(regular="Liberation Serif:style=Medium",
           bold="Liberation Serif:style=Bold",
           italic="Liberation Serif:style=Oblique",
           bolditalic="Liberation Serif:style=BoldOblique")
options(width=50)
options(digits=3)

1 Introduction

1.1 Références

  • Ce qui suit est essentiellement une traduction de diapos de Ross Ihaka (l'un des deux créateurs de R) associées à un cours intitulé Statistical Computing ;
  • Une autre excellente référence pour ce cours et pour R en général est le bouquin de Pierre Lafaye de Micheaux, Rémy Drouilhet et Benoît Liquet « Le logiciel R. Maîtriser le langage. Effectuer des analyses statistiques. » (2011, Springer).

1.2 Rappels sur les expressions (1)

Un programme en R est constitué d'une suite d'expressions. Ces expressions peuvent être « simples » comme :

(maVariable <- "Ma variable")
[1] "Ma variable"

Elles peuvent aussi être « composées » lorsque plusieurs expressions simples sont séparées par un « ; » ou un saut de ligne et regroupées entre des accolades « { } » comme :

{ expr1 ; expr2 ; ... ; exprN } 

1.3 Rappels sur les expressions (2)

Toute expression dans R a une valeur et la valeur d'une expression composée est celle de la dernière expression simple qui la constitue :

x <- {10 ; 20}
x
[1] 20

1.4 Affectation dans les expressions composées

Il est possible d'avoir des affectations (attributions de valeurs à des variables) dans les expressions composées. Les valeurs des variables ainsi affectées peuvent être utilisées dans d'autres expressions :

z <- { x <- 10; y <- x^2; x+y }
x
[1] 10

y
[1] 100

z
[1] 110

2 Conditionnelles

2.1 if-then-else

Avec if-then-else il est possible de choisir entre deux expressions (potentiellement composées) suivant la valeur d'une condition (expression logique) :

if (condition) expr.si.vrai else expr.si.faux

Si condition est vraie (TRUE), expr.si.vrai est évaluée, sinon expr.si.faux est évaluée.

Remarques

  • le premier élément de condition est le seul à être pris en compte ;
  • la valeur de l'expression complète est la valeur de celle des deux expressions qui est évaluée.

2.2 if-then-else : exemples

L'expression :

if (x > 0) y <- sqrt(x) else y <- -sqrt(-x)

constitue un exemple de construction if-then-else qui semblera familière à un programmeur Pascal, Fortran, C, C++ ou Java.

Cette expression serait néanmoins typiquement écrite en R :

y <- if (x > 0) sqrt(x) else -sqrt(-x)

ce qui sera naturel pour un programmeur Lisp ou Algol.

2.3 if-then-else : forme simplifiée

Il y a une forme simplifiée de l'expression if-then-else qui sera employée lorsqu'il n'y a pas d'expr.si.faux. Cette expression a la forme générale :

if (condition) expr.si.vrai

et elle est complètement équivalente à :

if (condition) expr.si.vrai else NULL

2.4 La fonction switch

La fonction switch permet de choisir entre plusieurs alternatives. C'est une sorte de if-then-else généralisé.

switch(expr,
       étiquette-1 = bloc-de-code-1,
       étiquette-2 = bloc-de-code-2,
       .
       .
       . )

La fonction sélectionne un des blocs de code suivant la valeur d'expr.

2.5 switch par indice

Si expr a une valeur numérique (entière) n, alors le n-ième bloc de code est évalué. Dans ce cas, les blocs de code n'ont pas besoin d'être étiquetés :

k <- 5
switch(k,"I","II","III","IV","V",
       "VI","VII","VIII","IX","X")
[1] "V"

Si n n'est pas compris dans les indices possible (par exemple, n = 11, dans le cas illustré ci-dessus), alors l'expression switch prend la valeur NULL.

2.6 switch par étiquette

Si expr à une valeur de type chaîne de caractères alors les étiquettes sont examinées afin d'en trouver une correspondant exactement à expr. Si une telle étiquette est trouvée, alors le bloc de code correspondant est évalué et sa valeur devient celle de l'expression switch.

x <- rnorm(10)
loc.est <- "mean"
switch(loc.est,
       median = median(x),
       mean = mean(x))
[1] -0.4761289

mean(x)
[1] -0.4761289

2.7 Variations

Si il n'y a pas d'étiquette correspondante alors :

  1. si il y a un bloc de code sans étiquette, il est évalué et sa valeur « est retournée » (c.-à-d. devient la valeur de l'expression switch) ;
  2. si il n'y a pas de bloc de code sans étiquette, la valeur NULL est retournée.
switch("c", a = 1, 3, b = 2)
[1] 3

switch("c", a = 1, b = 2)

2.8 Blocs de code vides

Des blocs de code vides peuvent être utilisés pour faire correspondre plusieurs étiquettes au même bloc de code.

switch("a",
       a =,
       b = "Je l'ai",
       c = "Raté" )
[1] "Je l'ai"

Dans la situation ci-dessus, ''a'' et ''b'' vont donner ''Je l'ai'' comme valeur retournée.

2.9 ifelse

La fonction ifelse permet d'exécuter l'une ou l'autre de deux instructions appliquées à un vecteur suivant que les valeurs d'une condition logique sont vraies ou fausses.

x <- c(6:-4)
sqrt(x)
 [1] 2.45 2.24 2.00 1.73 1.41 1.00 0.00  NaN  NaN
[10]  NaN  NaN
Message d'avis :
In sqrt(x) : production de NaN

sqrt(ifelse(x >= 0, x, NA))
 [1] 2.45 2.24 2.00 1.73 1.41 1.00 0.00   NA   NA
[10]   NA   NA

3 Boucles

3.1 Boucles for

Nous souhaitons souvent effectuer une opération donnée sur tous les éléments d'un vecteur, d'une liste ou plus généralement d'un objet « indiçable ». Dans R, ces tâches répétitives sont effectuées avec une « boucle for ». \vspace{0.2cm}

Une boucle for a la forme :

for (variable in vecteur) expression

L'effet de cette boucle est d'affecter successivement à variable la valeur de chaque élément de vecteur avant d'évaluer expression.

3.2 Boucles for : exemple 1

Supposons que le vecteur x contient des valeurs numériques et que nous souhaitons calculer la somme de ces valeurs. Une manière d'effectuer ce calcul et d'initialiser une variable à zéro avant d'additionner successivement tous les éléments de x à cette dernière.

s <- 0
for (i in 1:length(x))
  s  <- s + x[i]

Ici, i se voit succéssivement assigner les valeurs 1, 2, …, length(x) et pour chaque valeur de i, l'expression

s  <- s + x[i] 

est évaluée.

3.3 Boucles for : exemple 2

L'exemple précédent est typique des boucles utilisées par de nombreux langages de programmation, mais R n'a pas besoin d'utiliser une variable de boucle de type entier. \vspace{0.2cm}

Ainsi, la boucle précédente pourrait être écrite :

s <- 0
for (elt in x)
  s  <- s + elt

Cette version est à la fois plus simple et plus efficace.

3.4 L'instruction next

Quand certaines conditions sont remplies, il peut être utile de ne pas effectuer les dernières instructions d'une boucle pour passer directement à l'itération suivante. Ceci peut être fait en évaluant l'instruction next lorsque les conditions sont remplies.

for (variable in vecteur) {
  expression-1
  expression-2
  if (condition)
    next
  expression-3
  expression-4 }

Quand condition est vraie (TRUE), expression-3 et expression-4 ne sont pas évaluées.

3.5 La boucle while

Les boucles for évaluent une expression un nombre de fois déterminé. Il est aussi utile de pouvoir évaluer une expression jusqu'à ce qu'une condition soit fausse (FALSE). La « boucle while » fournit ce type de structure de contrôle.

\vspace{0.2cm}

while (condition) expression

Comme d'habitude, condition est une expression qui doit avoir pour valeur TRUE ou FALSE et expression est une expression simple ou composée.

3.6 Exemple de boucle while

Comme exemple simple, considérons le problème de sommer les entiers jusqu'à ce que la somme excède un seuil. Pour un seuil de 100, nous pouvons effectuer la tâche de la façon suivante :

seuil <- 100
n <- 0
s <- 0
while (s <= seuil) {
  n <- n + 1
  s <- s + n }
c(n,s)
[1]  14 105

3.7 La boucle repeat

Le dernier type de boucle disponible en R est la « boucle repeat ». \vspace{0.2cm}

Ce type de boucle a la forme :

repeat expression

L'effet de cette boucle est de réévaluer expression « pour toujours ». Pour sortir de ces boucles il faut utiliser l'instruction break.

3.8 Sortir d'un boucle avec break

L'instruction break permet de sortir simplement de tout type de boucle. Par exemple :

seuil <- 100
n <- 0
s <- 0
repeat {
  if (s > seuil)
    break
  n <- n + 1
  s <- s + n }
c(n,s)
[1]  14 105

(Cette boucle est complètement équivalente à la boucle while que nous avons vu précédemment.)

4 Fonctions de boucles implicites

4.1 Moyennes des lignes et colonnes d'une matrice

Il arrive souvent que l'on souhaite obtenir la moyenne (ou toute autre statistique) des lignes ou des colonnes d'une matrice. Il est bien sur possible des les obtenir avec une boucle for. Nous pourrions ainsi obtenir la moyenne des lignes d'une matrice x avec:

rm <- numeric(nrow(x))
for (i in 1:nrow(x))
  rm[i] <- mean(x[i,])

Remarquez qu'il n'y a ici rien de spécial dans la fonction mean, le même code peut être utilisé pour tout autre statistique à valeur scalaire comme var ou sd.

4.2 La fonction apply

Comme le type de calcul que nous venons d'effectuer sur des structures multidimensionnelles (matrice, etc) est si fréquent, les concepteurs de R l'ont doté de fonctions spécialement prévues à cet effet. La fonction apply peut ainsi être utilisée pour appliquer une fonction aux lignes ou colonnes d'une matrice. L'expression

apply(matrice, 1, fonction)

applique fonction à chaque ligne de matrice et à pour valeur un vecteur qui a autant d'éléments que matrice à de lignes. De même,

apply(matrice, 2, fonction)

applique fonction à chaque colonne de matrice et à pour valeur un vecteur qui a autant d'éléments que matrice à de colonnes.

4.3 Moyennes des lignes et colonnes d'une matrice

Les moyennes des lignes et des colonnes d'une matrice x peuvent ainsi être calculées avec :

(x <- matrix(1:4, nr=2, nc=2))
     [,1] [,2]
[1,]    1    3
[2,]    2    4

apply(x,1,mean)
[1] 2 3

apply(x,2,mean)
[1] 1.5 3.5

Comme la fonction mean n'a rien de spécial on peut lui substituer la fonction sd pour obtenir l'écart type des lignes :

apply(x,1,sd)
[1] 1.41 1.41

4.4 apply et fonctions anonymes

Dans l'exemple précédent nous avons utilisé le nom de la fonction a appliquer aux lignes et colonnes de la matrice x. Mais il est aussi possible de spécifier une fonction directement en utilisant sa définition plutôt que son nom. Nous pouvons ainsi calculer la somme des carrés des colonnes de x avec :

apply(x, 2, function(x) sum(x^2))
[1]  5 25

Calculer la somme des carrés des variables centrées sur leur moyenne est aussi très simple :

apply(x, 2, function(x) sum((x - mean(x))^2))
[1] 0.5 0.5

4.5 apply et fonctions à valeurs vectorielles

Il est possible d'utiliser comme troisième paramètre de apply une fonction à valeurs vectorielles. La fonction apply renvoie alors une matrice dont les colonnes sont les résultats de l'application de la fonction aux lignes (si le second paramètre est 1) ou aux colonnes (si le second paramètre est 2) du premier paramètre.

apply(x, 1, range)
     [,1] [,2]
[1,]    1    2
[2,]    3    4

apply(x, 2, range)
     [,1] [,2]
[1,]    1    3
[2,]    2    4

4.6 Paramètres additionnels des fonctions passées à apply

Il est possible de spécifier des paramètres additionnels des fonctions passées à apply comme paramètres additionnels de apply. Un petit exemple devrait rendre la situation plus claire : la fonction mean prend un paramètre optionnel, trim, qui indique la fraction des éléments « extrêmes » à supprimer du vecteur dont on souhaite la moyenne avant de calculer cette dernière. La plus grande et la plus petite trim/2 fraction des observations sont retirées avant le calcul. Pour retirer les 5% valeurs les plus petites et les plus grandes nous utiliserons donc trim = 0.1, ce que nous pourrions faire dans apply de la façon suivante :

apply(matrice, marge, mean, trim = 0.1)

4.7 lapply et sapply des variations d'apply pour des objets unidimensionnels (1)

Les fonctions lapply et sapply sont des versions d'apply adaptées au objets unidimensionnels comme les listes et les vecteurs. L'objet étant unidimensionnel, nous avons pas besoin du second paramètre (MARGIN) de apply. lapply renvoie une liste et sapply renvoie un vecteur ou une matrice quand c'est possible :

lapply(1:3, function(x) x%%2==0)
[[1]]
[1] FALSE

[[2]]
[1] TRUE

[[3]]
[1] FALSE

4.8 lapply et sapply des variations d'apply pour des objets unidimensionnels (2)

sapply(1:10, function(x) x%%2==0)
[1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE
[8]  TRUE FALSE  TRUE

4.9 Retirer une statistique d'une matrice avec sweep

Après le calcul d'une statistique, nous somme souvent amenés à soustraire cette dernière des observations d'où elle est issue afin d'obtenir des résidus. Quand cette opération doit être effectuée sur les lignes ou les colonnes d'une matrice, nous pouvons utiliser une boucle for. Cette tâche étant fréquemment effectué, R met à notre disposition la fonction sweep. Ainsi, pour soustraire la statistique définie par la fonction statistique des lignes de la matrice x, nous employons :

sweep(matrice, 1, apply(matrice, 1, statistique))

Pour faire la même chose sur les colonnes, nous employons :

sweep(matrice, 2, apply(matrice, 2, statistique))

4.10 Variations sur sweep (1)

Par défaut, sweep soustrait de son premier paramètre, le vecteur définit par son troisième paramètre selon la dimension spécifiée par son deuxième paramètre (MARGIN = 1 pour les lignes et MARGIN = 2 pour les colonnes). Un quatrième paramètre optionnel peut être utilisé pour spécifier une opération binaire autre que la soustraction. Par exemple, pour diviser au lieu de soustraire, il suffit de faire :

sweep(matrice,
      1,
      apply(matrice, 1, statistique),
      "/")

et

sweep(matrice,
      2,
      apply(matrice, 2, statistique),
      "/")

4.11 Variations sur sweep (2)

Plus concrètement, pour diviser les colonnes d'une matrice par leurs moyennes respectives, nous employons :

(x <- cbind(1:3, 9:11))
     [,1] [,2]
[1,]    1    9
[2,]    2   10
[3,]    3   11

sweep(x, 2, apply(x, 2, mean), "/")
     [,1] [,2]
[1,]  0.5  0.9
[2,]  1.0  1.0
[3,]  1.5  1.1

4.12 Un exemple en statistique

Analysons le taux de mortalité infantile (morts pour 1000 naissances) en fonction de la région des États-Unis et du niveau d'éducation du père (période 1964-1966). C'est un exemple typique de données rencontrées en analyse statistique.

(mortalité  <- matrix(c(25.3, 25.3, 18.2, 18.3, 16.3,
                       32.1, 29.0, 18.8, 24.3, 19.0,
                       38.8, 31.0, 19.3, 15.7, 16.8,
                       25.4, 21.1, 20.3, 24.0, 17.5),
                     nrow = 4, byrow = TRUE,
                     dimnames = list(
                       Region =
                       c("Nord Est", "Centre Nord",
                         "Sud", "Ouest"),
                       "Éducation du père (années)" =
                       c("<=8", "9-11", "12",
                         "13-15", ">=16"))))
 
            Éducation du père (années)
Region         <=8 9-11   12 13-15 >=16
  Nord Est    25.3 25.3 18.2  18.3 16.3
  Centre Nord 32.1 29.0 18.8  24.3 19.0
  Sud         38.8 31.0 19.3  15.7 16.8
  Ouest       25.4 21.1 20.3  24.0 17.5

4.13 Création de la matrice

mortalité  <-
  matrix(c(25.3, 25.3, 18.2, 18.3, 16.3,
           32.1, 29.0, 18.8, 24.3, 19.0,
           38.8, 31.0, 19.3, 15.7, 16.8,
           25.4, 21.1, 20.3, 24.0, 17.5),
         nrow = 4, byrow = TRUE,
         dimnames = list(
           Region =
           c("Nord Est", "Centre Nord",
             "Sud", "Ouest"),
           "Éducation du père (années)" =
           c("<=8", "9-11", "12",
             "13-15", ">=16")))

4.14 Un modèle linéaire à deux facteurs

Classiquement, on cherche à expliquer ce type de table via :

  • un effet global ;
  • un effet « région »;
  • un effet « niveau d'éducation du père ».

Le modèle le plus simple auquel on peut penser est un modèle additif, dit « modèle linéaire » : \[y_{i,j} = \mu + \alpha_i + \beta_j + \epsilon_{i,j} \; ,\] où \(y_{i,j}\) représente l'entrée de la table, \(\mu\) représente la « mortalité de base », \(\alpha_i\) est un effet commun à toutes les observations de la ligne \(i\) (effet de la région), \(\beta_j\) est un effet commun à toutes les observations de la colonne \(j\) (effet de l'éducation du père) et les \(\epsilon_{i,j}\) sont de (petits) résidus décrivant les déviations entre les prédictions du modèle et les observations.

4.15 Ajustement par soustraction des effets

Nous allons ajuster notre modèle en soustrayant successivement les effets d'une matrice de résidus courants. Nous commençons par estimer la « mortalité de base », que nous définissons plus précisément comme la moyenne globale, avant de la soustraire des données brutes. Cela nous donne une matrice de déviations par-rapport à la moyenne globale :

r <- mortalité
μ <- mean(mortalité)
r <- r - μ
μ
[1] 22.8

4.16 Soustraction de l'effet de la région

Nous estimons ensuite l'effet de ligne (ou l'effet de région) comme la moyenne suivant les lignes de r avant de soustraire, ligne à ligne, cet effet de r. Vous aurez devinez que la combinaison de apply et de sweep est idéale pour cette tâche :

α <- apply(r, 1, mean)
r <- sweep(r, 1, α)
α

Nord Est Centre Nord         Sud       Ouest 
    -2.14        1.82        1.50       -1.16

sum(α)
[1] 3.77e-15

4.17 Soustraction de l'effet de l'éducation du père

Nous terminons en estimant l'effet de colonne (niveau d'éducation du père) en calculant la moyenne des colonnes de r avant de soustraire, colonne par colonne, cet effet :

β <- apply(r, 2, mean)
r <- sweep(r, 2, β)
β

<=8  9-11    12 13-15  >=16 
7.57  3.78 -3.67 -2.25 -5.42

sum(β)
[1] 0

4.18 Les résidus

Après avoir terminé notre procédure, il nous reste une matrice de résidus :

r
             Éducation du père (années)
Region           <=8   9-11    12 13-15   >=16
  Nord Est    -2.955  0.845  1.19 -0.13  1.045
  Centre Nord -0.115  0.585 -2.17  1.91 -0.215
  Sud          6.905  2.905 -1.34 -6.37 -2.095
  Ouest       -3.835 -4.335  2.32  4.59  1.265

4.19 Effets restants ?

Nous pouvons vérifier que nous avons bien soustrait les effets de lignes et de colonnes en calculant les moyennes suivant les lignes et le colonnes de r.

round(apply(r, 1, mean), digits=4)
Nord Est Centre Nord         Sud       Ouest 
       0           0           0           0

round(apply(r, 2, mean), digits=4)
<=8  9-11    12 13-15  >=16 
  0     0     0     0     0

Elles sont (essentiellement) nulles.

4.20 Interactions ?

Il arrive que les résidus possèdent une structure qui indique que le modèle n'est pas aussi bien ajusté qu'il le devrait. Il y a ainsi une petite indication d'interactions dans nos résidus :

r[order(α), order(β)]
             Éducation du père (années)
Region          >=16    12 13-15   9-11    <=8
  Nord Est     1.045  1.19 -0.13  0.845 -2.955
  Ouest        1.265  2.32  4.59 -4.335 -3.835
  Sud         -2.095 -1.34 -6.37  2.905  6.905
  Centre Nord -0.215 -2.17  1.91  0.585 -0.115

4.21 Produits externes (généralisés)

La fonction outer fournit une fonctionnalité utile dans le cadre des calculs matriciels. En mathématiques, le produit externe de deux vecteurs x et y est la matrice dont l'élément \((i,j)\) est : \[ x_i \times y_j \; .\] La fonction R outer généralise le produit externe de sorte que, lorsque qu'une fonction f est donnée comme paramètre optionnel, le résultat est une matrice dont l'élément \((i,j)\) est : \[f(x_i,y_j) \; .\] Par défaut la fonction utilisée par R est \(f(x,y) = x \times y\) ce qui donne le produit externe classique.

4.22 Exemple : une table de multiplications

Pour générer une table de multiplication jusqu'à 8x8 il suffit de faire :

outer(1:8,1:8)
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]    1    2    3    4    5    6    7    8
[2,]    2    4    6    8   10   12   14   16
[3,]    3    6    9   12   15   18   21   24
[4,]    4    8   12   16   20   24   28   32
[5,]    5   10   15   20   25   30   35   40
[6,]    6   12   18   24   30   36   42   48
[7,]    7   14   21   28   35   42   49   56
[8,]    8   16   24   32   40   48   56   64

4.23 Exemple : génération de labels

outer(LETTERS[1:8],1:5,
      function(x,y) paste(x, y, sep=""))
     [,1] [,2] [,3] [,4] [,5]
[1,] "A1" "A2" "A3" "A4" "A5"
[2,] "B1" "B2" "B3" "B4" "B5"
[3,] "C1" "C2" "C3" "C4" "C5"
[4,] "D1" "D2" "D3" "D4" "D5"
[5,] "E1" "E2" "E3" "E4" "E5"
[6,] "F1" "F2" "F3" "F4" "F5"
[7,] "G1" "G2" "G3" "G4" "G5"
[8,] "H1" "H2" "H3" "H4" "H5"

4.24 Exemple : génération de labels (suite)

c(outer(LETTERS[1:8],1:5,
        function(x,y) paste(x, y, sep="")))
 [1] "A1" "B1" "C1" "D1" "E1" "F1" "G1" "H1" "A2"
[10] "B2" "C2" "D2" "E2" "F2" "G2" "H2" "A3" "B3"
[19] "C3" "D3" "E3" "F3" "G3" "H3" "A4" "B4" "C4"
[28] "D4" "E4" "F4" "G4" "H4" "A5" "B5" "C5" "D5"
[37] "E5" "F5" "G5" "H5"

c(outer(1:5, LETTERS[1:8],
        function(x,y) paste(y, x, sep="")))
 [1] "A1" "A2" "A3" "A4" "A5" "B1" "B2" "B3" "B4"
[10] "B5" "C1" "C2" "C3" "C4" "C5" "D1" "D2" "D3"
[19] "D4" "D5" "E1" "E2" "E3" "E4" "E5" "F1" "F2"
[28] "F3" "F4" "F5" "G1" "G2" "G3" "G4" "G5" "H1"
[37] "H2" "H3" "H4" "H5"

4.25 Un exemple plus intéressant

  • l'usage de apply combiné à celui de outer permet de réaliser de façon efficace des calculs assez compliqués ;
  • dans cet exemple nous allons chercher pour chaque valeur y1, y2, \ldots, ym l'élément le plus proche dans l'ensemble {x1, \ldots, xn} (une sorte de régression vers le plus proche voisin) ;
  • ce problème peut être résolu avec des boucles imbriquées, mais il admet une solution tenant sur une ligne.

4.26 La fonction which

La fonction which prend comme paramètre un vecteur de booléen et renvoie un vecteur contenant les indices des éléments vrais de l'argument :

x <- c(-1, 0, 1)
which( x >= 0)
[1] 2 3

which( x == 0)
[1] 2

which( x > 2)
integer(0)

Cette fonction peut être simplement recodée par :

which  <-  function(x) (1:length(x))[x]

4.27 Trouver le plus proche voisin

L'algorithme est :

  1. construire la matrice D dont l'élément \((i,j)\) est la distance entre yi et xj (on utilisera évidemment outer pour cela) ;
  2. pour chaque ligne de D, trouver l'indice de la colonne contenant la plus petite distance ;
  3. renvoyer la valeur de x correspondant à cet indice.

4.28 Mise en œuvre

plusProche  <- function(x, y)
  x[apply(outer(y, x,
                function(y, x)
                abs(y - x)),
          1,
          function(x) which(x == min(x))[1])]
x = 0:10/10
(y = round(runif(4), 4))
[1] 0.4764 0.4498 0.2766 0.0286

plusProche(x, y)
[1] 0.5 0.4 0.3 0.0

5 Traitement des chaînes de caractères

5.1 Création de (vecteurs de) chaînes de caractères

Il y a deux manières de créer des chaînes de caractères :

\vspace{0.2cm} Avec des guillemets simples ou doubles :

(chaîne <- c("a",'bb',"ccc"))
[1] "a"   "bb"  "ccc"

Avec la fonction as.character :

formule <- y ~ a + b + c
as.character(formule)
[1] "~"         "y"         "a + b + c"

as.character(1:3)
[1] "1" "2" "3"

5.2 Chaînes prédéfinies (1)

R dispose de plusieurs vecteurs de chaînes de caractères prédéfinis :

letters
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k"
[12] "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v"
[23] "w" "x" "y" "z"

LETTERS
 [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K"
[12] "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V"
[23] "W" "X" "Y" "Z"

5.3 Chaînes prédéfinies (2)

month.name
 [1] "January"   "February"  "March"    
 [4] "April"     "May"       "June"     
 [7] "July"      "August"    "September"
[10] "October"   "November"  "December"

month.abb
[1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul"
[8] "Aug" "Sep" "Oct" "Nov" "Dec"

5.4 La fonction générique format

La fonction générique format « imprime de jolie façon » (pretty prints) certains objets R. Utilisée avec la fonction ISOdate, elle nous permet d'imprimer les mois de l'année ou leurs abréviations suivant le « codage local », c.-à-d. en français :

format(ISOdate(2013, 1:12, 1), "%B")
 [1] "janvier"   "février"   "mars"     
 [4] "avril"     "mai"       "juin"     
 [7] "juillet"   "août"      "septembre"
[10] "octobre"   "novembre"  "décembre"

format(ISOdate(2013, 1:12, 1), "%b")
 [1] "janv." "févr." "mars"  "avril" "mai"  
 [6] "juin"  "juil." "août"  "sept." "oct." 
[11] "nov."  "déc."

(Jetez un œil sur les exemples de la documentation de format pour vous faire une idée ses nombreuses possibilités.)

5.5 La fonction noquote

Si les guillemets anglo-saxonnes vous irritent, supprimez les avec la fonction noquote :

noquote(format(ISOdate(2013, 1:12, 1), "%B"))
[1] janvier   février   mars      avril    
[5] mai       juin      juillet   août     
[9] septembre octobre   novembre  décembre

5.6 La fonction nchar

La fonction nchar compte le nombre de symboles d'une chaîne et est vectorisée, c'est-à-dire qu'elle peut être appliquée à un vecteur de chaînes.

chaîne1 <- c("un","Cours R","a","Quoi !!!","A","À")
nchar(chaîne1)
[1] 2 7 1 8 1 1

Remarquez qu'un espace compte bien comme un symbole :

nchar(" ")
[1] 1

5.7 La fonction paste

Nous l'avons déjà utilisée lorsque nous avons vu la fonction outer. Elle permet de concaténer (rabouter) des chaînes comme :

paste("R,","c'est super !")
[1] "R, c'est super !"

Elle fait aussi des conversions automatiques si nécessaire :

paste("A",1:3)
[1] "A 1" "A 2" "A 3"

Avec son paramètre optionnel collapse, elle permet de convertir un vecteur de chaînes en une seule chaîne :

paste("A",1:3, collapse=", ")
[1] "A 1, A 2, A 3"

5.8 La fonction substring

La fonction substring permet d'extraire des sous-chaînes d'une chaîne :

substring("R, c'est super !", first=4, last=8)
[1] "c'est"

Elle est aussi vectorisée :

paste(substring(c("Windows",
                  "R, c'est super !",
                  "nul"),
                first=c(1,2,1), last=c(7,9,3)),
      collapse="")
[1] "Windows, c'est nul"

5.9 La fonction strsplit

La fonction strsplit permet de découper des chaînes lorsqu'un « motif » ou, plus précisément, une expression rationnelle (regular expression) est trouvée :

strsplit("A <- \"A\"", split=" ")
[[1]]
[1] "A"     "<-"    "\"A\""

Elle est aussi vectorisée et comme vous pouvez le deviner, elle renvoie une liste avec autant d'éléments qu'il y d'éléments dans le vecteur passé comme premier paramètre :

strsplit(c("A <- \"A\"", "plot(x, y, type=\"l\")"),
         split=" ")
[[1]]
[1] "A"     "<-"    "\"A\""

[[2]]
[1] "plot(x,"     "y,"          "type=\"l\")"

5.10 La fonction grep

La fonction grep permet de chercher un motif / expression rationnelle dans un vecteur et renvoie les indices des élément du vecteur qui contiennent ce motif. Un exemple simple :

grep("r|R",c("R","C","Fortran","Common Lisp"))
[1] 1 3

Un exemple plus compliqué où j'appelle la fonction list.files et où je cherche les fichier dont l'extension est « .tex » dans le répertoire courant :

list.files()[grep(".tex$",list.files())]  
[1] "Pouzat_MNHN_130417-V1.tex"
[2] "Pouzat_MNHN_130417-V2.tex"
[3] "Pouzat_MNHN_130417-V3.tex"

5.11 Fonctions tolower et toupper

Les fonctions tolower et toupper permettent de changer la casse (typographique) d'une chaîne de caractère :

toupper("ceci a commencé en minuscules...")
[1] "CECI A COMMENCÉ EN MINUSCULES..."

tolower("Et Est Passé en Majuscules...")
[1] "et est passé en majuscules..."

Date: 17-04-2013

Auteur: Christophe Pouzat

Created: 2019-01-13 dim. 15:34

Validate