Séance 1 : Premiers pas avec R et RStudio

Interface, projets, objets, fonctions, packages, vecteurs et data frames

Objectifs de la séance

  • Installer R et RStudio et vous repérer dans les quatre panneaux.
  • Travailler dans un projet RStudio et construire des chemins de fichiers avec here::here().
  • Créer des objets, appeler des fonctions et lire une page d’aide.
  • Savoir ce que R garde en mémoire, et vérifier qu’un script tourne seul après un redémarrage.
  • Distinguer l’installation d’un package de son chargement, lire les trois messages d’erreur les plus courants, distinguer une erreur d’un avertissement, et poser une question à laquelle on peut répondre.
  • Manipuler des vecteurs (y compris les valeurs manquantes) et des data frames avec R de base.
  • Calculer le PIB par habitant de trois pays : la première étape de notre fil rouge.

Préparer votre session R

Avez-vous suivi les étapes « Avant la première séance » de la page d’accueil ? Sinon, faites-les d’abord : le bloc ci-dessous a besoin des packages et du fichier R/theme_cours.R qu’elles installent.

library(tidyverse)
library(here)
library(scales)
source(here("R", "theme_cours.R"))
Sys.setenv(LANGUAGE = "en")  # messages d'erreur en anglais

Script de la séance : télécharger seance-1.R, tout le code de cette page dans un seul fichier.

Rythme. Le parcours principal dure environ 200 minutes, exercices compris. L’exercice 1.7 est Facultatif : pour aller plus loin ; les exercices 1.3, 1.4, 1.8 et 1.9 sont à faire à la maison.

Que sont R et RStudio ?

≈ 15 min À la fin de cette section, vous saurez dire ce que sont R et RStudio, et pourquoi nous les utilisons.

R est un langage de programmation pour les statistiques et l’analyse de données. RStudio (désormais édité par Posit) est l’éditeur que nous utilisons pour écrire et exécuter du code R. R est le moteur, RStudio est le tableau de bord.

NoteEn clair

R est le moteur d’une voiture, RStudio est le tableau de bord et le volant. On peut conduire sans tableau de bord, mais on n’en a pas envie.

Pourquoi R pour un économiste ?

  • Gratuit et open source : votre code tourne de la même façon sur n’importe quelle machine, aujourd’hui et dans cinq ans.
  • Packages : des milliers d’extensions sur le CRAN (l’archive officielle) pour la manipulation de données, l’économétrie, les cartes, l’analyse de texte.
  • Reproductible : le code, les résultats et le texte vivent dans un seul document (Quarto, séance 6).
  • Très utilisé dans le monde académique, les banques centrales et les organisations internationales, avec une large communauté.

Installation

Installez dans cet ordre : d’abord R depuis le CRAN, puis RStudio Desktop depuis Posit. Le chapitre Starting with R and RStudio (en anglais) de Hands-On Programming with R détaille les deux installations avec des captures d’écran.

Les quatre panneaux

Capture d'écran de RStudio avec ses quatre panneaux annotés : éditeur de script en haut à gauche, console en bas à gauche, environnement et historique en haut à droite, fichiers, graphiques et aide en bas à droite.

Les quatre panneaux de RStudio. Source : documentation de Posit, panneaux de RStudio (en anglais).
Panneau À quoi il sert
Source (en haut à gauche) Vos scripts (fichiers .R). C’est là que vous écrivez le code que vous voulez conserver.
Console (en bas à gauche) L’endroit où R exécute le code et affiche les résultats. Pratique pour les tests rapides.
Environment (en haut à droite) Les objets actuellement en mémoire.
Files / Plots / Packages / Help (en bas à droite) Explorateur de fichiers, graphiques, packages installés, pages d’aide.

Projets RStudio et chemins de fichiers

≈ 15 min Après cette section, vous saurez mettre en place un dossier de projet dans lequel chaque chemin fonctionne sur n’importe quel ordinateur.

Faites-le dès le premier jour, cela vous fera gagner des heures plus tard.

Un projet RStudio est un dossier contenant un fichier .Rproj. Quand vous l’ouvrez, RStudio fixe le répertoire de travail sur ce dossier, de sorte que tout chemin peut s’écrire relativement au projet.

  1. File > New Project > New Directory > New Project. Nommez-le r-course et choisissez son emplacement.
  2. Décompressez data.zip (page d’accueil) dans le dossier du projet. Cela crée data/ (tous les fichiers de données y vont), data/raw/ et R/theme_cours.R.
  3. Créez un script : File > New File > R Script, enregistrez-le sous session1.R dans le dossier du projet.

Votre dossier de projet ressemble maintenant à ceci :

r-course/
├── r-course.Rproj
├── session1.R
├── data/          # les données du cours (de data.zip)
│   └── raw/       # fichiers originaux, en lecture seule
├── R/
│   └── theme_cours.R   # thème graphique commun (de data.zip)
└── output/        # tout ce que vous produisez (créé en séance 2)

Pourquoi here::here() et jamais setwd()

Beaucoup de tutoriels commencent leurs scripts par setwd("/Users/john/Desktop/course"). Ne le faites jamais. Ce chemin n’existe que sur votre propre ordinateur : le script plante sur toute autre machine (celle de votre collègue, de votre directeur de mémoire, la vôtre dans six mois). N’écrivez jamais setwd() avec un chemin de votre propre ordinateur dans un script que vous partagez.

À la place, le package here construit les chemins à partir de la racine du projet, où que se trouve celui-ci :

# Construit "<racine du projet>/data/wdi_panel.csv" sur n'importe quel ordinateur
here::here("data", "wdi_panel.csv")

# Utilisé dans une fonction de lecture (séance 2)
wdi <- read_csv(here::here("data", "wdi_panel.csv"))
ImportantIdée clé

Un projet par cours ou par article, les données dans data/, les chemins construits avec here::here(). Votre code tourne alors sur n’importe quel ordinateur sans modifier un seul chemin.

AvertissementErreur fréquente

Ouvrir un fichier .R en double-cliquant dessus, en dehors du projet. Le répertoire de travail est alors faux et here() ne trouve pas data/. Ouvrez toujours d’abord le fichier .Rproj (le nom du projet s’affiche en haut à droite de RStudio).

