2.numbers <- function(a, b){
add.<- a+b
sum return(sum)
}add.2.numbers(3, 4)
[1] 7
Avant de commencer, assurez-vous que vous avez bien téléchargé tous les fichiers nécessaires pour la séance (pdf, code R et données) et que tout est sauvé dans un répertoire de votre ordinateur. Assurez-vous aussi que le code IntroR_part3.R
est bien ouvert dans l’éditeur de RStudio. Finalement, vous devez avoir assimilé les notions de la première et de la deuxième partie de l’introduction à R avant de commencer cette séance.
Dans ce document, nous allons couvrir les thèmes suivants:
list
et array
apply
et ses variantesNous allons utiliser ici les données présentes dans le fichier salesmen.txt
qui contient les ventes réalisées par les employés d’une compagnie. Chaque ligne du fichier représente un employé et les variables sont les suivantes:
EmpID
: ID de l’employéMonth
: mois de l’année (de 1 à 12)nSales
: nombre de ventes totalesTotSales
: total des ventes (en $)Il est très facile d’écrire ses propres fonctions dans R. Une fois écrites, elles deviennent des fonctions permanentes dans votre version de R, et disponibles tout comme n’importe quelle autre fonction. Nous allons vous présenter quelques exemples. Voici tout d’abord un exemple facile, où nous allons créer une fonction qui additionne deux nombres. Bien sûr, ce n’est pas un exemple très pertinent ni utile, mais cela va nous permettre de voir comment les fonctions sont construites en général.
2.numbers <- function(a, b){
add.<- a+b
sum return(sum)
}add.2.numbers(3, 4)
[1] 7
C’est tout ce dont nous avons besoin pour créer une fonction !
Une fonction peut retourner plus qu’un résultat, via une liste:
<- function(a, b){
sommediff <- a + b
somme <- a - b
diff # par défaut, le dernier énoncé est retourné si return() est omis
list("somme" = somme,
"diff" = diff)
}sommediff(3, 4)
$somme
[1] 7
$diff
[1] -1
Voici maintenant un exemple d’utilisation de boucle, permettant d’automatiser la répétition d’une tâche. Comme R est un langage vectorisé, les opérations sur les vecteurs sans boucle, à moindre coût de calcul.
# Afficher une série de chiffres successifs à l'écran
for (i in seq_len(4)){ # pour 1 à 4
print(i^2)
}
[1] 1
[1] 4
[1] 9
[1] 16
# Autre exemple
# Afficher une série d'éléments d'un ensemble donné à l'écran
for (w in c("red", "blue", "green")){
print(w)
}
[1] "red"
[1] "blue"
[1] "green"
Il existe un objet en R qui est un pointeur objet “vide” (NULL
) et qui est très utile lorsqu’on veut réinitialiser un élément d’une liste
Voici un autre exemple, où nous allons créer une fonction qui construit de façon automatisée des graphiques pour les données salesmen.txt
.
#' Créer des graphiques pour un employé
#' @param x base de données
#' @param emp identifiant de l'employé que l'on veut analyser
#' @return NULL; des graphiques
<- function(x, emp){
sales if(sum(x$EmpID == emp) == 0){
stop("Cet employé n'existe pas.")
}par(mfrow = c(1, 2))
plot(x = 1:12,
y = x$nSales[x$EmpID == emp],
type = 'l',
xlab = "mois",
ylab = "nombre de transactions")
title(paste("nombre de transactions de l'employé", emp, "par mois"))
plot(x = 1:12,
y= (x$TotSales/x$nSales)[x$EmpID==emp],
type = 'l',
xlab = "mois",
ylab = "ventes moyenne")
title("Montant moyen des ventes par mois")
}
Exercice 1 Lire les commandes de la fonction et bien comprendre ce qu’elle fait. Exécuter ensuite la fonction avec les données du fichier salesmen.txt
et ID=1001
, puis ID=1006
puis enfin avec ID=1
.
Dans la plupart des cas, les données avec lesquelles vous allez travailler vont contenir des valeurs manquantes. Ces valeurs sont représentées par des NA
(not available) dans R. Voici quelques fonctions prédéfinies reliées au traitement des valeurs manquantes:
# Création d'un vecteur qui contient une valeur manquante
<- c(1, NA, 5.6, -1, 0)
x
# Retourne des indicateurs de valeurs manquantes TRUE/FALSE
is.na(x)
[1] FALSE TRUE FALSE FALSE FALSE
# Que se passe-t-il lorsque on veut prendre le log d'une valeur NA ?
log(x)
Warning in log(x): NaNs produced
[1] 0.000000 NA 1.722767 NaN -Inf
# Retourne des indicateurs logiques de valeurs manquantes "not a number" (NaN)
is.na(x)
[1] FALSE TRUE FALSE FALSE FALSE
is.nan(x)
[1] FALSE FALSE FALSE FALSE FALSE
is.finite(x) # vrai si NA, NaN, ou Inf, -Inf
[1] TRUE FALSE TRUE TRUE TRUE
Plusieurs fonctions ont un traitement spécial des valeurs manquantes. C’est le cas des fonctions mean
, var
, sd
, etc. Voici un exemple:
mean(x)
[1] NA
mean(x, na.rm = TRUE)
[1] 1.4
On peut aussi extraire les valeurs non manquantes d’un vecteur ou bien calculer le nombre de valeurs manquantes tel que ci-dessous:
sum(is.na(x)) # nombre de valeurs manquantes
[1] 1
!is.na(x)] # sous-ensemble de valeurs non-manquantes x[
[1] 1.0 5.6 -1.0 0.0
Voici maintenant un exemple de fonction que nous pouvons créer qui permet de retourner le nombre de valeurs manquantes dans un vecteur. Vous pouvez en même temps y voir un exemple d’utilisation de la commande if
.
#' Nombre de valeurs manquantes
#'
#' @param x vecteur
#' @param prop proportion de valeurs manquantes, 0.1 par défaut
#' @return nombre de valeurs manquantes
<- function(x, prop = 0.1){
nb.na if(mean(is.na(x)) >= prop){
warning(paste("Proportion de valeurs manquantes supérieure à", propr))
}return(sum(is.na(x)))
}
Exercice 2 Écrire une fonction qui prend comme entrée une table de données (data.frame
) et un vecteur représentant des indices de colonnes. La fonction devrait retourner, pour chaque colonne, le nombre d’observations dont la valeur est supérieure à la moyenne de la colonne.
list
et `array``Nous allons voir ici deux types d’objets un peu plus complexes.
Un array
est une généralisation d’une matrice, en plus de deux dimensions. Si un array
est de dimension trois, on peut accéder aux différents éléments avec l’opérateur [ , , ]
, comme pour les vecteurs et matrices. Voici un exemple de construction de array
qui va nous permettre de mieux visualiser les données de vente.
<- read.table("Data/salesmen.txt", header = TRUE)
donnees
# Mettre les données sous la forme d'un cube
# Dimensions: 12 (mois) * 150 (clients) * 4 (variables)
<- array(as.matrix(donnees), c(12, 150, 4))
donnees_array
# Données pour le 1er client, 1er mois
1, 1, ] donnees_array[
[1] 100.00 1.00 16.00 15687.46
# Données pour le 1er client
1, ] donnees_array[,
[,1] [,2] [,3] [,4]
[1,] 100 1 16 15687.46
[2,] 100 2 21 21496.56
[3,] 100 3 11 12015.78
[4,] 100 4 18 18537.36
[5,] 100 5 13 13440.72
[6,] 100 6 13 12748.57
[7,] 100 7 17 16931.61
[8,] 100 8 21 21864.75
[9,] 100 9 20 20147.32
[10,] 100 10 13 12928.39
[11,] 100 11 19 19046.06
[12,] 100 12 21 19194.89
# Données pour le 1er mois
head(donnees_array[1, , ])
[,1] [,2] [,3] [,4]
[1,] 100 1 16 15687.46
[2,] 101 1 11 11559.90
[3,] 102 1 7 7783.04
[4,] 103 1 13 13323.09
[5,] 104 1 21 20399.71
[6,] 105 1 11 11035.39
# Nombre de ventes pour tous les employés en janvier (6 premières lignes)
head(donnees_array[1, , c(1, 3)])
[,1] [,2]
[1,] 100 16
[2,] 101 11
[3,] 102 7
[4,] 103 13
[5,] 104 21
[6,] 105 11
# Vente moyenne mensuelle pour le 3e employé
3, 4]/donnees_array[, 3, 3] donnees_array[,
[1] 1111.8629 904.9420 975.1046 NA 908.8900 985.2789 1051.4989
[8] 1067.3665 996.9230 1000.7414 1021.9857 943.7427
Travailler avec des (hyper)cubes requiert un niveau d’abstraction plus élevé, mais une fois que vous allez être habitués, vous allez voir que cela peut être une approche très efficace.
Exercice 3 Afficher toutes les données du nombre de vente pour le mois de mars pour tous les employés dont l’identifiant commence par 10
.
Un autre objet très utilisé en R est le type list
. Une liste est un vecteur d’éléments qui eux même peuvent être de n’importe quel format (vecteur, matrice, etc.). Voici un exemple de création de liste:
<- read.table("Data/salesmen.txt", header = TRUE)
donnees
# Creation d'une liste qui va contenir 5 elements:
# year, succursale, ID, nbre de ventes par mois et total vente par mois
<- with(donnees,
donnees_list list(
year = 2012,
succ = "Montreal Centre",
ID = unique(EmpID),
nb = t(matrix(nSales, nrow = 12)),
montant = t(matrix(TotSales, nrow = 12))
))names(donnees_list)
[1] "year" "succ" "ID" "nb" "montant"
Pour accéder aux éléments d’une liste, il faut utiliser le symbole [[ ]]
avec le numéro de l’élément auquel on veut accéder. On peut aussi utiliser le symbole $ avec le nom de l’élément, comme pour un dataframe. Voici quelques exemples:
# Extraire de la liste le nbre de vente pour tous les employés par mois
# (6 premières lignes)
head(donnees_list$nb)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 16 21 11 18 13 13 17 21 20 13 19 21
[2,] 11 6 12 12 15 23 17 20 18 16 15 24
[3,] 7 15 13 NA 15 18 19 20 23 21 28 30
[4,] 13 12 9 21 19 22 22 12 16 25 19 21
[5,] 21 11 20 17 21 25 23 21 28 18 31 21
[6,] 11 19 17 25 20 19 21 27 24 29 32 32
# Autre facon de faire la même chose...
head(donnees_list[[4]])
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 16 21 11 18 13 13 17 21 20 13 19 21
[2,] 11 6 12 12 15 23 17 20 18 16 15 24
[3,] 7 15 13 NA 15 18 19 20 23 21 28 30
[4,] 13 12 9 21 19 22 22 12 16 25 19 21
[5,] 21 11 20 17 21 25 23 21 28 18 31 21
[6,] 11 19 17 25 20 19 21 27 24 29 32 32
# Extraire de la liste le nbre de vente pour tous les mois, premier sujet
4]][1, ] donnees_list[[
[1] 16 21 11 18 13 13 17 21 20 13 19 21
Exercice 4 Afficher toutes les données du montant de vente pour le mois de mars pour tous les employés dont l’identifiant commence par 10
.
apply
Comme nous l’avons vu, les boucles peuvent être très utiles pour automatiser certains processus. Cependant, les boucles ne sont pas très efficaces d’un point de vue computationnel. Il existe des fonctions dans R qui permettent d’éviter de faire des boucles tout en permettant une répétition automatique de tâches. C’est ce que nous allons voir avec la commande apply
et ses variants lapply
, tapply
et sapply
.
Nous allons illustrer l’utilisation de ces fonctions à l’aide de la matrice du nombre de vente pour tous les employés, que nous allons extraire à partir du array que nous avons crée dans la section précédente:
# Lignes = mois
# Colonnes = employés
<- donnees_array[, , 3]
nb_vente dim(nb_vente)
[1] 12 150
L’idée de la commande apply
est d’appliquer une fonction (par exemple mean
) de façon systématique et répétitive à toutes les lignes ou colonne d’une matrice. Cette fonction peut aussi s’appliquer à un hypercube de classe array
. Le premier argument de la fonction est l’objet (données) sur lequel on veut appliquer la fonction, le second argument prend les indices des dimensions que l’on veut conserver dans l’objet de sortie. Ainsi, MARGIN = 1
si la fonction est appliquée aux lignes de l’objet, et prend MARGIN = 2
si elle est appliquée aux colonnes dans le cas d’une matrice. Le dernier argument est le nom de la fonction que l’on veut appliquer (par ex., mean
). On peut aussi rajouter d’autres options spécifiques à la fonction en question.
Voici des exemples d’utilisation de la fonction:
# Applique la fonction "mean" à toutes les lignes (donc mois) de la matrice
# Cela donne la moyenne des ventes par mois
apply(nb_vente, 1, mean)
[1] NA NA NA NA NA NA NA NA
[9] NA NA NA 25.44667
# Même chose, en spécifiant l'option `na.rm` de la fonction `mean`
apply(nb_vente, 1, mean, na.rm = TRUE)
[1] 13.92308 14.50350 15.70423 17.00000 18.20946 18.80272 20.69128 20.45638
[9] 21.93243 22.80537 23.65986 25.44667
# Applique la fonction "mean" à toutes les colonnes (donc employés) de la matrice
# Cela donne la moyenne des ventes par employé (6 premières lignes)
head(apply(nb_vente, 2, mean, na.rm = TRUE))
[1] 16.91667 15.75000 19.00000 17.58333 21.41667 23.00000
# Valeurs min et max par mois
apply(nb_vente, 1, range, na.rm = TRUE)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 5 5 6 6 7 6 7 7 10 12 10 11
[2,] 29 29 33 31 33 32 37 32 35 38 40 43
# Valeurs min et max par employé pour les 10 premiers employés
apply(nb_vente[, 1:10],
MARGIN = 2, #dimensions à conserver (ici colonnes)
FUN = range, # fonction à appliquer
na.rm = TRUE) # paramètres additionnels à passer à la fonction
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 11 6 7 9 11 11 8 12 9 14
[2,] 21 24 30 25 31 32 20 26 27 30
Les variantes sapply
et lapply
s’appliquent aux vecteurs et aux objets de type liste. Voici un exemple:
<- function(i, num = 1:10){
fn sum(num^i)
}sapply(0:5, fn)
[1] 10 55 385 3025 25333 220825
Les commandes apply
peuvent paraître étrange au début, surtout pour les utilisateurs qui sont habitués aux boucles. Cependant, l’utilisation de ces commandes rend le code plus compacte et elimine les erreurs reliées aux indices dans les boucles. Notez qu’un bon programmeur R utilise rarement des boucles.
Le fichier salesmen.txt
avec lequel nous avons travaillé contient en réalité les données de vente pour plusieurs branches de la compagnie. Les 2 premier caractères du ID de l’employé représentent l’identifiant de la branche.
apply
(ou ses dérivés), calculez le nbre total de vente pour chaque branche. Faire un graphique (diagramme à bande, voir ?barplot
) qui illustre la contribution de chaque branches aux ventes de la compagnie.