logo

Le développement des technologies s’est accompagné d’une production massive de données, le Big Data. De nombreuses institutions et organisations à but non lucratif se sont engagées dans une mise à disposition des données. Et depuis quelques années, l’accès à ces données est facilité à travers le développement de plateformes ou d’API. Des plateformes mettent à disposition de nombreuses données pour les chercheurs, journalistes, élus et citoyens. Les données utilisées pour cette formation sont produites par la police deNew-York et disponibles sur le site du https://data.cityofnewyork.us/. Il s’agit d’un ensemble de statistiquessur les crimes à New-York entre 2016-2018.

1 Gérer son espace de travail

L’ensemble des objets ainsi créés sont stockés dans l’espace de travail. Cet espace de travail peut être sauvegardé, chargé ou réinitialisé, selon les besoins. L’enregistrement de l’espace de travail se fait automatiquement dans le répertoire courant (c’est-à-dire celui dans lequel vous êtes “présentement positionnés”). Plusieurs commandes permettent de gérer le répertoire courant.

# Reinitialiser (nettoyer) son espace de travail
# Lister les objets définis dans l'espace de travail actuel puis supprimer
rm(list=ls())

# Dans Documents, creer un répertoire "FORMATION_R"
# Dans ce meme repertoire, copier/coller le fichier dezippe "DONNEES"
# Enregistrer le script R dans le répertoire "FORMATION_R"
setwd('.')

# Afficher le repertoire de travail actuel
getwd()
...   [1] "C:/GIT/R_FORMATION_DEBUTANT"
# Packages necessaires pour cette formation : 
#install.packages('dplyr')
library('dplyr')
#install.packages('tidyr')
library('tidyr')
#install.packages('sf')
library('sf')

2 Ouvrir les données

