Chargement de packages pour les solutions utilisant des fonctions du tidyverse :

library(dplyr)
library(tidyr)
library(dplyr)
library(tidyr)

Prétraitement de données

Données airquality

Les premières questions vous feront encore manipuler les données stockées dans le data frame airquality, inclus dans le package datasets de l’installation de base de R. Il s’agit d’observations de quantité d’ozone et de radiation solaire dans l’air de New York pour chaque jour des mois de mai à septembre en 1973. Le data frame comprend aussi des mesures de vitesse du vent et de température.

str(airquality)
## 'data.frame':    153 obs. of  6 variables:
##  $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
##  $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
##  $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
##  $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
##  $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...

Question 1

Transformation de variables

Créez une copie du data frame airquality et ajoutez à cette copie une variable contenant le nom anglais du mois. Le vecteur month.name provenant du package base de R, qui contient les noms des mois en anglais en ordre chronologique, peut vous être utile.

Voici un extrait et la structure de ce que vous devez obtenir :

air_quality[c(30, 48, 75, 110, 153), ]
##     Ozone Solar.R Wind Temp Month Day Month.Name
## 30    115     223  5.7   79     5  30        May
## 48     37     284 20.7   72     6  17       June
## 75     NA     291 14.9   91     7  14       July
## 110    23     115  7.4   76     8  18     August
## 153    20     223 11.5   68     9  30  September
str(air_quality)
## 'data.frame':    153 obs. of  7 variables:
##  $ Ozone     : int  41 36 12 18 NA 28 23 19 8 NA ...
##  $ Solar.R   : int  190 118 149 313 NA NA 299 99 19 194 ...
##  $ Wind      : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
##  $ Temp      : int  67 72 74 62 56 66 65 59 61 69 ...
##  $ Month     : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ Day       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Month.Name: chr  "May" "May" "May" "May" ...

Solution :

# Création d'une copie du jeu de données
air_quality <- airquality

# Création et ajout de la nouvelle variable
air_quality$Month.Name <- month.name[air_quality$Month]
# Le résultat d'une extraction avec l'opérateur [ et un vecteur
# de positions fourni en argument (ici air_quality$Month) est 
# de la même longueur que le vecteur de positions. Ce vecteur peut 
# contenir des positions répétées, auquel cas la valeur correspondante
# dans l'objet d'où sont extrait des données (ici month.name) est
# répétée autant de fois.

# Autres façons d'ajouter la nouvelle variable à air_quality
air_quality[["Month.Name"]] <- month.name[air_quality$Month]
# ou encore
air_quality <- cbind(
  air_quality, 
  Month.Name = month.name[air_quality$Month]
)
# ou encore
air_quality <- transform(air_quality, Month.Name = month.name[Month])

Question 2

Manipulation de dates

Ajoutez à votre copie de airquality créée à la question précédente une autre variable contenant la date complète sous la classe Date de R (les données ont été recueillies en 1973).

Voici un extrait et la structure de ce que vous devez obtenir :

air_quality[c(30, 48, 75, 110, 153), ]
##     Ozone Solar.R Wind Temp Month Day Month.Name       Date
## 30    115     223  5.7   79     5  30        May 1973-05-30
## 48     37     284 20.7   72     6  17       June 1973-06-17
## 75     NA     291 14.9   91     7  14       July 1973-07-14
## 110    23     115  7.4   76     8  18     August 1973-08-18
## 153    20     223 11.5   68     9  30  September 1973-09-30
str(air_quality)
## 'data.frame':    153 obs. of  8 variables:
##  $ Ozone     : int  41 36 12 18 NA 28 23 19 8 NA ...
##  $ Solar.R   : int  190 118 149 313 NA NA 299 99 19 194 ...
##  $ Wind      : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
##  $ Temp      : int  67 72 74 62 56 66 65 59 61 69 ...
##  $ Month     : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ Day       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Month.Name: chr  "May" "May" "May" "May" ...
##  $ Date      : Date, format: "1973-05-01" "1973-05-02" "1973-05-03" ...

Solution :

air_quality$Date <- 
  as.Date(
    paste(1973, air_quality$Month, air_quality$Day, sep = "-"),
    format = "%Y-%m-%d" # format par défaut en fait
  )