Scripts, console et commentaires

≈ 10 min Après cette section, vous saurez écrire, exécuter et commenter du code dans un script.

Vous pouvez taper dans la console, mais ce que vous y tapez est perdu. Le code que vous voulez garder va dans un script.

flowchart LR
    A["Script : écrire et enregistrer le code"] -->|"Cmd/Ctrl + Enter"| B["Console : R l'exécute"]
    B --> C["Environment : objets en mémoire"]
    B --> D["Plots / Files : sorties"]

  • Écrivez le code dans le script, puis exécutez la ligne courante (ou les lignes sélectionnées) avec Ctrl + Enter (Windows/Linux) ou Cmd + Enter (Mac). R l’envoie à la console.
  • Exécutez tout le script avec Ctrl/Cmd + Shift + Enter.
  • Tout ce qui suit # sur une ligne est un commentaire : R l’ignore. Commentez le pourquoi, pas l’évident.
# Un commentaire : R ignore cette ligne
2 + 2   # un commentaire peut aussi suivre du code
[1] 4
ImportantIdée clé

Le script est votre production ; la console est un brouillon. Si ce n’est pas dans le script, cela n’existe plus demain.

Une bonne habitude : un script doit pouvoir s’exécuter de haut en bas dans une session R neuve et donner les mêmes résultats. Si ce n’est pas le cas, quelque chose est caché dans votre environnement (Ce que R retient montre comment le vérifier).

Objets, types et opérateurs

≈ 30 min Après cette section, vous saurez ranger n’importe quoi sous un nom, le retrouver, distinguer les types et utiliser les opérateurs.

R est un langage fondé sur les objets

Tout ce que vous manipulez dans R vit dans un objet : un nom qui pointe vers une valeur stockée dans la mémoire de l’ordinateur. Deux gestes font tout le travail :

  • name <- value stocke la valeur sous le nom (la flèche d’assignation <-, raccourci Alt + - sous Windows, Option + - sous Mac). Rien ne s’affiche.
  • Taper le nom seul rappelle la valeur et l’affiche.

Vous pouvez ranger n’importe quoi sous un nom : un nombre, un texte, un vecteur, un tableau entier, le résultat d’un calcul, et plus tard un modèle de régression (séance 4) ou un graphique (séance 5). Même une fonction est un objet.

gdp_usa <- 20.9                                  # un nombre : PIB des États-Unis, milliers de milliards de US$, 2020
three_countries <- c("USA", "China", "India")    # un vecteur de textes (voir Vecteurs)
gdp_table <- tibble(country = three_countries,
                    gdp_bn  = c(20894, 14688, 2671))  # un tableau entier (voir Data frames)
gdp_sum <- sum(gdp_table$gdp_bn)                 # le résultat d'un calcul
my_mean <- mean                                  # même une fonction

gdp_usa            # taper le nom rappelle la valeur
[1] 20.9
gdp_table
# A tibble: 3 × 2
  country gdp_bn
  <chr>    <dbl>
1 USA      20894
2 China    14688
3 India     2671
my_mean(gdp_table$gdp_bn)   # my_mean fonctionne maintenant exactement comme mean
[1] 12751

flowchart LR
    A["gdp_usa"] -->|"pointe vers"| A2["20.9"]
    B["three_countries"] -->|"pointe vers"| B2["USA, China, India"]
    C["gdp_table"] -->|"pointe vers"| C2["un tableau : 3 lignes, 2 colonnes"]
    D["my_mean"] -->|"pointe vers"| D2["la fonction mean()"]

Chaque objet que vous créez apparaît dans le panneau Environment (en haut à droite), avec un aperçu de sa valeur ; cliquez sur un tableau pour l’ouvrir. ls() affiche la même liste dans la console :

ls()
 [1] "course_lang"     "gdp_sum"         "gdp_table"       "gdp_usa"        
 [5] "my_mean"         "nb"              "nb_dollar"       "nb_pct"         
 [9] "pal_cours"       "pal_regions"     "theme_cours"     "three_countries"

La liste contient aussi des objets que vous n’avez pas tapés : pal_cours, theme_cours, nb et quelques autres ont été créés par source(here("R", "theme_cours.R")) dans le bloc de préparation. theme_cours et nb sont des fonctions, rangées sous un nom comme n’importe quel autre objet.

NoteEn clair

Un objet est une boîte étiquetée. <- met quelque chose dans la boîte et écrit l’étiquette ; taper l’étiquette ouvre la boîte. Une boîte peut contenir un seul nombre ou un jeu de données entier.

AvertissementErreur fréquente

« J’ai exécuté la ligne et il ne s’est rien passé. » Rien ne s’est affiché, mais la valeur a été stockée : regardez dans le panneau Environment, ou tapez le nom.

AstuceExercice 1.1 : ranger et rappeler

Rangez le PIB 2020 de l’Inde, 2671 (milliards de US$), dans gdp_india ; les trois PIB 20894, 14688, 2671 dans un vecteur gdp_three ; et leur total dans gdp_total. (a) Retrouvez les trois objets dans le panneau Environment. (b) Rappelez gdp_total en tapant son nom. (c) Rangez la fonction max sous le nom largest et appliquez-la à gdp_three.

gdp_india <- 2671
gdp_three <- c(20894, 14688, 2671)
gdp_total <- sum(gdp_three)

gdp_total            # (b) rappel par le nom
[1] 38253
largest <- max       # (c) une fonction rangée sous un nouveau nom
largest(gdp_three)
[1] 20894

Types de valeurs

gdp_usa <- 20.9          # numeric (nombre)
country <- "United States"  # character (texte, toujours entre guillemets)
is_rich <- TRUE          # logical (TRUE ou FALSE, sans guillemets)

gdp_usa
[1] 20.9
country
[1] "United States"
is_rich
[1] TRUE

Trois types de base : numeric (nombres), character (texte), logical (TRUE/FALSE). class() vous dit lequel vous avez :

class(gdp_usa)
[1] "numeric"
class(country)
[1] "character"
class(is_rich)
[1] "logical"

