Bases de R

Contexte

Matière : _Analyse probabiliste de données · Prof : F. Corset · Type : TP1 Source : TP1 — Introduction à R

🎯 Objectifs de la séance

  • Prendre en main RStudio et la logique de script
  • Maîtriser les types de base et les structures de données (vecteur, liste, matrice, data.frame)
  • Comprendre l’indexation et le filtrage, qui sont le cœur de R
  • Écrire ses propres fonctions et simuler de l’aléatoire

🧠 Notions clés

  • Tout est vecteur : un scalaire est un vecteur de longueur 1. Les opérations sont vectorisées — pas besoin de boucle pour appliquer un calcul à tout un vecteur.
  • Indexation à partir de 1 (pas 0 comme en C/Java/Python).
  • Indice négatif = exclusion, pas « compter depuis la fin ».
  • Auto-conversion (coercition) : un vecteur n’a qu’un seul type ; R promeut tout vers le type le plus général.
  • Recycling : un vecteur trop court est répété pour atteindre la longueur voulue.
  • Structures : vecteur (1 type, plat) → matrice (1 type, 2D) → data.frame (1 type par colonne) → liste (tout type, hétérogène).

📝 Cours

1. RStudio — les 4 fenêtres

FenêtreRôle
ÉditionÉcriture des scripts .R — exécution avec Ctrl+Entrée
ConsoleExécution directe, tests rapides
Environnement / HistoriqueVariables en mémoire, commandes passées
Explorateur / Graphiques / Packages / AideFichiers, plots, doc

Bonne pratique

Travailler dans un script (TP1.R) et pas dans la console : commenter chaque ligne avec #. La console ne garde rien de reproductible.

2. R comme super-calculateur

5*(-3.2)        # -16
5^2             # 25
5**2            # 25 (équivalent, mais 5^2 est la forme idiomatique)
 
sin(2*pi/3)     # 0.8660254
sqrt(3)/2       # 0.8660254  <- même valeur, vérification numérique
log(exp(1))     # 1  (log = népérien par défaut)
log(1)          # 0

3. Affectation et types

x <- 1          # affectation (flèche = idiomatique en R)
x               # [1] 1
2*x             # [1] 2

Le [1] en sortie

Ce n’est pas la valeur : c’est l’indice du premier élément affiché sur la ligne. Sur un long vecteur, chaque ligne commence par l’indice de son premier élément.

Chaînes — guillemets simples ou doubles, indifférent :

ch <- "Voici une chaîne"
(ch <- 'Voici une chaîne')   # les parenthèses = affecte ET affiche

Numérique vs entier :

(nb  <- 101)     # numeric (double)
(nb2 <- 101L)    # integer — le suffixe L force l'entier
nb == nb2        # TRUE     <- égalité de valeur
identical(nb,nb2) # FALSE   <- mais types différents !

Piège d'examen

== compare les valeurs (avec conversion), identical() compare valeur ET type. C’est la distinction classiquement demandée.

Valeurs spéciales :

ValeurSens
TRUE / FALSE (ou T / F)Booléens
NADonnée manquante (Not Available)
NaNRésultat indéfini (Not a Number), ex. 0/0
Inf / -InfInfini, ex. 1/0
1+2iComplexe
rm(ch, nb, nb2, a)   # supprime des variables de l'environnement

4. Vecteurs

Création :

c(1,2,3,4,5)      # [1] 1 2 3 4 5   — c = "combine"
c(1:5)            # [1] 1 2 3 4 5
1:10              # séquence entière
seq(1,10)         # idem
seq(0,20,1.3)     # séquence à pas choisi (1.3)
rep(2,3)          # [1] 2 2 2
rep(1:2,3)        # [1] 1 2 1 2 1 2   — répète le motif 3 fois
rep(1:2,2:3)      # [1] 1 1 2 2 2     — répète 1 deux fois, 2 trois fois

Vecteurs typés vides / initialisés :

logical(2)     # [1] FALSE FALSE
integer(1)     # [1] 0
numeric()      # numeric(0)  — vecteur vide
character(3)   # [1] "" "" ""

Indexation et filtrage — le cœur du TP :

z <- seq(0,20,1.3)
 
z[8]              # 8e élément -> 9.1
z[-8]             # TOUT SAUF le 8e
z[1:8]            # les 8 premiers
z[-(1:8)]         # tout sauf les 8 premiers
z[9:length(z)]    # du 9e au dernier
z[c(2,6,14)]      # éléments 2, 6 et 14
 