Question 3

Transformation de variables

Ajoutez à votre copie de airquality créée à la question 1 une autre variable contenant les observations catégorisées de la variable Wind :

  • pour une valeur de Wind inférieure ou égale à 6, la catégorie associée doit être "light";
  • pour une valeur de Wind supérieure à 6, mais inférieure ou égale à 12, la catégorie associée doit être "moderate";
  • pour une valeur de Wind supérieure à 12, mais inférieure ou égale à 18, la catégorie associée doit être "strong";
  • toutes les valeurs supérieures à 18 doivent être transformées en données manquantes, car nous considérons ces observations extrêmes et désirons les ignorer dans des calculs éventuels.

Voici un extrait et la structure de ce que vous devez obtenir :

air_quality[c(30, 48, 75, 110, 153), ]
##     Ozone Solar.R Wind Temp Month Day Month.Name       Date Wind.Catego
## 30    115     223  5.7   79     5  30        May 1973-05-30       light
## 48     37     284 20.7   72     6  17       June 1973-06-17        <NA>
## 75     NA     291 14.9   91     7  14       July 1973-07-14      strong
## 110    23     115  7.4   76     8  18     August 1973-08-18    moderate
## 153    20     223 11.5   68     9  30  September 1973-09-30    moderate
str(air_quality)
## 'data.frame':    153 obs. of  9 variables:
##  $ Ozone      : int  41 36 12 18 NA 28 23 19 8 NA ...
##  $ Solar.R    : int  190 118 149 313 NA NA 299 99 19 194 ...
##  $ Wind       : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
##  $ Temp       : int  67 72 74 62 56 66 65 59 61 69 ...
##  $ Month      : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ Day        : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Month.Name : chr  "May" "May" "May" "May" ...
##  $ Date       : Date, format: "1973-05-01" "1973-05-02" "1973-05-03" ...
##  $ Wind.Catego: Factor w/ 3 levels "light","moderate",..: 2 2 3 2 3 3 2 3 NA 2 ...

Solution :

# ---- Solution avec cut ----
air_quality$Wind.Catego <- 
  cut(
    x = air_quality$Wind, 
    breaks = c(-Inf, 6, 12, 18, Inf),
    labels = c("light", "moderate", "strong", "extreme"),
    right = TRUE
  )
# Pourquoi ne pas avoir mis NA comme dernière modalité ?
# Parce que NA serait alors compris comme une modalité de la variable,
# et non comme une donnée manquante.
# Mais il faut maintenant remplacer les valeurs égales à la dernière 
# modalité par NA.
air_quality$Wind.Catego[air_quality$Wind.Catego == "extreme"] <- NA
# Et il faut laisser tomber le niveau "extreme".
air_quality$Wind.Catego <- droplevels(air_quality$Wind.Catego)

# ---- Solution avec ifelse ----
air_quality$Wind.Catego2 <- ifelse(
  test = air_quality$Wind <= 6, 
  yes = "light",
  no = ifelse(
    test = air_quality$Wind > 6 & air_quality$Wind <= 12, 
    yes = "moderate",
    no = ifelse(
      test = air_quality$Wind > 12 & air_quality$Wind <= 18,
      yes = "strong",
      no = NA
    )
  )
)
# Si nous voulons un facteur, comme ce que retourne la fonction cut :
air_quality$Wind.Catego2 <- as.factor(air_quality$Wind.Catego)

# ---- Solution avec [ et <- ----
# Initialisation d'une variable vide
air_quality$Wind.Catego3 <- vector(
  mode = "character",
  length = nrow(air_quality)
)
# Ensuite correctement remplie
air_quality$Wind.Catego3[air_quality$Wind <= 6] <- "light"
air_quality$Wind.Catego3[air_quality$Wind > 6 & air_quality$Wind <= 12] <- "moderate"
air_quality$Wind.Catego3[air_quality$Wind > 12 & air_quality$Wind <= 18] <- "strong"
air_quality$Wind.Catego3[air_quality$Wind > 18] <- NA
# Si nous voulons un facteur, comme ce que retourne la fonction cut :
air_quality$Wind.Catego3 <- as.factor(air_quality$Wind.Catego)