Règles de nommage : lettres, chiffres, _ et ., pas d’espaces, ne pas commencer par un chiffre. R est sensible à la casse (gdp et GDP sont deux objets différents). Ne réutilisez pas les noms de fonctions existantes (sum, mean, summary, data) : cela fonctionne jusqu’au jour où cela provoque un bug déroutant. Utilisez des noms descriptifs comme total_gdp.

Opérateurs arithmétiques et logiques

3 + 5      # addition
[1] 8
10 - 4     # soustraction
[1] 6
6 * 7      # multiplication
[1] 42
20 / 4     # division
[1] 5
2^3        # puissance
[1] 8
17 %/% 5   # division entière
[1] 3
17 %% 5    # reste
[1] 2

Les comparaisons renvoient une valeur logique :

3 == 3     # égal (deux signes égal !)
[1] TRUE
5 != 4     # différent
[1] TRUE
7 > 5
[1] TRUE
2 < 6
[1] TRUE
4 >= 4
[1] TRUE

Combinez les conditions avec & (et), | (ou) et ! (non) :

TRUE & FALSE
[1] FALSE
TRUE | FALSE
[1] TRUE
!TRUE
[1] FALSE
AvertissementErreur fréquente

Écrire x = 3 quand on veut dire x == 3. Le premier assigne (et n’affiche rien), le second compare et renvoie TRUE ou FALSE. Utilisez <- pour assigner et gardez = pour les arguments de fonctions.

Fonctions et arguments

Fonction : une recette nommée. Elle prend des entrées (les arguments) entre parenthèses et renvoie un résultat.

Une fonction prend des entrées (arguments) entre parenthèses et renvoie un résultat. Les arguments peuvent être donnés par position ou par nom.

sqrt(16)
[1] 4
round(3.14159, digits = 2)
[1] 3.14
round(3.14159, 2)      # idem, par position
[1] 3.14
mean(c(4, 8, 15, 16, 23, 42))
[1] 18

Vous pouvez stocker un résultat dans un nouvel objet et le réutiliser :

total_gdp <- sum(20.9, 14.7, 2.7)
total_gdp
[1] 38.3

Ce que R retient

≈ 15 min Après cette section, vous saurez distinguer votre script de la mémoire de R, et prouver que votre script tourne seul.

R garde deux choses différentes, et les débutants les confondent sans cesse :

Le script (session1.R) L’environnement (les objets en mémoire)
Où Un fichier sur votre disque La mémoire de R, affichée dans le panneau Environment
Survit à la fermeture de RStudio ? Oui, une fois enregistré (Ctrl/Cmd + S) Non : on le reconstruit en exécutant le script
Change quand Vous tapez et enregistrez Vous exécutez une ligne

Trois conséquences :

  • Modifier une ligne du script ne change pas l’objet tant que vous n’exécutez pas cette ligne à nouveau.
  • Supprimer une ligne ne supprime pas l’objet : il reste en mémoire jusqu’au redémarrage de R.
  • Un objet créé dans la console est en mémoire mais dans aucun fichier. Demain, il a disparu.

R stocke une valeur, pas une formule. Un objet calculé à partir d’un autre ne se met pas à jour quand le premier change :

pop_india <- 1380                     # millions, arrondi 2020
pop_three <- 331 + 1411 + pop_india   # calculé une fois, à partir de la valeur 1380
pop_india <- 1400                     # on modifie pop_india...
pop_three                             # ...pop_three contient toujours l'ancien total
[1] 3122

Un tableur recalculerait pop_three ; R ne le fait pas. Pour le mettre à jour, exécutez à nouveau sa ligne.

Redémarrer R et tout relancer

La seule preuve que votre script est complet, c’est de l’exécuter dans une mémoire vide :

  1. Session > Restart R (Ctrl/Cmd + Shift + F10). L’environnement est vidé ; le script n’est pas touché.
  2. Exécutez tout le script : Ctrl/Cmd + Shift + Enter, ou le bouton Source au-dessus du script.
  3. S’il va jusqu’au bout et donne les mêmes résultats, votre script fonctionne seul. S’il s’arrête, vous avez trouvé quelque chose qui n’existait qu’en mémoire.

Faites-le avant d’envoyer un script à qui que ce soit, et chaque fois que des résultats vous semblent étranges.

Faites démarrer RStudio à vide à chaque fois. Dans Tools > Global Options > General :

  • décochez Restore .RData into workspace at startup ;
  • réglez Save workspace to .RData on exit sur Never.

Sinon, RStudio recharge discrètement les objets de la veille, et un script cassé peut sembler fonctionner.

ImportantIdée clé

Le script fait foi ; l’environnement n’en est qu’une copie temporaire. Tout ce dont vous aurez besoin demain doit être écrit dans le script.

AstuceExercice 1.2 : pourquoi ça marchait hier ?

Hier, Amina a testé une valeur dans la console en tapant pop_mn <- c(331, 1411, 1380). Puis elle a écrit ce script, l’a exécuté et a obtenu trois PIB par habitant :

gdp_bn <- c(20894, 14688, 2671)          # PIB, milliards de US$, 2020
gdp_pc <- gdp_bn * 1000 / pop_mn         # PIB par habitant, US$
gdp_pc

Reproduisez sa journée : tapez sa ligne dans la console, mettez les trois lignes dans votre script et exécutez-les. Redémarrez ensuite R et exécutez à nouveau le script. Que se passe-t-il, pourquoi, et comment corriger le script ?

Après le redémarrage, la mémoire est vide et le script s’arrête :

gdp_bn <- c(20894, 14688, 2671)          # PIB, milliards de US$, 2020
gdp_pc <- gdp_bn * 1000 / pop_mn         # PIB par habitant, US$
Error:
! object 'pop_mn' not found

pop_mn n’a jamais été créé que dans la console : il vivait en mémoire, pas dans le script. Hier, cela fonctionnait parce que l’objet était encore là. La correction consiste à écrire la ligne manquante dans le script, au-dessus de la ligne qui l’utilise :

gdp_bn <- c(20894, 14688, 2671)          # PIB, milliards de US$, 2020
pop_mn <- c(331, 1411, 1380)             # population, millions, 2020
gdp_pc <- gdp_bn * 1000 / pop_mn         # PIB par habitant, US$
gdp_pc
[1] 63123.867 10409.639  1935.507