z[z>10]           # FILTRAGE : les éléments > 10
z[z>4 & z<15]     # & = ET logique,  | = OU logique

Indice négatif

z[-8] exclut l’élément 8. Ce n’est pas « le 8e depuis la fin » comme en Python. Pour le dernier élément : z[length(z)].

Trois façons d’indexer :

v <- c(1,3,2)
v[c(1,3)]                # par POSITION       -> 1 2
v[c(TRUE,FALSE,TRUE)]    # par MASQUE logique -> 1 2
v %in% c(1,2)            # [1] TRUE FALSE TRUE  — test d'appartenance
v[v %in% c(1,2)]         # par APPARTENANCE   -> 1 2

Coller des chaînes :

paste(c("toto","tutu"), 1:2)                  # "toto 1" "tutu 2"  (sep=" " par défaut)
paste(c("toto","tutu"), 1:2, sep="")          # "toto1"  "tutu2"
paste(c("toto","tutu"), 1:2, collapse=" & ")  # "toto 1 & tutu 2"  (UNE seule chaîne)

sep vs collapse

sep = colle les arguments entre eux, élément par élément (résultat : un vecteur). collapse = aplatit ensuite le vecteur résultat en une seule chaîne.


5. Listes

Une liste est un conteneur hétérogène : chaque élément peut avoir un type et une longueur différents.

(l  <- list(1:10, c("toto","titi")))          # éléments anonymes -> [[1]], [[2]]
(l2 <- list(a=1:10, b=c("toto","titi")))      # éléments nommés   -> $a, $b
c(l, l2)                                       # fusionne deux listes
names(l2)                                      # [1] "a" "b"

Extraction :

l2[[1]]      # par indice      -> le CONTENU (vecteur 1:10)
l2$a         # par nom, mode "utilisateur"
l2[["a"]]    # par nom, mode "développeur"
 
cle <- "a"
l2[[cle]]    # OK  -> évalue la variable cle, donc l'élément "a"
l2$cle       # NULL -> $ ne fait PAS d'évaluation, il cherche un élément nommé "cle"

[ ] vs [[ ]] — piège classique

l2[1] renvoie une sous-liste (structure conservée). l2[[1]] renvoie le contenu de l’élément. Quand on veut travailler sur les données, c’est presque toujours [[ ]] ou $. Et pour indexer par une variable, il faut [[ ]]$ ne marche pas.

l2[1]        # sous-liste contenant $a
l2[c(2,1)]   # sous-liste réordonnée

6. Matrices

A <- matrix(1:12, nrow=4, ncol=3, byrow=TRUE)  # remplit LIGNE par ligne
B <- matrix(1:12, nrow=4, ncol=3)              # remplit COLONNE par colonne (défaut)
 
t(A)          # transposée
A[2,3]        # élément ligne 2, colonne 3
A[,3]         # toute la colonne 3
A[,c(1,3)]    # colonnes 1 et 3
t(B) %*% A    # produit MATRICIEL (%*%), à ne pas confondre avec * (terme à terme)

byrow

Par défaut R remplit par colonnes. byrow=TRUE change tout le résultat — erreur fréquente.

* vs %*%

A * B = produit terme à terme. A %*% B = produit matriciel au sens algébrique.


7. Auto-conversion (coercition)

Un vecteur ne contient qu’un seul type. R promeut silencieusement selon la hiérarchie :

logical → integer → numeric → character

a <- c(TRUE, 2L)
a ; class(a)          # [1] 1 2          "integer"   (TRUE devient 1)
a <- c(a, 3)
a ; class(a)          # [1] 1 2 3        "numeric"
a <- c(a, "quatre")
a ; class(a)          # "1" "2" "3" "quatre"   "character"

Danger silencieux

Un seul texte dans une colonne de nombres transforme tout en character — et les calculs échouent ensuite. C’est la cause n°1 de bugs après un import de fichier.

Vecteur → liste :

c(l, v)   # équivalent à c(l, as.list(v)) : chaque élément de v devient un élément de liste

8. Recycling

Si un vecteur est trop court, R le répète jusqu’à la longueur nécessaire.

(1:8)[c(TRUE,FALSE)]        # [1] 1 3 5 7    — motif TF recyclé 4 fois
(1:9)[c(TRUE,FALSE)]        # [1] 1 3 5 7 9  — recyclage incomplet, mais toléré
(1:8)[c(TRUE,FALSE,FALSE)]  # [1] 1 4 7      — un sur trois
 