# ---- Solution avec le tidyverse ----
air_quality <- mutate(
  air_quality, 
  Wind.Catego4 = factor(  # pour avoir un facteur, comme avec cut
    case_when(
      Wind <= 6 ~ "light",
      Wind > 6 & Wind <= 12 ~ "moderate",
      Wind > 12 & Wind <= 18 ~ "strong",
      Wind > 18 ~ NA_character_   # Seulement NA ne fonctionne pas
    )
  )
)
# Référence : https://dplyr.tidyverse.org/reference/case_when.html
# La fiche d'aide suivante mentionne l'existence de versions de NA possédant 
# un type spécifique : NA_integer_, NA_real_, NA_complex_ et NA_character_
help(NA)

# ---- Comparaison des solutions ----
# Est-ce que les 4 solutions donnent vraiment le même résultat ?
identical(air_quality$Wind.Catego, air_quality$Wind.Catego2)
# [1] TRUE
identical(air_quality$Wind.Catego, air_quality$Wind.Catego3)
# [1] TRUE
identical(air_quality$Wind.Catego, air_quality$Wind.Catego4)
# [1] TRUE

# Oui. Retirons donc les variables en double.
air_quality$Wind.Catego2 <- NULL
air_quality$Wind.Catego3 <- NULL
air_quality$Wind.Catego4 <- NULL

Question 4

Transformation de variables

Retour sur la question 2 de la semaine dernière

Les données airquality comprennent des données manquantes. Si nous voulions traiter ces données, il faudrait décider quoi faire avec les données manquantes. La semaine dernière, nous avons fait l’exercice de les omettre. Une autre option pour traiter ces données serait de les remplacer par des valeurs imputées.

Nous pourrions remplacer les données manquantes par la valeur moyenne de la variable en question pour le mois correspondant. Ce type d’imputation par une valeur moyenne est rarement la meilleure façon de faire, d’un point de vue théorique. C’est cependant un bon exercice pour nous.

Procédez donc à ce remplacement, dans la copie du jeu de données créée à la question 1.

Par exemple, la valeur moyenne d’Ozone au mois de mai, en omettant les données manquantes, est 23.6153846. Ainsi, les données manquantes dans la colonne 1 aux lignes 5, 10, 25, 26 et 27 devraient être remplacées par 23.6153846.

Contrainte : NE PAS UTILISER DE BOUCLE.

Solution :

# Calcul des moyennes par mois
mean_per_month <- aggregate(
  x = airquality[, c("Ozone", "Solar.R")],
  by = list(Month = airquality$Month),
  FUN = mean, na.rm = TRUE
)
# Fusion par association avec airquality
air_qualityTemp <- merge(x = air_quality, y = mean_per_month, by = "Month")
# Imputation
air_quality$Ozone <- ifelse(
  test = is.na(air_qualityTemp$Ozone.x),
  yes = air_qualityTemp$Ozone.y,
  no = air_qualityTemp$Ozone.x
)
air_quality$Solar.R <- ifelse(
  test = is.na(air_qualityTemp$Solar.R.x),
  yes = air_qualityTemp$Solar.R.y,
  no = air_qualityTemp$Solar.R.x
)
View(air_quality)

# ##############
# AUTRE SOLUTION 
# mais nécessitant la création d'une fonction, 
# ce que nous n'avons pas encore appris à faire.

ozone_mean <- ave(
  airquality$Ozone, 
  airquality$Month, 
  FUN = function(x) mean(x, na.rm = TRUE)
)
# La commande suivante retourne uniquement des NA
#   ozone_mean <- ave(airquality$Ozone, airquality$Month, FUN = mean, na.rm = TRUE)
# parce que l'argument na.rm = TRUE est ignoré. Contrairement aux fonctions
# de la famille des apply, l'argument ... de ave sert à recevoir en entrée
# un nombre indéterminé d'arguments, et non à passer des arguments à la
# sous-fonction FUN.

solar_mean <- ave(
  airquality$Solar.R, 
  airquality$Month, 
  FUN = function(x) mean(x, na.rm = TRUE)
)

# Imputation
air_quality$Ozone <- ifelse(
  test = is.na(air_quality$Ozone),
  yes = ozone_mean,
  no = air_quality$Ozone
)
air_quality$Solar.R <- ifelse(
  test = is.na(air_quality$Solar.R),
  yes = solar_mean,
  no = air_quality$Solar.R
)
View(air_quality)