Redémarrez R et exécutez le script une fois de plus : il fonctionne maintenant seul.

Obtenir de l’aide

≈ 15 min Après cette section, vous saurez trouver et lire la page d’aide de n’importe quelle fonction, et poser une question à laquelle on peut répondre.

Personne ne retient toutes les fonctions. Savoir trouver de l’aide est la vraie compétence.

  • ?mean ou help("mean") ouvre la page d’aide d’une fonction (dans le panneau Help).
  • ??regression cherche un mot dans toutes les pages d’aide installées.
  • Les vignettes et les sites web des packages (p. ex. tidyverse.org, en anglais) donnent des exemples travaillés.
  • Cherchez le message d’erreur exact en ligne : quelqu’un l’a déjà rencontré.
?mean
help("round")

Comment lire une page d’aide, dans cet ordre :

  1. Description : ce que fait la fonction, en une ligne.
  2. Usage : les arguments et leurs valeurs par défaut. mean(x, trim = 0, na.rm = FALSE, ...) vous indique que na.rm vaut FALSE sauf si vous le changez.
  3. Arguments : ce que signifie chacun d’eux.
  4. Value : ce que vous récupérez.
  5. Examples en bas de page : copiez-les dans votre console et exécutez-les. C’est souvent le moyen le plus rapide de comprendre.
NoteUtiliser les assistants d’IA

ChatGPT, Claude, Copilot et les outils similaires sont utiles pour expliquer un message d’erreur ou vous rappeler la syntaxe d’une fonction. Trois règles :

  • Exécutez toujours le code et vérifiez qu’il fait ce que vous avez demandé ; le code produit par une IA peut être faux ou utiliser des fonctions obsolètes.
  • Si vous ne savez pas expliquer une ligne, ne la gardez pas. Vous êtes responsable de chaque résultat que vous rendez.
  • Ne collez jamais de données confidentielles ou personnelles dans un outil public.

Apprenez d’abord les bases : on ne peut pas vérifier une réponse qu’on ne comprend pas.

Poser une question à laquelle on peut répondre

Que vous demandiez à l’enseignant, à un camarade ou à un assistant d’IA, la réponse ne peut pas être meilleure que la question. Une bonne question donne quatre choses :

  1. Ce que vous voulez faire, en une phrase.
  2. Le plus petit morceau de code qui échoue : retirez tout ce qui n’est pas nécessaire pour voir le problème. Redémarrez d’abord R et vérifiez que l’échec se produit toujours.
  3. Le message exact, copié-collé, ni retapé ni résumé.
  4. Ce que vous attendiez, et ce que vous avez déjà essayé.

Comparez :

  • Mauvaise : « Mon code sur le PIB ne marche pas, au secours ! » Personne ne peut répondre sans vous poser quatre questions en retour.
  • Bonne : « Je veux le PIB par habitant de trois pays. Dans une session neuve, gdp_pc <- gdp_bn * 1000 / pop_mn renvoie Error: object 'pop_mn' not found. J’attendais trois nombres. J’ai vérifié l’orthographe de pop_mn. Qu’est-ce qui m’échappe ? »
NoteEn clair

Souvent, réduire votre code au plus petit exemple qui échoue vous montre le problème avant même d’envoyer la question. Ce n’est pas du temps perdu : c’est ainsi que les utilisateurs expérimentés déboguent.

Packages : installer une fois, charger à chaque fois

≈ 10 min Après cette section, vous saurez distinguer l’installation d’un package de son chargement.

R de base est petit. L’essentiel de ce que nous utilisons vient de packages. Deux étapes distinctes :

  1. Installer (une fois par ordinateur) : télécharger le package depuis le CRAN. Tapez ceci dans la console, jamais dans un script que vous partagez.
  2. Charger (à chaque session) : library() rend ses fonctions disponibles. Placez les appels à library() en haut de votre script.
# Étape 1, une seule fois, dans la console : tous les packages utilisés dans ce cours
install.packages(c(
  "tidyverse", "here", "janitor", "readxl", "writexl", "lubridate", "scales",
  "WDI", "gt", "fixest", "modelsummary", "ggrepel", "sf", "rnaturalearth",
  "rnaturalearthdata", "patchwork", "plotly", "crosstalk", "shiny"
))

# Étape 2, en haut de chaque script
library(tidyverse)
NoteEn clair

Installer un package, c’est acheter un livre pour votre étagère (une fois). Le charger avec library(), c’est le prendre sur l’étagère et l’ouvrir (à chaque fois que vous travaillez).

AvertissementErreur fréquente

Mettre install.packages() en haut d’un script. Cela retélécharge le package à chaque exécution et plante sur la machine d’un collègue. Installez dans la console, chargez dans le script.

Installez-les tous maintenant (quelques minutes). Chaque séance suivante commence par une ligne « Packages utilisés aujourd’hui » qui vous dit quoi installer s’il manque quelque chose.

tidyverse est un ensemble de packages (ggplot2, dplyr, tibble, readr, …) que nous utilisons tout au long du cours. Pour les packages que nous n’utilisons qu’une fois, package::function() (comme dans here::here()) appelle une fonction sans charger tout le package.

Lire les messages d’erreur

≈ 20 min Après cette section, vous saurez lire les trois messages d’erreur les plus courants et les corriger, distinguer une erreur d’un avertissement ou d’un message, et sortir de l’invite +.

Vous aurez des erreurs tous les jours, y compris dans dix ans. Une erreur n’est pas un échec : R vous dit ce qu’il n’a pas pu faire. Lisez le message, puis regardez la ligne qu’il désigne. Voici les trois que vous rencontrerez le plus.

1. object 'xxx' not found (« objet introuvable ») : vous utilisez un nom qui n’existe pas. Faute de frappe, mauvaise casse, ou vous avez oublié d’exécuter la ligne qui le crée.

gdp_china <- 14.7
gdp_chinaa + 1      # faute de frappe dans le nom
Error:
! object 'gdp_chinaa' not found

