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.
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')
# Lecture du premier fichier :
df <- read.csv2("DONNEES//NYPD_Arrests_Data_2016-2018.csv')
# 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"
# 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",]
# 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")
# 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)
# 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.
# 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
# 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)
# 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"
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)
Objectif : Calculer la densité de population par quartier.
# 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')