Question 5

Sélection d’un sous-ensemble de données

Créez un nouveau jeu de données contenant l’extrait suivant de airquality :

  • les observations pour les mois de juin, juillet et août seulement;
  • les variables Ozone, Solar.R, Wind et Temp seulement.

Vous devez effectuer cette tâche de 2 façons :

  • en utilisant des outils du R de base,
  • en utilisant des outils de dplyr.

Comparez les résultats obtenus avec les deux approches.

Solution :

# R de base : utilisons la fonction subset
extrait <- subset(
  airquality, 
  subset = Month %in% c(6, 7, 8), 
  select = Ozone:Temp
)
# Remarque : Ozone:Temp est compris comme toutes les variables se situant
# entre les variables Ozone et Temp inclusivement -> équivalent à 
# c(Ozone, Solar.R, Wind, Temp) étant donné l'ordre des variables.

# Même sélection avec l'opérateur [
extrait <- airquality[
  airquality$Month %in% c(6, 7, 8), 
  c("Ozone", "Solar.R", "Wind", "Temp")
]
# L'opérateur crochet ne comprend pas les raccourcis avec l'opérateur : 
# pour identifier une série de variables consécutives.


# Avec dplyr
extrait_autre <- select(filter(airquality, Month %in% c(6, 7, 8)), Ozone:Temp)

# Comparaison
all.equal(extrait, extrait_autre)
# [1] "Attributes: < Component “row.names”: Mean relative difference: 0.4 >"

# Seuls les noms de lignes diffèrent. [ et subset conservent les noms de
# lignes originaux tandis que filter de dplyr renomme les lignes.

Question 6

Normalisation de données

Reprenez l’extrait du jeu de données airquality créé à la question précédente. Normalisez les valeurs de ces variables en les ramenant toutes sur une échelle entre 0 et 1.

Solution :

# Obtention des minimums par variable
minimums <- apply(extrait, MARGIN = 2, FUN = min, na.rm = TRUE)
# Obtention des maximums par variable
maximums <- apply(extrait, MARGIN = 2, FUN = max, na.rm = TRUE)
# Normalisation
extrait_01norm <- scale(extrait, center = minimums, scale = maximums - minimums)
# Vérification
summary(extrait_01norm)
##     Ozone           Solar.R            Wind             Temp       
## Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
## 1st Qu.:0.1242   1st Qu.:0.3938   1st Qu.:0.2737   1st Qu.:0.4062  
## Median :0.2609   Median :0.6400   Median :0.3947   Median :0.5312  
## Mean   :0.2987   Mean   :0.5741   Mean   :0.4013   Mean   :0.5425  
## 3rd Qu.:0.4410   3rd Qu.:0.7785   3rd Qu.:0.5158   3rd Qu.:0.6562  
## Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
## NA's   :31       NA's   :3                                         

Données sur la production de fruits dans le monde

Reprenons encore une fois les données sur la production de fruits dans le monde enregistrées dans le fichier fruitsFinal.rds. Ce fichier est téléchargeable à partir du site web du cours dans le contenu de cette semaine.

Les variables de ces données sont ainsi définies :

  • Region : Région du monde, presque toujours un pays, mais parfois un territoire rattaché à un pays;
  • Item : Nom du ou des fruits, ou du groupe de fruits;
  • Annee : Année (2014, 2015 ou 2016);
  • Production : Quantité de fruits produits, en tonnes;
  • Superficie : Superficie de la région, en km2.

Téléchargez le fichier fruitsFinal.rds et chargez en R l’objet qu’il contient. Nommez cet objet fruits.

fruits <- readRDS("C:/coursR/fruitsFinal.rds")
str(fruits)
## 'data.frame':    7758 obs. of  5 variables:
##  $ Region    : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
##  $ Item      : chr  "Fruits, agrumes nda" "Fruits, agrumes nda" "Figues" "Figues" ...
##  $ Annee     : int  2016 2015 2014 2015 2015 2014 2015 2014 2016 2014 ...
##  $ Production: int  17472 3043 6100 4996 50657 3000 87686 46186 17894 23561 ...
##  $ Superficie: num  652230 652230 652230 652230 652230 ...