2. unexpected symbol / unexpected numeric constant / unexpected ',' (« symbole inattendu ») : la syntaxe est incorrecte, souvent une virgule, un guillemet ou une parenthèse manquants. R vous dit quel élément il n’attendait pas (ici le 2), donc l’erreur se trouve juste avant : la virgule manquante après le 1. Dans la console, le message s’écrit unexpected numeric constant in "c(1 2" ; cette page le met en forme différemment.

c(1 2 3)            # virgules manquantes
Error in parse(text = input): <text>:1:5: unexpected numeric constant
1: c(1 2
        ^

3. could not find function "xxx" (« fonction introuvable ») : le nom de la fonction est mal orthographié, ou son package n’est pas chargé. Ici feols() vient de fixest, que nous n’avons pas chargé avec library().

feols(gdp ~ year, data = tibble(gdp = 1:3, year = 1:3))
Error in `feols()`:
! could not find function "feols"
ImportantIdée clé

Un message d’erreur est une information, pas un verdict. Il nomme l’objet, le symbole ou la fonction sur lequel R a buté ; commencez par là.

Méthode : (1) lisez le message à voix haute, (2) trouvez la ligne, (3) vérifiez l’orthographe, les virgules, les guillemets et les parenthèses, (4) vérifiez que vous avez exécuté les lignes précédentes et chargé le package, (5) seulement ensuite, cherchez en ligne ou demandez à un assistant d’IA en collant le message exact (voir Poser une question à laquelle on peut répondre).

Erreur, avertissement ou message ?

R vous parle de trois façons, qui ne veulent pas dire la même chose.

Un avertissement (warning) signifie que R est allé au bout, mais que quelque chose semble anormal. Ici, la population de la Chine a été stockée comme texte avec une virgule des milliers ; R ne peut pas lire "1,411" comme un nombre :

pop_text <- c("331", "1,411", "1380")   # des nombres stockés comme texte
as.numeric(pop_text)
Warning: NAs introduced by coercion
[1]  331   NA 1380

Vous avez obtenu un résultat, mais il contient un NA (une valeur manquante, voir Vecteurs) à la place de la Chine. N’ignorez jamais un avertissement : cherchez ce qu’il signale.

Un message est une simple information. read_csv() (séance 2) vous dit ce qu’il a lu :

gdp_3 <- read_csv(here("data", "wdi_3countries.csv"))
Rows: 63 Columns: 4
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (2): country, iso3c
dbl (2): year, gdp

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
R affiche Commence par R est-il allé au bout ? Ce que vous faites
Erreur Error Non : rien n’a été produit Corrigez le code, puis relancez-le
Avertissement Warning Oui, mais vérifiez le résultat Cherchez ce que signale l’avertissement
Message (n’importe quoi) Oui Lisez-le une fois ; souvent, rien à faire

L’invite + : R vous attend

La console affiche normalement >, prête pour une nouvelle commande. Si elle affiche +, R pense que votre expression n’est pas terminée : une parenthèse ou un guillemet est encore ouvert, alors il attend la suite.

> mean(c(4, 8, 15)
+
> country <- "India
+

La première ligne oublie une ), la seconde un " fermant. Appuyez sur Échap (Escape) pour revenir à >, corrigez la ligne dans votre script et exécutez-la à nouveau.

Vecteurs

≈ 25 min Après cette section, vous saurez construire des vecteurs, les indexer et calculer avec eux, y compris avec des valeurs manquantes.

Vecteur : une liste ordonnée de valeurs du même type. C’est la brique de base de R : une colonne d’un jeu de données est un vecteur.

population <- c(331, 1411, 1380)             # numeric (nombres)
countries  <- c("USA", "China", "India")     # character (texte)
high_income <- c(TRUE, FALSE, FALSE)         # logical (vrai/faux)

Séquences et répétitions :

1:10                      # entiers de 1 à 10
 [1]  1  2  3  4  5  6  7  8  9 10
seq(1, 10, by = 2)        # pas de 2
[1] 1 3 5 7 9
seq(0, 1, length.out = 5) # 5 valeurs régulièrement espacées
[1] 0.00 0.25 0.50 0.75 1.00
rep(5, times = 3)
[1] 5 5 5
rep(c("a", "b"), each = 2)
[1] "a" "a" "b" "b"

Indexation

Les crochets sélectionnent des éléments par position (en commençant à 1) ou par condition :

population[2]                # deuxième élément
[1] 1411
population[c(1, 3)]          # premier et troisième
[1]  331 1380
population[-1]               # tous sauf le premier
[1] 1411 1380
population[population > 1000]  # éléments qui satisfont une condition
[1] 1411 1380
countries[population > 1000]   # un vecteur sert à filtrer un autre
[1] "China" "India"

Opérations vectorisées

Les opérations s’appliquent à tous les éléments d’un coup, sans boucle.

population + 2
[1]  333 1413 1382
population * 1000000         # des millions aux personnes
[1] 3.310e+08 1.411e+09 1.380e+09
population / sum(population) # part du total
[1] 0.1060218 0.4519539 0.4420243
Afficher le code du graphique (vu en séance 5)
tibble(country = countries, population = population,
       share = 100 * population / sum(population)) |>
  mutate(country = fct_reorder(country, share)) |>
  ggplot(aes(x = share, y = country)) +
  geom_col(fill = pal_cours[["principal"]]) +
  geom_text(aes(label = paste0(round(share), " %")), hjust = -0.2) +
  scale_x_continuous(limits = c(0, 55)) +
  labs(
    title = "La Chine et l'Inde concentrent 89 % de la population des trois pays",
    x = "Part de la population totale des trois pays (%)",
    y = NULL,
    caption = "Source : valeurs 2020 arrondies d'une version antérieure des WDI (Banque mondiale), à visée pédagogique."
  )
Diagramme en barres horizontales de la part de chaque pays dans la population totale de trois pays, en pourcentage. La Chine et l'Inde ont les deux barres les plus longues, 89 % à elles deux ; le troisième pays a une barre courte.
Figure 1
NoteEn clair

Une opération vectorisée, c’est une photocopieuse avec un tampon : une seule instruction (/ sum(population)) est appliquée à toutes les feuilles d’un coup.

Valeurs manquantes

NA signifie « valeur non disponible ». Ce n’est pas zéro. Tout calcul impliquant NA renvoie NA, car R ne peut pas deviner la valeur manquante.

gini <- c(41.5, 38.2, NA, 35.7)
mean(gini)
[1] NA
mean(gini, na.rm = TRUE)   # na.rm = TRUE : retirer les NA avant le calcul
[1] 38.46667
is.na(gini)
[1] FALSE FALSE  TRUE FALSE
sum(is.na(gini))           # nombre de valeurs manquantes
[1] 1
AvertissementErreur fréquente

Utiliser na.rm = TRUE par réflexe. Demandez-vous pourquoi la valeur est manquante et si l’écarter biaise le résultat. Dans les WDI, le Gini manque pour beaucoup de pays pauvres, comme le montre le graphique.

Afficher le code du graphique (vu en séance 5)
read_csv(here("data", "wdi_panel.csv"), show_col_types = FALSE) |>
  filter(year == 2019, !is.na(income)) |>
  summarise(share_na = 100 * mean(is.na(gini)), .by = income) |>
  mutate(income = factor(income, levels = c("Low income", "Lower middle income",
                                            "Upper middle income", "High income"))) |>
  ggplot(aes(x = income, y = share_na)) +
  geom_col(fill = pal_cours[["accent"]]) +
  geom_text(aes(label = paste0(round(share_na), " %")), vjust = -0.4) +
  scale_y_continuous(limits = c(0, 100)) +
  labs(
    title = "Le Gini manque pour la plupart des pays une année donnée",
    x = "Groupe de revenu de la Banque mondiale (classification actuelle)",
    y = "Pays dont le Gini est manquant, 2019 (%)",
    caption = "Source : Banque mondiale, World Development Indicators (WDI), 2019."
  )
Diagramme en barres du pourcentage de pays dont le Gini est manquant en 2019, pour quatre groupes de revenu de la Banque mondiale, du plus faible au plus élevé. Le Gini manque pour la plupart des pays de chaque groupe.
Figure 2
ImportantPourquoi c’est important en économie du développement

Les données d’inégalités sont les plus rares là où elles comptent le plus. Faire la moyenne des seuls pays qui déclarent un Gini décrit les pays qui mesurent l’inégalité, pas le monde.

Data frames et tibbles

≈ 25 min Après cette section, vous saurez construire un petit tableau, l’explorer et calculer une nouvelle colonne.

Data frame (tableau de données) : un tableau dont chaque colonne est un vecteur, tous de même longueur.

Un tibble est la version tidyverse d’un data frame, avec un affichage plus lisible. Nous utiliserons des tibbles tout au long du cours.

Notre fil rouge commence ici : trois pays, avec des valeurs 2020 arrondies (PIB en milliards de dollars US courants, population en millions ; chiffres approximatifs à visée pédagogique, issus d’une version antérieure des WDI de la Banque mondiale). La version actuelle, utilisée à partir de la séance 2, donne des chiffres légèrement différents : la Banque mondiale révise ses séries.

countries3 <- tibble(
  country    = c("United States", "China", "India"),
  gdp_bn     = c(20894, 14688, 2671),   # PIB, milliards de US$ courants, arrondi 2020
  pop_mn     = c(331, 1411, 1380),      # population, millions, arrondi 2020
  high_income = c(TRUE, FALSE, FALSE)
)
countries3
# A tibble: 3 × 4
  country       gdp_bn pop_mn high_income
  <chr>          <dbl>  <dbl> <lgl>      
1 United States  20894    331 TRUE       
2 China          14688   1411 FALSE      
3 India           2671   1380 FALSE      

Explorer un data frame

nrow(countries3)     # nombre de lignes
[1] 3
ncol(countries3)     # nombre de colonnes
[1] 4
names(countries3)    # noms des colonnes
[1] "country"     "gdp_bn"      "pop_mn"      "high_income"
str(countries3)      # structure : type de chaque colonne
tibble [3 × 4] (S3: tbl_df/tbl/data.frame)
 $ country    : chr [1:3] "United States" "China" "India"
 $ gdp_bn     : num [1:3] 20894 14688 2671
 $ pop_mn     : num [1:3] 331 1411 1380
 $ high_income: logi [1:3] TRUE FALSE FALSE
summary(countries3)  # statistiques rapides par colonne
      country       gdp_bn          pop_mn       high_income    
 Length   : 3   Min.   : 2671   Min.   : 331.0   Mode :logical  
 N.unique : 3   1st Qu.: 8680   1st Qu.: 855.5   FALSE:2        
 N.blank  : 0   Median :14688   Median :1380.0   TRUE :1        
 Min.nchar: 5   Mean   :12751   Mean   :1040.7                  
 Max.nchar:13   3rd Qu.:17791   3rd Qu.:1395.5                  
                Max.   :20894   Max.   :1411.0                  
head(countries3, 2)  # 2 premières lignes
# A tibble: 2 × 4
  country       gdp_bn pop_mn high_income
  <chr>          <dbl>  <dbl> <lgl>      
1 United States  20894    331 TRUE       
2 China          14688   1411 FALSE      

Sélectionner des colonnes et des lignes

countries3$gdp_bn          # une colonne (un vecteur) avec $
[1] 20894 14688  2671
countries3[1, ]            # première ligne, toutes les colonnes
# A tibble: 1 × 4
  country       gdp_bn pop_mn high_income
  <chr>          <dbl>  <dbl> <lgl>      
1 United States  20894    331 TRUE       
countries3[ , "country"]   # toutes les lignes, une colonne
# A tibble: 3 × 1
  country      
  <chr>        
1 United States
2 China        
3 India        
countries3[2, "pop_mn"]    # une cellule
# A tibble: 1 × 1
  pop_mn
   <dbl>
1   1411
countries3[countries3$pop_mn > 1380, ]  # lignes satisfaisant une condition
# A tibble: 1 × 4
  country gdp_bn pop_mn high_income
  <chr>    <dbl>  <dbl> <lgl>      
1 China    14688   1411 FALSE      

Le schéma est data[lignes, colonnes]. Laisser un côté vide signifie « tout ».

NoteEn clair

Pensez à un tableur : data[2, 3] signifie « ligne 2, colonne 3 ». La virgule sépare les lignes (avant) des colonnes (après).

Ajouter une colonne

countries3$gdp_pc <- countries3$gdp_bn * 1000 / countries3$pop_mn
countries3
# A tibble: 3 × 5
  country       gdp_bn pop_mn high_income gdp_pc
  <chr>          <dbl>  <dbl> <lgl>        <dbl>
1 United States  20894    331 TRUE        63124.
2 China          14688   1411 FALSE       10410.
3 India           2671   1380 FALSE        1936.

Unités : un PIB en milliards divisé par une population en millions donne des milliers de dollars par personne ; on multiplie donc par 1 000 pour obtenir des US$ par personne.

Trier et filtrer (R de base)

# Trier par PIB par habitant, du plus élevé au plus faible
countries3[order(countries3$gdp_pc, decreasing = TRUE), ]
# A tibble: 3 × 5
  country       gdp_bn pop_mn high_income gdp_pc
  <chr>          <dbl>  <dbl> <lgl>        <dbl>
1 United States  20894    331 TRUE        63124.
2 China          14688   1411 FALSE       10410.
3 India           2671   1380 FALSE        1936.
# Garder uniquement les pays de plus de 1 000 millions d'habitants
countries3[countries3$pop_mn > 1000, ]
# A tibble: 2 × 5
  country gdp_bn pop_mn high_income gdp_pc
  <chr>    <dbl>  <dbl> <lgl>        <dbl>
1 China    14688   1411 FALSE       10410.
2 India     2671   1380 FALSE        1936.

Cela fonctionne mais c’est lourd à écrire. En séance 3, dplyr rendra cela lisible (arrange(), filter(), mutate()).

Fil rouge : le PIB par habitant de trois pays

countries3[order(countries3$gdp_pc, decreasing = TRUE), c("country", "gdp_pc")]
# A tibble: 3 × 2
  country       gdp_pc
  <chr>          <dbl>
1 United States 63124.
2 China         10410.
3 India          1936.

Les États-Unis produisent environ six fois plus par habitant que la Chine et environ trente-trois fois plus que l’Inde (valeurs 2020 arrondies, US$ courants, donc non corrigées des différences de prix entre pays). À partir de la séance 2, nous utilisons le jeu de données complet de la Banque mondiale avec tous les pays.

Afficher le code du graphique (vu en séance 5)
countries3 |>
  mutate(country = fct_reorder(country, gdp_pc)) |>
  ggplot(aes(x = gdp_pc, y = country)) +
  geom_col(fill = pal_cours[["principal"]]) +
  geom_text(aes(label = label_dollar()(round(gdp_pc))), hjust = -0.1) +
  scale_x_continuous(limits = c(0, 80000), labels = label_dollar()) +
  labs(
    title = "Les États-Unis produisent six fois plus par habitant que la Chine",
    x = "PIB par habitant ($ courants, 2020)",
    y = NULL,
    caption = "Source : valeurs 2020 arrondies d'une version antérieure des WDI (Banque mondiale), à visée pédagogique."
  )
Diagramme en barres horizontales du PIB par habitant en dollars courants, 2020, pour trois pays. La barre des États-Unis est bien plus longue que celle de la Chine, environ six fois, et celle de l'Inde est la plus courte.
Figure 3
ImportantIdée clé

PIB par habitant = PIB / population. Le total vous dit la taille d’une économie ; le ratio vous dit combien elle produit par personne. À populations proches, le PIB total de la Chine (5,5 fois celui de l’Inde) se traduit par un écart par habitant de même ampleur.

Le pipe |>

≈ 10 min Après cette section, vous saurez lire et écrire une chaîne d’étapes avec le pipe, et l’exécuter une étape à la fois.

Le pipe |> passe le résultat situé à sa gauche comme premier argument de la fonction située à sa droite. Le code se lit de gauche à droite, comme une recette :

flowchart LR
    A["c(1, 4, 9)"] -->|"puis somme"| B["14"] -->|"puis racine carrée"| C["3.74"]

sqrt(sum(c(1, 4, 9)))          # imbriqué : à lire de l'intérieur vers l'extérieur
[1] 3.741657
c(1, 4, 9) |> sum() |> sqrt()  # avec pipe : « prendre c(1, 4, 9), puis la somme, puis la racine carrée »
[1] 3.741657
NoteEn clair

Lisez |> comme « et ensuite » : prenez le vecteur, et ensuite faites-en la somme, et ensuite prenez la racine carrée.

Même résultat. À partir de la séance 2, la plupart de notre code utilise le pipe (raccourci Ctrl/Cmd + Shift + M).

Quand une chaîne échoue, exécutez-la une étape à la fois. Sélectionnez le code jusqu’à la fin d’une étape (par exemple c(1, 4, 9) |> sum()) et exécutez seulement la sélection avec Ctrl/Cmd + Enter. Regardez le résultat, puis ajoutez l’étape suivante. La première étape dont le résultat vous surprend est celle où se trouve le problème. La séance 3 s’y exerce sur des chaînes plus longues.

c(1, 4, 9)                     # étape 1 : les données
[1] 1 4 9
c(1, 4, 9) |> sum()            # étapes 1 et 2
[1] 14
c(1, 4, 9) |> sum() |> sqrt()  # la chaîne complète
[1] 3.741657

Exercices

≈ 10 min Après ces exercices, vous saurez appliquer seul chaque outil de la séance.

Les exercices 1.1 et 1.2 se trouvent dans les sections précédentes. Ici, faites les exercices 1.5 et 1.6 en classe. Les exercices 1.3 et 1.4 reprennent les objets et les opérateurs : faites-les à la maison. L’exercice 1.7 est Facultatif : pour aller plus loin ; 1.8 et 1.9 sont à faire à la maison.

AstuceExercice 1.3

Créez un objet my_age avec votre âge, un objet my_name avec votre nom, et un objet is_student avec une valeur logique. Affichez les trois, puis vérifiez la classe de chacun.

my_age <- 25
my_name <- "Pierre"
is_student <- TRUE

my_age
[1] 25
my_name
[1] "Pierre"
is_student
[1] TRUE
class(my_age)
[1] "numeric"
class(my_name)
[1] "character"
class(is_student)
[1] "logical"
AstuceExercice 1.4

Créez x <- 12 et y <- 5. Calculez leur somme, leur différence, leur produit et leur quotient. Vérifiez ensuite si x est supérieur à y et si x est divisible par y (indice : le reste %%).

x <- 12
y <- 5

x + y
[1] 17
x - y
[1] 7
x * y
[1] 60
x / y
[1] 2.4
x > y
[1] TRUE
x %% y == 0   # FALSE : le reste est 2
[1] FALSE
AstuceExercice 1.5

Ouvrez la page d’aide de round(). Que fait l’argument digits, et quelle est sa valeur par défaut ? Arrondissez ensuite pi à 3 décimales.

?round

digits est le nombre de décimales ; la valeur par défaut est 0.

round(pi, digits = 3)
[1] 3.142
AstuceExercice 1.6

Créez un vecteur numérique gdp_growth avec les valeurs 2.3, 5.1, NA, 3.8, -0.4 (taux de croissance annuels en %). Calculez sa moyenne avec et sans la valeur manquante, puis sélectionnez les années où la croissance dépasse 2.

gdp_growth <- c(2.3, 5.1, NA, 3.8, -0.4)

mean(gdp_growth)                 # NA
[1] NA
mean(gdp_growth, na.rm = TRUE)   # 2,7
[1] 2.7
# Attention : comparer NA à 2 donne NA, donc on retire d'abord les NA
gdp_growth[!is.na(gdp_growth) & gdp_growth > 2]
[1] 2.3 5.1 3.8
AstuceExercice 1.7 (Facultatif : pour aller plus loin)

Créez le vecteur 1:10 puis, avec une seule opération chacun, (a) le vecteur de ses carrés, (b) uniquement les nombres pairs (indice : %% 2 == 0), (c) les nombres impairs de 1 à 9 avec seq().

v <- 1:10

v^2
 [1]   1   4   9  16  25  36  49  64  81 100
v[v %% 2 == 0]
[1]  2  4  6  8 10
seq(1, 9, by = 2)
[1] 1 3 5 7 9
AstuceExercice 1.8

Créez un data frame students avec trois colonnes name, age, is_student et quatre lignes (inventez-les). Puis : (a) affichez la colonne age, (b) affichez la première ligne, (c) gardez les lignes dont age est strictement supérieur à 20, quel que soit leur statut d’étudiant, (d) triez par âge.

students <- tibble(
  name       = c("Alice", "Bob", "Charlie", "Dalia"),
  age        = c(19, 24, 31, 22),
  is_student = c(TRUE, TRUE, FALSE, TRUE)
)

students$age                       # (a)
[1] 19 24 31 22
students[1, ]                      # (b)
# A tibble: 1 × 3
  name    age is_student
  <chr> <dbl> <lgl>     
1 Alice    19 TRUE      
students[students$age > 20, ]      # (c) condition sur l'âge uniquement
# A tibble: 3 × 3
  name      age is_student
  <chr>   <dbl> <lgl>     
1 Bob        24 TRUE      
2 Charlie    31 FALSE     
3 Dalia      22 TRUE      
students[order(students$age), ]    # (d)
# A tibble: 4 × 3
  name      age is_student
  <chr>   <dbl> <lgl>     
1 Alice      19 TRUE      
2 Dalia      22 TRUE      
3 Bob        24 TRUE      
4 Charlie    31 FALSE     
AstuceExercice 1.9

À partir de countries3, ajoutez une colonne gdp_share égale à la part du PIB de chaque pays dans le PIB total des trois pays, en %. Affichez ensuite les pays triés par gdp_share, du plus élevé au plus faible.

countries3$gdp_share <- 100 * countries3$gdp_bn / sum(countries3$gdp_bn)

countries3[order(countries3$gdp_share, decreasing = TRUE),
           c("country", "gdp_share")]
# A tibble: 3 × 2
  country       gdp_share
  <chr>             <dbl>
1 United States     54.6 
2 China             38.4 
3 India              6.98

À retenir

  • Travaillez dans un projet RStudio, gardez les données dans data/ et construisez les chemins avec here::here(). N’utilisez jamais setwd() avec un chemin de votre propre ordinateur.
  • Écrivez le code dans un script, exécutez-le avec Ctrl/Cmd + Enter et commentez le pourquoi.
  • R est fondé sur les objets : name <- value range n’importe quoi (un nombre, un vecteur, un tableau, un résultat, une fonction) et taper le nom le rappelle. Les trois types de base sont numeric, character et logical. Ne donnez pas à vos objets des noms de fonctions (sum, mean, summary).
  • Le script fait foi, l’environnement n’en est qu’une copie temporaire. Redémarrez R (Ctrl/Cmd + Shift + F10) et relancez tout le script pour prouver qu’il tourne seul ; ne restaurez jamais .RData au démarrage.
  • Installez un package une fois (install.packages(), dans la console), chargez-le dans chaque script (library(), en haut).
  • Lisez les erreurs calmement : object not found (faute de frappe), unexpected symbol (syntaxe), could not find function (faute de frappe ou package non chargé). ?function ouvre la page d’aide.
  • Une erreur arrête R ; un avertissement signifie que R est allé au bout mais que le résultat doit être vérifié ; un message est une information. Un + dans la console signale une ligne inachevée : appuyez sur Échap.
  • Posez des questions auxquelles on peut répondre : l’objectif, le plus petit code qui échoue, le message exact, ce que vous attendiez.
  • Les opérations sur les vecteurs sont vectorisées ; NA est contagieux, utilisez donc na.rm = TRUE en connaissance de cause.
  • Un data frame est un ensemble de vecteurs de même longueur : data$col, data[rows, cols]. PIB par habitant = PIB / population.
  • Le pipe |> transmet le résultat d’une étape à la suivante : x |> f() |> g(). Quand une chaîne échoue, exécutez-la une étape à la fois.

Suite : Séance 2 : Importer et exporter des données.

Retour au sommet