# Lecture du premier fichier :
df <- read.csv2("DONNEES//NYPD_Arrests_Data_2016-2018.csv')

3 Parcourir le tableau de données

# Valeur prise par l'individu 1 pour la variable 2
df[1,2]

# Valeurs prises par l'individu 1 pour l'ensemble des variables
df[1, ]

# Valeurs prises par l'ensemble des individus pour la variable 1
df[ ,1]
# Valeurs prises par les individus 1 à 3 pour la variable 1
df[1:3,1]
...   [1] 1 2 3
# Afficher que les lignes 50 à 55 et les colonnes 2, 4, 17 et 18
df[50:55, c(2,4,17,18)]
...      ARREST_KEY PD_CD Y_COORD_CD Latitude
...   50  173114444   258     236932 40.81698
...   51  173114460   478     227883 40.79217
...   52  173115841   258     164172 40.61730
...   53  173115846   567     188480 40.68401
...   54  173116026   114     252019 40.85837
...   55  173116029   101     247658 40.84637
# Compter le nombre de colonnes (Premiere methode)
numberOfColumns <- ncol(df)
cat('il y a', numberOfColumns, 'colonnes')
...   il y a 19 colonnes
# Afficher le nombre de lignes et colonnes (Seconde methode)
# /!\ la fonction retourne un vecteur de deux elements
dim(df)
...   [1] 847116     19
# Afficher le resultat
cat('il y a', dim(df)[2], 'colonnes et', dim(df)[1], 'lignes')
...   il y a 19 colonnes et 847116 lignes
# Afficher le nom des colonnes
colnames(df)
...    [1] "X"                 "ARREST_KEY"        "ARREST_DATE"      
...    [4] "PD_CD"             "PD_DESC"           "KY_CD"            
...    [7] "OFNS_DESC"         "LAW_CODE"          "LAW_CAT_CD"       
...   [10] "ARREST_BORO"       "ARREST_PRECINCT"   "JURISDICTION_CODE"
...   [13] "AGE_GROUP"         "PERP_SEX"          "PERP_RACE"        
...   [16] "X_COORD_CD"        "Y_COORD_CD"        "Latitude"         
...   [19] "Longitude"

4 Filtrer le tableau de données

# Creer un vecteur contenant la liste des colonnes d'interet
# (liste des noms de colonnes ou des numeros de colonnes)
listColumnsByNames <- c("ARREST_DATE", "PD_CD","KY_CD","OFNS_DESC","ARREST_BORO","AGE_GROUP","PERP_SEX","PERP_RACE","Latitude","Longitude")
listColumnsByIndex <- c(3,4,6,7,10,13,14,15,18,19)

# Filtrer le tableau
dfFilter <- df[,listColumnsByNames] # Par le nom des colonnes
dfFilter <- df[,listColumnsByIndex] # Par le numéro des colonnes

# Supprimer la premiere "PD_CD" (deuxieme colonne)
dfFilter <- dfFilter[,-2]

# Renommer les colonnes
newColNames <- c("ARREST_DATE","CODE_INF","INFRACTION","ARREST_QUARTIER","AGE_GROUPE","SEX","RACE","LATITUDE","LONGITUDE")
colnames(dfFilter) <- newColNames

# Modifier le type de donnees de la colonne "ARREST_DATE"
dfFilter$ARREST_DATE <- as.POSIXct(as.character(dfFilter$ARREST_DATE),format="%Y-%m-%d")

# Afficher la structure de la table
str(dfFilter)
...   'data.frame': 847116 obs. of  9 variables:
...    $ ARREST_DATE    : POSIXct, format: "2017-12-31" "2017-12-31" ...
...    $ CODE_INF       : int  678 343 677 344 344 105 344 235 344 358 ...
...    $ INFRACTION     : Factor w/ 72 levels "","ABORTION",..: 42 56 57 8 8 67 8 16 8 54 ...
...    $ ARREST_QUARTIER: Factor w/ 5 levels "B","K","M","Q",..: 4 4 2 3 3 2 3 3 3 3 ...
...    $ AGE_GROUPE     : Factor w/ 5 levels "<18","18-24",..: 3 3 2 3 4 2 2 3 3 2 ...
...    $ SEX            : Factor w/ 2 levels "F","M": 2 2 2 2 2 2 1 2 2 2 ...
...    $ RACE           : Factor w/ 7 levels "AMERICAN INDIAN/ALASKAN NATIVE",..: 3 2 3 6 3 3 7 3 4 6 ...
...    $ LATITUDE       : num  40.7 40.8 40.7 40.8 40.8 ...
...    $ LONGITUDE      : num  -73.7 -73.9 -73.9 -74 -74 ...
# Selectionner 2017
dfFilter <- dfFilter[dfFilter$ARREST_DATE >= "2017-01-01" & dfFilter$ARREST_DATE < "2018-01-01",]

5 Modifier certaines variables

# Afficher les modalites de la variable 'ARREST_QUARTIER'
unique(dfFilter$ARREST_QUARTIER)
...   [1] Q K M S B
...   Levels: B K M Q S
# Remplacer la lettre d'identification par le nom
# B(Bronx), S(Staten Island), K(Brooklyn), M(Manhattan), Q(Queens)
dfFilter$ARREST_QUARTIER <- ifelse(dfFilter$ARREST_QUARTIER == "S", "Staten Island",
                              ifelse(dfFilter$ARREST_QUARTIER == "K", "Brooklyn",
                                ifelse(dfFilter$ARREST_QUARTIER == "M", "Manhattan",
                                  ifelse(dfFilter$ARREST_QUARTIER == "Q", "Queens", "Bronc"))))

# Afficher les modalites de la variable 'ARREST_QUARTIER'
unique(dfFilter$ARREST_QUARTIER)
...   [1] "Queens"        "Brooklyn"      "Manhattan"     "Staten Island"
...   [5] "Bronc"
# Modifier certaines valeurs du tableau
dfFilter$ARREST_QUARTIER <- gsub("Bronc", "Bronx", dfFilter$ARREST_QUARTIER)

# Recodage des modalites d'une variable
dfFilter$SEX <- as.character(dfFilter$SEX)
dfFilter$SEX[dfFilter$SEX == "F"] <- "Femme" 
dfFilter$SEX[dfFilter$SEX == "M"] <- "Homme"

# Afficher les modalites de la variable 'SEX'
unique(dfFilter$SEX)
...   [1] "Homme" "Femme"
# Afficher le nombre de lignes 
nrow(dfFilter)
...   [1] 286225
# Supprimer des NA's
dfFilter <-  dfFilter[complete.cases(dfFilter), ]

# Afficher de nouveau le nombre de lignes 
nrow(dfFilter)
...   [1] 285418
# Sauvegarder le fichier
# Exporter le fichier en csv
write.csv2(dfFilter, file = "NYPD_Arrests_Data_2017.csv")
# Exporter en Rdata, objet R 
save(dfFilter, file = "NYPD_Arrests_Data_2017.Rdata")

6 Requêter le jeu de données

# Réinitialiser (nettoyer) son espace de travail
# Lister les objets définis dans l'espace de travail actuel puis supprimer
rm(list=ls())

# Charger le tableau précédemment créé
load('NYPD_Arrests_Data_2017.Rdata')
# Trier par date, fonction "order"
dfFilter <- dfFilter[order(dfFilter$ARREST_DATE),]

# Afficher un resume statistique de l'ensemble du tableau
summary(dfFilter)
...     ARREST_DATE                     CODE_INF    
...    Min.   :2017-01-01 00:00:00   Min.   :101.0  
...    1st Qu.:2017-03-28 00:00:00   1st Qu.:121.0  
...    Median :2017-06-24 00:00:00   Median :341.0  
...    Mean   :2017-06-26 11:01:55   Mean   :276.5  
...    3rd Qu.:2017-09-26 00:00:00   3rd Qu.:347.0  
...    Max.   :2017-12-31 00:00:00   Max.   :995.0  
...                                                 
...                              INFRACTION     ARREST_QUARTIER    AGE_GROUPE    
...    DANGEROUS DRUGS                : 48625   Length:285418      <18  : 16149  
...    ASSAULT 3 & RELATED OFFENSES   : 33896   Class :character   18-24: 69417  
...    PETIT LARCENY                  : 23020   Mode  :character   25-44:143093  
...    VEHICLE AND TRAFFIC LAWS       : 20772                      45-64: 53836  
...    OTHER OFFENSES RELATED TO THEFT: 19911                      65+  :  2923  
...    FELONY ASSAULT                 : 15009                                    
...    (Other)                        :124185                                    
...        SEX                                        RACE           LATITUDE    
...    Length:285418      AMERICAN INDIAN/ALASKAN NATIVE:   732   Min.   :40.50  
...    Class :character   ASIAN / PACIFIC ISLANDER      : 14573   1st Qu.:40.68  
...    Mode  :character   BLACK                         :135506   Median :40.74  
...                       BLACK HISPANIC                : 24464   Mean   :40.74  
...                       UNKNOWN                       :  2528   3rd Qu.:40.81  
...                       WHITE                         : 35290   Max.   :40.91  
...                       WHITE HISPANIC                : 72325                  
...      LONGITUDE     
...    Min.   :-74.25  
...    1st Qu.:-73.98  
...    Median :-73.93  
...    Mean   :-73.93  
...    3rd Qu.:-73.88  
...    Max.   :-73.70  
...   
# Afficher les valeurs uniques pour une colonne 
unique(dfFilter$RACE)
...   [1] BLACK                          WHITE                         
...   [3] ASIAN / PACIFIC ISLANDER       WHITE HISPANIC                
...   [5] BLACK HISPANIC                 UNKNOWN                       
...   [7] AMERICAN INDIAN/ALASKAN NATIVE
...   7 Levels: AMERICAN INDIAN/ALASKAN NATIVE ASIAN / PACIFIC ISLANDER ... WHITE HISPANIC
# Afficher un compte de ces valeurs
summary(dfFilter$RACE)
...   AMERICAN INDIAN/ALASKAN NATIVE       ASIAN / PACIFIC ISLANDER 
...                              732                          14573 
...                            BLACK                 BLACK HISPANIC 
...                           135506                          24464 
...                          UNKNOWN                          WHITE 
...                             2528                          35290 
...                   WHITE HISPANIC 
...                            72325
# Afficher le nombre de modalites pour la variable "RACE"
# Identifier ces valeurs uniques, fonction "unique"
# Puis fonction "length" pour afficher la longueur d'un vecteur
length(unique(dfFilter$RACE))
...   [1] 7
# Compter le nombre d'infractions par jour, fonction "table"
countInfByDay <- table(dfFilter$ARREST_DATE)

# Trier le vecteur nommé par ordre decroissant et afficher les trois premières valeurs
# Tableau initial
head(countInfByDay, n=3)
...   
...   2017-01-01 2017-01-02 2017-01-03 
...          527        524        844
# Tri decroissant, fonction "sort"
countInfByDay <- sort(countInfByDay, decreasing = T)
# Afficher le jour comptant le plus d'arrestations
head(countInfByDay, n=1)
...   2017-02-08 
...         1244
# Afficher le nombre d'infractions commises par les mineurs ("<18")
# Premiere methode, fonction "length" pour afficher la longueur d'un vecteur
length(dfFilter$AGE_GROUP[dfFilter$AGE_GROUP == "<18"])
...   [1] 16149
# Seconde methode, fonction "nrow" pour afficher le nombre de lignes d'une table
nrow(dfFilter[which(dfFilter$AGE_GROUPE == "<18"),])
...   [1] 16149
# Trouver le nombre d'infractions commises le 5 août 2017
nOfInfraction <- nrow(dfFilter[which(dfFilter$ARREST_DATE == "2017-08-05"),])

# Et le jour de votre anniversaire, combien y a-t-il eu d'infractions à New-York?

# Combien de femmes ont commis une infraction ce même jour ?
nOfwoman <- nrow(dfFilter[which(dfFilter$ARREST_DATE == "2017-08-05" & dfFilter$SEX == "Femme"),])

# Pourcentage Homme-Femme
perWoman <-(nOfwoman/nOfInfraction)*100 
cat(format(perWoman, digits = 3),'% des criminels du 05 août 2017 sont des femmes')
...   17.2 % des criminels du 05 août 2017 sont des femmes
# Supprimer les variables inutiles pour la suite du script
rm(nOfwoman, perWoman, nOfInfraction, countInfByDay)

7 Exercice

  1. Trouver le pourcentage de femmes arrêtées en 2017 et afficher la phrase “…% des criminels en 2017 sont des femmes”
  2. Afficher le nombre de vols (ROBBERY) entre le 15 décembre et le 31 decembre 2017
  3. Afficher le quartier ayant connu le plus de vols entre le 15 et le 31 decembre 2017
  4. Afficher les 3 types d’infractions les plus commis
  5. Afficher le nombre d’infractions commis par des blancs de plus de 65 ans
  6. Afficher le nombre d’infractions commis par des blancs de plus de 65 ans, dans le quartier de Manhattan
  7. Afficher les 3 types d’infractions les plus commis par des blancs de plus de 65 ans, dans le quartier de Manhattan
  8. Afficher les 3 types d’infractions les plus commis par des noirs, âgés de 18 à 24 ans, dans le Bronx

8 Manipuler le tableau de données

# Premiere methode, compter le  nombre  d'infractions selon les genres avec la fonction 'table'
as.data.frame(table(dfFilter$SEX))
...      Var1   Freq
...   1 Femme  49796
...   2 Homme 235622
# Seconde methode, compter le  nombre  d'infractions selon les genres avec la fonction 'aggregate'
# Le parametre FUN de la fonction "aggregate" peut prendre plusieurs valeurs.
# Valeurs possibles selon le type de données : lenght, mean, sum, median, min, max 
df <- aggregate(INFRACTION ~ SEX, data = dfFilter, FUN = length)
df
...       SEX INFRACTION
...   1 Femme      49796
...   2 Homme     235622
# La fonction "aggregate" permet d'affiner la requete en combinant plusieurs variables
# Compter le  nombre d'infractions selon les genres et par quartier 
dfSexeQuartier <- aggregate(INFRACTION ~ SEX + ARREST_QUARTIER, data = dfFilter, FUN = length)
dfSexeQuartier
...        SEX ARREST_QUARTIER INFRACTION
...   1  Femme           Bronx      11190
...   2  Homme           Bronx      51376
...   3  Femme        Brooklyn      13569
...   4  Homme        Brooklyn      66405
...   5  Femme       Manhattan      13298
...   6  Homme       Manhattan      62630
...   7  Femme          Queens       9306
...   8  Homme          Queens      46271
...   9  Femme   Staten Island       2433
...   10 Homme   Staten Island       8940
dfSexeQuartier <- spread(dfSexeQuartier, key = SEX, value = INFRACTION)
dfSexeQuartier
...     ARREST_QUARTIER Femme Homme
...   1           Bronx 11190 51376
...   2        Brooklyn 13569 66405
...   3       Manhattan 13298 62630
...   4          Queens  9306 46271
...   5   Staten Island  2433  8940

Le package “tidyr” a été développé pour manipuler et mettre en forme des tableaux de données. La fonction “spread” issue de ce package permet de créer nouvelles colonnes à partir des modalités d’une variable.

9 Exercice

  1. Compter le nombre d’infractions selon les classes d’âge et par quartier, fonction “aggregate”.
  2. Puis stocker le résultat dans un tableau nommé “dfAgeQuartier”
# Afficher les deux tables
dfAgeQuartier
...     ARREST_QUARTIER  <18 18-24 25-44 45-64 65+
...   1           Bronx 4000 16391 31008 10731 436
...   2        Brooklyn 4814 18554 40320 15523 763
...   3       Manhattan 3245 17601 38165 15965 952
...   4          Queens 3311 14153 27921  9537 655
...   5   Staten Island  779  2718  5679  2080 117
dfSexeQuartier
...     ARREST_QUARTIER Femme Homme
...   1           Bronx 11190 51376
...   2        Brooklyn 13569 66405
...   3       Manhattan 13298 62630
...   4          Queens  9306 46271
...   5   Staten Island  2433  8940

10 Réaliser une jointure attributaire

# Joindre les deux tables
df <- merge(dfSexeQuartier,dfAgeQuartier, by='ARREST_QUARTIER')
df
...     ARREST_QUARTIER Femme Homme  <18 18-24 25-44 45-64 65+
...   1           Bronx 11190 51376 4000 16391 31008 10731 436
...   2        Brooklyn 13569 66405 4814 18554 40320 15523 763
...   3       Manhattan 13298 62630 3245 17601 38165 15965 952
...   4          Queens  9306 46271 3311 14153 27921  9537 655
...   5   Staten Island  2433  8940  779  2718  5679  2080 117
# Supprimer les variables inutiles pour la suite du script
rm(dfAgeQuartier, dfSexeQuartier)

11 Créer de la donnée

11.1 Requêter le jeu de données

# Identifier le crime majoritaire pour chaque quartier 
# Utilisation d'une boucle "For" pour itérer sur chaque quartier de New-York

# Exemple sur le quartier de Manhattan
# 1 ==> Selectionner tous les crimes pour Manhattan (Fonction "which")
tmp <- dfFilter[which(dfFilter$ARREST_QUARTIER == "Manhattan"),]
# 2 ==> Compter le nombre d'infraction par type (Fonction "table")
tmp <- table(tmp$INFRACTION)
# 3 ==> Trier le resultat par ordre decroissant (Fonction "sort") et ne garder que le nom du premier element du vecteur (Fonction "name"))
tmp <- sort(tmp,decreasing = T)[1]
names(tmp)
...   [1] "DANGEROUS DRUGS"
# Chainer toutes les fonctions sur une meme ligne
names(sort(table(dfFilter[which(dfFilter$ARREST_QUARTIER == "Manhattan"),"INFRACTION"]),decreasing = T)[1])
...   [1] "DANGEROUS DRUGS"

11.2 Structure de contrôle de flux

L’une des tâches que les machines font le mieux est la répétition de tâches identiques sans erreur.
Sauf mentions explicites, les instructions d’un programme s’exécutent les unes après les autres, dans l’ordre où elles ont été écrites à l’intérieur du script.
On parle de séquence d’instructions ou flux d’exécution. Il existe également des constructions qui modifient le flux d’exécution, les boucles par exemple. Elles permettent de répéter une ou plusieurs instructions plusieurs fois. La boucle for permet de faire des itérations sur un élément, comme une chaîne de caractères ou une liste.

# Exercice simple pour comprendre le fonctionnement des boucles "For"
# Afficher la liste des quartiers et le nombre d'elements dans la liste
# 1. Obtenir la liste des quartiers new-yorkais
listQuartier <- unique(dfFilter$ARREST_QUARTIER)
listQuartier
...   [1] "Manhattan"     "Staten Island" "Brooklyn"      "Queens"       
...   [5] "Bronx"
# 2. Afficher la longueur du vecteur
length(listQuartier)
...   [1] 5
# Iterer sur les elements (quartier) de la liste
# Creation d'une variable iterative "quartier"
# Elle prendra une nouvelle valeur a chaque itération
# 5 iterations au total (longueur du vecteur "listQuartier")
for(quartier in listQuartier){
  # Fonction "match" pour identifier la position du quartier dans la liste
  indexElem <- match(quartier, listQuartier)
  # Afficher l'index et le nom du quartier a partir de la variable iterative
  cat(indexElem, '+++',quartier,'\n')
}
...   1 +++ Manhattan 
...   2 +++ Staten Island 
...   3 +++ Brooklyn 
...   4 +++ Queens 
...   5 +++ Bronx
# Iterer sur les quartiers et appliquer les fonctions vues precedemment  (cf. section 11.1)
# Ajouter le resultat dans la table df
# Creer une nouvelle colonne vide, sans valeurs
df$Crime <- NULL

# Iterer sur chaque quartier avec la boucle 'For'
for(quartier in listQuartier){
  # Afficher le nom du quartier
  cat('+++',quartier,'\n')
  # Identifier le crime principal du quartier
  # /!\/!\Toutes les fonctions sont chainées sur une ligne/!\/!\
  crime <- names(sort(table(dfFilter[which(dfFilter$ARREST_QUARTIER == quartier),"INFRACTION"]),decreasing = T)[1])
  # Afficher le crime
  cat('------',crime,'\n')
  # Ajouter le crime dans la dataframe 'df'
  df[which(df$ARREST_QUARTIER == quartier),"Crime"] <- crime
}
...   +++ Manhattan 
...   ------ DANGEROUS DRUGS 
...   +++ Staten Island 
...   ------ DANGEROUS DRUGS 
...   +++ Brooklyn 
...   ------ DANGEROUS DRUGS 
...   +++ Queens 
...   ------ ASSAULT 3 & RELATED OFFENSES 
...   +++ Bronx 
...   ------ DANGEROUS DRUGS
# Renommer la premiere colonne
names(df)[1] <- "Quartiers"

# Afficher la table mais seulement la colonne 'Quartiers' et 'Crime'
df[,c('Quartiers','Crime')]
...         Quartiers                        Crime
...   1         Bronx              DANGEROUS DRUGS
...   2      Brooklyn              DANGEROUS DRUGS
...   3     Manhattan              DANGEROUS DRUGS
...   4        Queens ASSAULT 3 & RELATED OFFENSES
...   5 Staten Island              DANGEROUS DRUGS
# Creer de nouvelles colonnes
# Calculer le total de crimes par quartier (somme des colonnes homme et femme)
df$Total <- rowSums(df[, c('Femme','Homme')])

# Calculer le pourcentage d'hommes et de femmes par quartier (deux méthodes)
df$perFemme <- format((df$Femme/df$Total)*100, digits = 3)
df[['perHomme']] <- format((df[['Homme']]/df[['Total']])*100, digits = 3)

# Reorganiser les colonnes de la dataframe
df <- df[, c("Quartiers","Femme","perFemme","Homme","perHomme","<18","18-24","25-44","45-64","65+","Total","Crime")]

# Supprimer les variables inutiles pour la suite du script
rm(crime, indexElem, listQuartier, quartier, tmp)

12 Exercice

Objectif : Calculer la densité de population par quartier.

  1. Créer un tableau avec nombre d’habitants par quartiers (informations disponibles sur https://www.citypopulation.de/en/usa/newyorkcity/ )
    Commencer par un tableau “dfNY” contenant 2 colonnes : “quartiers” et “nbPop”
    Pour les valeurs numériques, indiquer les chiffres sans ponctuation.
  2. Ensuite, compléter le tableau avec les aires de chaque quartier.
  3. Calculer la densité par km² dans une nouvelle colonne du tableau, “nbPop”.
  4. Joindre deux tableaux de données “dfNY” et “df”.
  5. Sauvegarder le fichier en .csv et .Rdata.

13 Représentations graphiques

# Diagramme en barre
barplot(df$nbPop,
        names.arg = df$Quartiers,
        main = "Number of inhabitants by New York's districts",
        xlab = "Neighborhoods",
        ylab = "Number of inhabitants",
        col = 'lightblue')

# Sur le dataframe initial "dfFilter", faire un diagramme des groupes d'âge et des genres. 
# Pour représenter la distribution d'une variable qualitative, on utilise un diagramme en barres (barplot). Il est nécessaire de compter le nombre d'occurences de chaque niveau par la fonction table.  
barplot(table(dfFilter$AGE_GROUPE), 
        names.arg = names(table(dfFilter$AGE_GROUPE)),
        main = "Number of offenders by age group",
        col = 'lightblue')

barplot(table(dfFilter$SEX), 
        names.arg = names(table(dfFilter$SEX)),
        main = "Number of offenders by gender",
        col = 'lightblue')

1 - R et ses concepts.

3 - Représenter des données.