Question 7

Manipulation de chaînes de caractères

Quels noms de régions dans ces données contiennent le mot "Republique" ?

Solution :

regions_uniques <- unique(fruits$Region)
grep(pattern = "Republique", x = regions_uniques, value  = TRUE)
## [1] "l'ex-Republique yougoslave de Macedoine"    "Republique arabe syrienne"                 
## [3] "Republique centrafricaine"                  "Republique de Coree"                       
## [5] "Republique de Moldova"                      "Republique democratique du Congo"          
## [7] "Republique democratique populaire lao"      "Republique dominicaine"                    
## [9] "Republique populaire democratique de Coree" "Republique-Unie de Tanzanie"    

Question 8

Manipulation de chaînes de caractères

Si des noms de régions comportent un bout entre parenthèses, retirez ce bout.

Solution :

# Couper à la parenthèse ouvrante
etape_1 <- strsplit(x = fruits$Region, split = "(", fixed = TRUE)

# Est-ce que des noms comportent plus d'une parenthèse ouvrante?
table(sapply(etape_1, FUN = length))
##    1    2 
## 7500  258 
# Non, car le résultat de la commande strsplit serait alors de longueur 3 ou plus

# Est-ce que des noms comportent d'autres mots après la parenthèse fermante?
verif <- strsplit(x = fruits$Region, split = ")", fixed = TRUE)
table(sapply(verif, FUN = length))
##    1
## 7758
# Non, parce que le résultat de la dernière commande strsplit serait 
# alors de longueur 2 ou plus

# Pour retirer les bouts entre parenthèses, il suffit donc de conserver seulement
# le premier élément de chaque vecteur dans la liste etape_1
fruits$Region <- sapply(etape_1, FUN = "[", i = 1)
# ou
fruits$Region <- sapply(etape_1, FUN = head, n = 1)

Question 9

Manipulation de chaînes de caractères

Si des noms d’items comportent le caractère "/", remplacez toutes ses occurrences par la chaîne de caractères " ou ".

Solution :

# Un nom d'item comporte le caractère `"/"`.
unique(fruits$Item)
##  [1] "Fruits, agrumes nda"                           "Figues"  
##  [3] "Fruit a noyau, nda"                            "Abricots"
##  [5] "Baies nda"                                     "Raisins"
##  [7] "Oranges"                                       "Fruits frais nda"
##  [9] "Pasteques"                                     "Melons, cantaloups"
## [11] "Peches et nectarines"                          "Poires"
## [13] "Pommes"                                        "Prunes et prunelles"
## [15] "Bananes"                                       "Pamplemousses et pomelos"
## [17] "Papayes"                                       "Ananas"
## [19] "Avocats"                                       "Citrons et limes"
## [21] "Cerises"                                       "Coings"
## [23] "Fraises"                                       "Mangues, mangoustans et goyaves"
## [25] "Tangerines, mandarines, clementines, satsumas" "Dattes"
## [27] "Grillottes"                                    "Caroubes"
## [29] "Fruits tropicaux frais, nda"                   "Groseilles a maquereaux"
## [31] "Cassis et groseilles"                          "Framboises"
## [33] "Myrtilles"                                     "Kiwis"
## [35] "Plaqueminiers/kakis"                           "Canneberges"
## [37] "Plantains et autres"                           "Pomme d'acajou"
## [39] "Fruit a pepins, nda"

# Il s'agit de "Plaqueminiers/kakis"

# Remplacement du caractère
fruits$Item <- gsub(
  pattern = "/", 
  replacement = " ou ", 
  x = fruits$Item, fixed = TRUE
)

# Vérification
unique(fruits$Item)
## ... lignes inchangées
## [35] "Plaqueminiers ou kakis"                        "Canneberges" 
## ... lignes inchangées

Question 10

Modification de l’ordre des données

Ordonnez les observations du data frame fruits en ordre ascendant d’identifiant des fruits (variable Item), puis en ordre ascendant du nom de la région pour un même fruit et finalement en ordre décroissant d’année par combinaison fruit-région.