cbind(1:5, c(TRUE,FALSE,FALSE))
#      [,1] [,2]
# [1,]    1    1
# [2,]    2    0
# [3,]    3    0
# [4,]    4    1     <- le motif a rebouclé ici
# [5,]    5    0

Deux pièges

  1. Le recycling est silencieux quand la longue est un multiple de la courte — sinon simple warning, pas d’erreur. Un bug peut passer inaperçu.
  2. Le recycling ne s’applique pas aux data.frames.

9. data.frame — le tableau individus × variables

C’est la structure de l’analyse de données : une ligne = un individu, une colonne = une variable. Chaque colonne a son propre type (contrairement à la matrice).

IMC <- data.frame(
  Sexe   = c("H","F","H","F"),
  Taille = c(1.83,1.76,1.82,1.60),
  Poids  = c(67,58,66,48),
  row.names = c("Pierre","Valérie","Paul","Virginie")
)

À connaître pour explorer un data.frame : str(), summary(), head(), nrow(), ncol(), dim(), names().


10. Facteurs et variables ordinales

Un facteur encode une variable qualitative : R stocke des niveaux (levels) et non du texte libre.

x <- factor(c("bleu","vert","bleu","rouge","bleu","vert","vert"))
x            # Levels: bleu rouge vert   (ordre ALPHABÉTIQUE par défaut)
levels(x)    # [1] "bleu" "rouge" "vert"
class(x)     # [1] "factor"

Variable ordinale — quand les modalités ont un ordre naturel :

z <- ordered(
  c("Petit","Grand","Moyen","Grand","Moyen","Petit","Petit"),
  levels = c("Petit","Moyen","Grand")     # on IMPOSE l'ordre
)
class(z)     # [1] "ordered" "factor"
z            # Levels: Petit < Moyen < Grand

Pourquoi ça compte

Sans levels=, R trie par ordre alphabétique — « Grand < Moyen < Petit », ce qui n’a aucun sens. Le type conditionne aussi les tests statistiques et les graphiques applicables.


11. Écrire ses fonctions

Hello <- function() {
  cat("Hello World", "\n")
}
Hello()
 
Bonjour <- function(x) {
  cat("Bonjour", x, "\n")
}
Bonjour("et bon TP")

cat() vs print()

cat() affiche du texte brut (sans le [1], sans les guillemets) — pour un message. print() affiche la représentation R de l’objet — pour inspecter. Une fonction R renvoie automatiquement sa dernière expression évaluée ; return() est optionnel.


12. Aléatoire et simulation

sample(0:1, 10, replace=TRUE)   # 10 lancers de pièce (1=pile, 0=face)
runif(5)                        # 5 tirages uniformes sur [0,1]

replace=TRUE

Sans lui, sample() tire sans remise — impossible de tirer 10 valeurs dans 0:1. À ne jamais oublier pour simuler des tirages répétés.

Répéter une expérience :

lancer <- function() sample(0:1, 10, replace=TRUE)
simu   <- replicate(100, lancer())   # matrice 10 x 100 : 100 expériences en colonnes

Appliquer une fonction sur les lignes/colonnes :

(nb.pile <- apply(simu, MARGIN=2, FUN=sum))   # somme par COLONNE = nb de piles par expérience
table(nb.pile)                                # tableau de fréquences
 
barplot(table(nb.pile)/100,
        main = "Diagramme en bâton des piles obtenus",
        xlab = "Nombre de piles")

Retenir MARGIN

MARGIN=1 → on applique par ligne. MARGIN=2par colonne. Moyen mnémotechnique : dans dim(), les lignes viennent en premier (1), les colonnes en second (2).


13. Importer des données

donnees <- read.table(
  file   = "nutriage.txt",
  header = TRUE,    # 1re ligne = noms des variables
  sep    = "\t",    # séparateur : tabulation
  dec    = "."      # séparateur décimal
)
ParamètreRôle
fileChemin du fichier — file.choose() ouvre un sélecteur graphique
headerTRUE si la 1re ligne contient les noms de colonnes
sepSéparateur de champs : "\t", " ", ",", ";"
decSéparateur décimal : "." ou "," (fichiers français !)
row.names1 = utiliser la 1re colonne comme noms de lignes
attach(donnees)   # accès direct aux variables par leur nom