Vous devez effectuer cette tâche de 2 façons :

  • en utilisant la fonction order,
  • en utilisant le fonction arrange du package dplyr.

Comparez les résultats obtenus avec les deux approches.

Solution :

ordre <- order(fruits$Item, fruits$Region, -fruits$Annee)
fruits_order <- fruits[ordre,]
View(fruits_order)

fruits_arrange <- arrange(fruits, Item, Region, desc(Annee))
View(fruits_arrange)

all.equal(fruits_order, fruits_arrange)
## [1] "Attributes: < Component “row.names”: Mean relative difference: 0.6586219 >"

# Seuls les noms des lignes diffèrent. 
# Encore une fois, l'approche avec le R de base a conservé les 
# noms de lignes originaux, alors que dplyr a renommé les lignes.

Question 11

Changement de mise en forme de jeux de données

Le jeu de données fruits a une mise en forme longue. Créez un autre jeu de données contenant les mêmes données, mais sous une mise en forme large, avec les valeurs de production de fruits pour les différentes années dans des colonnes distinctes.

Vous devez effectuer cette tâche de 2 façons :

  • en utilisant la fonction reshape,
  • en utilisant le fonction pivot_wider du package tidyr.

Comparez les résultats obtenus avec les deux approches.

Solution :

# Avec reshape
fruits_large <- reshape(
  data = fruits, 
  direction = "wide",
  v.names = "Production", 
  timevar = "Annee",
  idvar = c("Region", "Item")
)
str(fruits_large)
## 'data.frame':    2602 obs. of  6 variables:
##  $ Region         : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
##  $ Item           : chr  "Fruits, agrumes nda" "Figues" "Fruit a noyau, nda" "Abricots" ...
##  $ Superficie     : num  652230 652230 652230 652230 652230 ...
##  $ Production.2016: num  17472 3143 51304 17894 23979 ...
##  $ Production.2015: num  3043 4996 50657 87686 24357 ...
##  $ Production.2014: num  3000 6100 46186 90000 23561 ...
##  - attr(*, "reshapeWide")=List of 5
##   ..$ v.names: chr "Production"
##   ..$ timevar: chr "Annee"
##   ..$ idvar  : chr  "Region" "Item"
##   ..$ times  : int  2016 2015 2014
##   ..$ varying: chr [1, 1:3] "Production.2016" "Production.2015" "Production.2014"
View(fruits_large)

# Avec pivot_wider
fruits_large_autre <- pivot_wider(
  data = fruits, 
  names_from = Annee,
  names_prefix = "Production.",  # pas obligatoire, seulement pour imiter reshape
  values_from = Production
)
str(fruits_large_autre)
## Classes ‘tbl_df’, ‘tbl’ and 'data.frame':    2602 obs. of  6 variables:
##  $ Region    : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
##  $ Item      : chr  "Fruits, agrumes nda" "Figues" "Fruit a noyau, nda" "Abricots" ...
##  $ Superficie: num  652230 652230 652230 652230 652230 ...
##  $ Production.2016: num  17472 3143 51304 17894 23979 ...
##  $ Production.2015: num  3043 4996 50657 87686 24357 ...
##  $ Production.2014: num  3000 6100 46186 90000 23561 ...
View(fruits_large_autre)

# À part
# - les attributs ajoutés par reshape
# - la classe du résultat retourné par pivot_wider (un tibble),
# - les noms de lignes
# est-ce que les deux jeux de données sont équivalents?

attr(fruits_large, "reshapeWide") <- NULL
fruits_large_autre <- as.data.frame(fruits_large_autre)
row.names(fruits_large) <- NULL
row.names(fruits_large_autre) <- NULL
all.equal(fruits_large, fruits_large_autre)
## [1] TRUE

# Réponse : oui!

Question 12

Manipulation de dates

Calculez en R dans combien de jours est le lundi de la semaine de lecture (7 mars 2022) en utilisant le format Date.

Solution :

auj <- Sys.Date()
dateLundiLecture <- as.Date("07/03/2022", format = "%d/%m/%Y")
difftime(dateLundiLecture, auj, units = "days")

# Si nous avions voulu calculer la durée en semaine :
difftime(dateLundiLecture, auj, units = "weeks")

Données WorldPhones

Les deux prochaines questions utilisent la matrice WorldPhones, incluse dans le package datasets de l’installation de base de R. Il s’agit d’observations de nombres de téléphones (en milliers) dans différentes régions du monde au cours des années 1951, 1956, 1957, 1958, 1959, 1960 et 1961.

WorldPhones
##      N.Amer Europe Asia S.Amer Oceania Africa Mid.Amer
## 1951  45939  21574 2876   1815    1646     89      555
## 1956  60423  29990 4708   2568    2366   1411      733
## 1957  64721  32510 5230   2695    2526   1546      773
## 1958  68484  35218 6662   2845    2691   1663      836
## 1959  71799  37598 6856   3000    2868   1769      911
## 1960  76036  40341 8220   3145    3054   1905     1008
## 1961  79831  43173 9053   3338    3224   2005     1076

Question 13

Changement de mise en forme de jeux de données

Le jeu de données WorldPhones a une mise en forme large. Transformez-le dans une mise en forme longue, avec les nombres de téléphones dans une seule colonne et l’identification de la région dans une autre. Pour ce faire, vous devez préalablement transformer la matrice en data frame et y ajouter une colonne contenant les années, qui sont à l’origine stockées dans les noms des lignes.

Vous devez effectuer la tâche de modification de mise en forme de 2 façons :

  • en utilisant la fonction reshape,
  • en utilisant le fonction pivot_longer du package tidyr.

Comparez les résultats obtenus avec les deux approches.

Solution :

# Transformation en data frame avec une nouvelle colonne pour l'année
phones <- data.frame(WorldPhones)
phones$Year <- as.numeric(rownames(phones))

# Avec reshape
phones_long <- reshape(
    data = phones, 
    direction = "long", 
    varying = c("N.Amer", "Europe", "Asia", "S.Amer", "Oceania", "Africa", "Mid.Amer"),
    v.names = "Nbr.Phones", 
    timevar = "Region",
    times = c("N.Amer", "Europe", "Asia", "S.Amer", "Oceania", "Africa", "Mid.Amer"),
    idvar = "Year"
)
str(phones_long)
## 'data.frame':    49 obs. of  3 variables:
##  $ Year      : num  1951 1956 1957 1958 1959 ...
##  $ Region    : chr  "N.Amer" "N.Amer" "N.Amer" "N.Amer" ...
##  $ Nbr.Phones: num  45939 60423 64721 68484 71799 ...
##  - attr(*, "reshapeLong")=List of 4
##   ..$ varying:List of 1
##   .. ..$ Nbr.Phones: chr  "N.Amer" "Europe" "Asia" "S.Amer" ...
##   .. ..- attr(*, "v.names")= chr "Nbr.Phones"
##   .. ..- attr(*, "times")= chr  "N.Amer" "Europe" "Asia" "S.Amer" ...
##   ..$ v.names: chr "Nbr.Phones"
##   ..$ idvar  : chr "Year"
##   ..$ timevar: chr "Region"
View(phones_long)

# Avec pivot_longer
phones_long_autre <- pivot_longer(
  data = phones, 
  cols = c(N.Amer, Europe, Asia, S.Amer, Oceania, Africa, Mid.Amer), 
  names_to = "Region", 
  values_to = "Nbr.Phones"
)
str(phones_long_autre)
## Classes ‘tbl_df’, ‘tbl’ and 'data.frame':    49 obs. of  3 variables:
##  $ Year      : num  1951 1951 1951 1951 1951 ...
##  $ Region    : chr  "N.Amer" "Europe" "Asia" "S.Amer" ...
##  $ Nbr.Phones: num  45939 21574 2876 1815 1646 ...
View(phones_long_autre)

# Différences entre les deux résultats :
# - les attributs ajoutés par reshape
# - la classe du résultat retourné par pivot_wider (un tibble),
# - les noms de lignes
# - l'odre des observations

attr(phones_long, "reshapeLong") <- NULL
phones_long_autre <- as.data.frame(phones_long_autre)
row.names(phones_long) <- NULL
row.names(phones_long_autre) <- NULL
phones_long <- dplyr::arrange(phones_long, Region, Year)
phones_long_autre <- dplyr::arrange(phones_long_autre, Region, Year)
all.equal(phones_long, phones_long_autre)
## [1] TRUE