attach()

Pratique en TP, mais déconseillé en vrai : ça duplique le contexte et crée des ambiguïtés si une variable du même nom existe déjà. Préférer donnees$Variable. Pour annuler : detach(donnees).

Fichiers français

Un CSV exporté depuis Excel FR utilise souvent sep=";" et dec=",". Si les nombres arrivent en character, c’est presque toujours ça (cf. coercition § 7).


💡 Exemples / démos

Chaîne complète : simuler et visualiser

lancer  <- function() sample(0:1, 10, replace=TRUE)
simu    <- replicate(100, lancer())
nb.pile <- apply(simu, MARGIN=2, FUN=sum)
barplot(table(nb.pile)/100,
        main="Diagramme en bâton des piles obtenus",
        xlab="Nombre de piles")

Ce pipeline fonction → replicateapplytablebarplot est le schéma type de toute simulation du cours.


📝 Exercices du TP

Simulation aléatoire

  1. Écrire une fonction simulant un lancer de pièce (pile / face).
  2. Écrire une fonction simulant un dé à 6 faces avec sample().
  3. Simuler un Yams (5 dés) — penser aux arguments size et replace.
  4. Estimer la probabilité d’un Yams avec replicate(), apply() et unique().

Indice pour le Yams

Un Yams = les 5 dés identiques ⇔ length(unique(des)) == 1.

TP — Indice de Masse Corporelle

Données : 10 enfants de 3–4 ans (sexe, ZEP, âge en années/mois, poids en kg, taille en cm).

  1. Enregistrer les variables dans des vecteurs nommés.
  2. Calculer les moyennes pertinentes.
  3. Calculer l’IMC = Poids(kg) / Taille²(m) — ⚠️ convertir les cm en m.
  4. Regrouper le tout dans la structure R appropriée (→ data.frame, § 9).
  5. Consulter l’aide de plot() (?plot).
  6. Tracer poids en fonction de la taille, avec titre et axes légendés.

⚠️ À retenir absolument (examen)

  • == (valeur) vs identical() (valeur et type).
  • Indexation à partir de 1 ; indice négatif = exclusion.
  • [ ] (sous-liste) vs [[ ]] / $ (contenu) ; $ n’évalue pas les variables.
  • Coercition : logical → integer → numeric → character, silencieuse et destructrice.
  • Recycling : silencieux si multiple, inapplicable aux data.frames.
  • matrix(byrow=) : par défaut R remplit par colonnes.
  • * (terme à terme) vs %*% (matriciel).
  • apply(MARGIN=1) = lignes, MARGIN=2 = colonnes.
  • factor() trie par ordre alphabétique → utiliser ordered(levels=) pour l’ordinal.
  • sample(..., replace=TRUE) obligatoire pour des tirages répétés.

📇 Aide-mémoire

ConceptFonction / opérateurExemple
Affectation<-x <- 5
Vecteurc()c(1,2,3)
Séquence:1:10
Séquence à passeq()seq(0,20,1.3)
Répétitionrep()rep(2,3)
Indexation[]v[1:3]
Exclusion[-]v[-8]
Filtrage[] + conditionz[z>10]
Appartenance%in%v %in% c(1,2)
Matricematrix()matrix(1:6,2,3)
Produit matriciel%*%t(B) %*% A
Transposéet()t(A)
Listelist()list(a=1,b=2)
Data.framedata.frame()data.frame(x,y)
Facteurfactor()factor(c("A","B"))
Ordinalordered()ordered(x, levels=...)
Fonctionfunction()f <- function(x){...}
Applicationapply()apply(mat,2,sum)
Réplicationreplicate()replicate(100, expr)
Aléatoiresample(), runif()sample(1:6,10,TRUE)
Fréquencestable()table(vec)
Graphiquesplot(), barplot()plot(x,y)
Importread.table()read.table(file=...)
Concaténation textepaste()paste(a, b, sep="")
Type d’un objetclass()class(a)
Suppressionrm()rm(x, y)
Aide??plot

❓ Points flous / à revoir

  • Refaire l’exercice du Yams et vérifier la probabilité estimée (théorique : 6/6^5 ≈ 0.0077)
  • Tester l2[1] vs l2[[1]] dans la console pour bien voir la différence de structure
  • Importer nutriage.txt et vérifier les types obtenus avec str()

🔗 Liens