Question 14

Fusion par association

Soumettez le code suivant pour créer un petit jeu de données contenant la population, en milliers, pour les mêmes régions du monde que celles présentes dans le jeu de données WorldPhones, mais pour l’année 1960 seulement. (Ces données sont tirées du site web suivant : https://en.wikipedia.org/wiki/List_of_continents_by_population)

population_1960 <- data.frame(
  An = 1960,
  Territoire = c("N.Amer", "Mid.Amer", "S.Amer", 
                 "Europe", "Africa", "Asia", "Oceania"),
  Pop = c(204167, 72124, 149066, 605619, 284887, 1686698, 15784),
  stringsAsFactors = FALSE
)

Effectuez une fusion par association entre le jeu de donnée créé à la question précédente et le jeu de données population_1960 en faisant correspondre les régions et les années. Ne conservez que les combinaisons présentes dans les deux jeux de données. En bout de ligne, vous devriez un jeu de données similaire au suivant.

##     Region Year Nbr.Phones     Pop
## 1   N.Amer 1960      76036  204167
## 2   Europe 1960      40341  605619
## 3     Asia 1960       8220 1686698
## 4   S.Amer 1960       3145  149066
## 5  Oceania 1960       3054   15784
## 6   Africa 1960       1905  284887
## 7 Mid.Amer 1960       1008   72124

Vous devez effectuer cette tâche de 2 façons :

  • en utilisant un outil du R de base,
  • en utilisant un outil de dplyr.

Comparez les résultats obtenus avec les deux approches.

Solution :

# Fonction merge du package base de R
phone1960 <- merge(
  x = phones_long, 
  y = population_1960, 
  by.x = c("Region", "Year"), 
  by.y = c("Territoire", "An"),
  sort = FALSE
)
phone1960
##     Region Year Nbr.Phones     Pop
## 1   N.Amer 1960      76036  204167
## 2   Europe 1960      40341  605619
## 3     Asia 1960       8220 1686698
## 4   S.Amer 1960       3145  149066
## 5  Oceania 1960       3054   15784
## 6   Africa 1960       1905  284887
## 7 Mid.Amer 1960       1008   72124

# Fonction inner_join du package dplyr
phone1960_autre <- inner_join(
  x = phones_long, 
  y = population_1960, 
  by = c("Region" = "Territoire", "Year" = "An")
)
phone1960_autre
##   Year   Region Nbr.Phones     Pop
## 1 1960   N.Amer      76036  204167
## 2 1960   Europe      40341  605619
## 3 1960     Asia       8220 1686698
## 4 1960   S.Amer       3145  149066
## 5 1960  Oceania       3054   15784
## 6 1960   Africa       1905  284887
## 7 1960 Mid.Amer       1008   72124

# Seule différence entre les deux résultats :
# - deux premières colonnes pas dans le même ordre.

phone1960_autre <- phone1960_autre[, names(phone1960)]
all.equal(phone1960, phone1960_autre)
## [1] TRUE

Retour aux données sur la production de fruits dans le monde

Question 15

Package data.table

Créez un data table contenant les données du data frame fruits. Avec les fonctionnalités du package data.table, apportez les modifications suivantes à cet objet :

  • conservez seulement les observations relatives à l’année 2014;
  • ajoutez une variable nommée Superficie_mi2 contenant les superficies en miles carrés plutôt que kilomètres carrés (facteur de conversion = 0.386102);
  • ordonnez les observations en ordre décroissant de valeur de Production.

Solution :

library(data.table)

fruits_dt <- as.data.table(fruits)
str(fruits_dt)

# Sélection d'observations : même syntaxe que le R de base
fruits_dt <- fruits_dt[Annee == 2014, ]
str(fruits_dt)

# Ajout de variable : fait en référence (aucune assignation nécessaire)
fruits_dt[, Superficie_mi2 := Superficie * 0.386102]
str(fruits_dt)

# Modification de l'ordre des observations : aussi fait en référence
setorder(fruits_dt, -Production, na.last = TRUE)
str(fruits_dt)

# Remarque : La fonction order a aussi un argument na.last, mais sa valeur
# par défaut est TRUE, alors qu'elle est FALSE pour setorder.