In [1]:
# Change HTML style
from IPython.core.display import HTML
HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
""")


Out[1]:

In [2]:
# Imports and settings
import warnings
warnings.filterwarnings('ignore')
import utils
import os
import numpy as np
import pandas as pd
import matplotlib
from matplotlib import pyplot as plt
matplotlib.style.use('ggplot')
pd.options.display.max_columns = 100
pd.options.display.max_rows = 100

Partendo dal dataset salvato precedentemente, cerchiamo di mettere su un algoritmo predittivo, atto a capire quali fattori causano o meno un incidente il cui esito sul tipo di lesione posso essere "Illeso", "Rimandato", "Ricoverato" o "Deceduto". Dato che abbiamo scelto di lavorare con dati di tipo categorico, la scelta migliore è quella di usare gli alberi, che si prestano molto bene a lavorare con questo tipologia di dati. In particolare, utilizzeremo la tecnica del bootstrap aggregation (bagging), per ottenere un modello predittivo robusto e con buona capacità di generalizzazione.


In [3]:
# Load dataset
load_path = r"0_CarIncident_2014"
dataset = pd.read_pickle(load_path)
dataset.drop('IDProtocollo', inplace=True, axis=1)
dataset.drop('Progressivo', inplace=True, axis=1)
dataset.describe()


Out[3]:
NaturaIncidente ParticolaritaStrade TipoStrada FondoStradale Pavimentazione Segnaletica CondizioneAtmosferica Traffico Visibilità Illuminazione TipoVeicolo TipoPersona Sesso TipoLesione FaseGiorno DimensioneIncidente FasciaEta
count 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074 70074
unique 8 7 5 3 3 5 3 3 3 4 14 3 2 4 6 3 6
top MarciaVsMarcia Rettilineo Una carreggiata a doppio senso Asciutto Regolare Verticale ed orizzontale Sereno Normale Buona Ore Diurne Autovettura Conducente M Illeso PomeriggioTardi Piccolo Adulto
freq 38664 36543 33481 56912 66915 49314 47909 42268 57757 50130 53091 50353 44677 54066 19427 49315 28562

Per prima cosa, creiamo la colonna delle label. Questo passaggio è necessario perchè la maggior parte delle librerie si aspetta le label in formato [0, 1] nei casi di classificazione binaria e in formato [0, 1, ..., N] nel caso di classificazione a N classi. Inoltre, per trattare i dati di tipo categorico, scegliamo la tecnica del one-hot encoding, anche detta tecnica delle dummy variables. Per fare un esempio, la colonna "TipoPersona", che ha i valori "Conducente", "Passeggero" e "Pedone", viene trasformata in 3 colonne, ovvero "TipoPersona_Conducente", "TipoPersona_Passeggero" e "TipoPersona_Pedone"; le colonne avranno un 1 in corrispondenza della categoria indicata dal record in esame.


In [4]:
from sklearn.preprocessing import LabelEncoder

# Create labels for classification
number = LabelEncoder()
dataset['TipoLesione'] = number.fit_transform(dataset['TipoLesione'].astype('str'))
print(list(number.classes_))
print(number.transform(number.classes_))
dataset.head()


['Deceduto', 'Illeso', 'Ricoverato', 'Rimandato']
[0 1 2 3]
Out[4]:
NaturaIncidente ParticolaritaStrade TipoStrada FondoStradale Pavimentazione Segnaletica CondizioneAtmosferica Traffico Visibilità Illuminazione TipoVeicolo TipoPersona Sesso TipoLesione FaseGiorno DimensioneIncidente FasciaEta
0 MarciaVsMarcia Rettilineo Piu di due carreggiate Asciutto Regolare Verticale ed orizzontale Sereno Intenso Sufficiente Sufficiente Motociclo Conducente M 3 SeraTardi Piccolo Adulto
1 MarciaVsMarcia Rettilineo Piu di due carreggiate Asciutto Regolare Verticale ed orizzontale Sereno Intenso Sufficiente Sufficiente Autovettura Conducente F 1 SeraTardi Piccolo Adulto
2 MarciaVsMarcia Rettilineo Piu di due carreggiate Asciutto Regolare Verticale ed orizzontale Sereno Intenso Sufficiente Sufficiente Autovettura Passeggero F 1 SeraTardi Piccolo Adulto
3 MarciaVsMarcia Rettilineo Una carreggiata a doppio senso Asciutto Regolare Verticale ed orizzontale Sereno Normale Buona Sufficiente Autovettura Conducente M 1 SeraTardi Medio Anziano
4 MarciaVsMarcia Rettilineo Una carreggiata a doppio senso Asciutto Regolare Verticale ed orizzontale Sereno Normale Buona Sufficiente Autovettura Passeggero M 1 SeraTardi Medio Anziano

In [5]:
# NaturaIncidente dummies
NaturaIncidente_dummies = pd.get_dummies(dataset['NaturaIncidente'], prefix='NaturaIncidente')
dataset = pd.concat([dataset, NaturaIncidente_dummies], axis=1)
dataset.drop('NaturaIncidente', inplace=True, axis=1)

In [6]:
# ParticolaritaStrade dummies
ParticolaritaStrade_dummies = pd.get_dummies(dataset['ParticolaritaStrade'], prefix='ParticolaritaStrade')
dataset = pd.concat([dataset, ParticolaritaStrade_dummies], axis=1)
dataset.drop('ParticolaritaStrade', inplace=True, axis=1)

In [7]:
# TipoStrada dummies
TipoStrada_dummies = pd.get_dummies(dataset['TipoStrada'], prefix='TipoStrada')
dataset = pd.concat([dataset, TipoStrada_dummies], axis=1)
dataset.drop('TipoStrada', inplace=True, axis=1)

In [8]:
# FondoStradale dummies
FondoStradale_dummies = pd.get_dummies(dataset['FondoStradale'], prefix='FondoStradale')
dataset = pd.concat([dataset, FondoStradale_dummies], axis=1)
dataset.drop('FondoStradale', inplace=True, axis=1)

In [9]:
# Pavimentazione dummies
Pavimentazione_dummies = pd.get_dummies(dataset['Pavimentazione'], prefix='Pavimentazione')
dataset = pd.concat([dataset, Pavimentazione_dummies], axis=1)
dataset.drop('Pavimentazione', inplace=True, axis=1)

In [10]:
# Segnaletica dummies
Segnaletica_dummies = pd.get_dummies(dataset['Segnaletica'], prefix='Segnaletica')
dataset = pd.concat([dataset, Segnaletica_dummies], axis=1)
dataset.drop('Segnaletica', inplace=True, axis=1)

In [11]:
# CondizioneAtmosferica dummies
CondizioneAtmosferica_dummies = pd.get_dummies(dataset['CondizioneAtmosferica'], prefix='CondizioneAtmosferica')
dataset = pd.concat([dataset, CondizioneAtmosferica_dummies], axis=1)
dataset.drop('CondizioneAtmosferica', inplace=True, axis=1)

In [12]:
# Traffico dummies
Traffico_dummies = pd.get_dummies(dataset['Traffico'], prefix='Traffico')
dataset = pd.concat([dataset, Traffico_dummies], axis=1)
dataset.drop('Traffico', inplace=True, axis=1)

In [13]:
# Visibilità dummies
Visibilità_dummies = pd.get_dummies(dataset['Visibilità'], prefix='Visibilità')
dataset = pd.concat([dataset, Visibilità_dummies], axis=1)
dataset.drop('Visibilità', inplace=True, axis=1)

In [14]:
# Illuminazione dummies
Illuminazione_dummies = pd.get_dummies(dataset['Illuminazione'], prefix='Illuminazione')
dataset = pd.concat([dataset, Illuminazione_dummies], axis=1)
dataset.drop('Illuminazione', inplace=True, axis=1)

In [15]:
# TipoVeicolo dummies
TipoVeicolo_dummies = pd.get_dummies(dataset['TipoVeicolo'], prefix='TipoVeicolo')
dataset = pd.concat([dataset, TipoVeicolo_dummies], axis=1)
dataset.drop('TipoVeicolo', inplace=True, axis=1)

In [16]:
# TipoPersona dummies
TipoPersona_dummies = pd.get_dummies(dataset['TipoPersona'], prefix='TipoPersona')
dataset = pd.concat([dataset, TipoPersona_dummies], axis=1)
dataset.drop('TipoPersona', inplace=True, axis=1)

In [17]:
# Sesso dummies
Sesso_dummies = pd.get_dummies(dataset['Sesso'], prefix='Sesso')
dataset = pd.concat([dataset, Sesso_dummies], axis=1)
dataset.drop('Sesso', inplace=True, axis=1)

In [18]:
# FaseGiorno dummies
FaseGiorno_dummies = pd.get_dummies(dataset['FaseGiorno'], prefix='FaseGiorno')
dataset = pd.concat([dataset, FaseGiorno_dummies], axis=1)
dataset.drop('FaseGiorno', inplace=True, axis=1)

In [19]:
# DimensioneIncidente dummies
DimensioneIncidente_dummies = pd.get_dummies(dataset['DimensioneIncidente'], prefix='DimensioneIncidente')
dataset = pd.concat([dataset, DimensioneIncidente_dummies], axis=1)
dataset.drop('DimensioneIncidente', inplace=True, axis=1)

In [20]:
# FasciaEta dummies
FasciaEta_dummies = pd.get_dummies(dataset['FasciaEta'], prefix='FasciaEta')
dataset = pd.concat([dataset, FasciaEta_dummies], axis=1)
dataset.drop('FasciaEta', inplace=True, axis=1)

In [21]:
dataset.head()


Out[21]:
TipoLesione NaturaIncidente_Fuoriuscita NaturaIncidente_Infortunio NaturaIncidente_Investimento NaturaIncidente_MarciaVsFermo NaturaIncidente_MarciaVsMarcia NaturaIncidente_MarciaVsOstacolo NaturaIncidente_Multiplo NaturaIncidente_Ribaltamento ParticolaritaStrade_Altro ParticolaritaStrade_Curva ParticolaritaStrade_Galleria ParticolaritaStrade_Incrocio ParticolaritaStrade_IntersezioneControllata ParticolaritaStrade_IntersezioneNonControllata ParticolaritaStrade_Rettilineo TipoStrada_Due carreggiate TipoStrada_Piu di due carreggiate TipoStrada_Una carreggiata a doppio senso TipoStrada_Una carreggiata a senso unico alternato TipoStrada_Una carreggiata a senso unico di marcia FondoStradale_Altro FondoStradale_Asciutto FondoStradale_Bagnato Pavimentazione_Dissestata Pavimentazione_Lastricata Pavimentazione_Regolare Segnaletica_Assente Segnaletica_Orizzontale Segnaletica_Temporanea di cantiere Segnaletica_Verticale Segnaletica_Verticale ed orizzontale CondizioneAtmosferica_Nuvoloso CondizioneAtmosferica_Precipitazioni CondizioneAtmosferica_Sereno Traffico_Intenso Traffico_Normale Traffico_Scarso Visibilità_Buona Visibilità_Insufficiente Visibilità_Sufficiente Illuminazione_Inesistente Illuminazione_Insufficiente Illuminazione_Ore Diurne Illuminazione_Sufficiente TipoVeicolo_Altro TipoVeicolo_Autobus TipoVeicolo_Autocaravan TipoVeicolo_Autocarro TipoVeicolo_Autotreno TipoVeicolo_Autovettura TipoVeicolo_Bicicletta TipoVeicolo_Ciclomotore TipoVeicolo_Motociclo TipoVeicolo_Opera TipoVeicolo_Piedi TipoVeicolo_Soccorso TipoVeicolo_Speciale TipoVeicolo_Tram TipoPersona_Conducente TipoPersona_Passeggero TipoPersona_Pedone Sesso_F Sesso_M FaseGiorno_MattinaPresto FaseGiorno_MattinaTardi FaseGiorno_PomeriggioPresto FaseGiorno_PomeriggioTardi FaseGiorno_SeraPresto FaseGiorno_SeraTardi DimensioneIncidente_Grande DimensioneIncidente_Medio DimensioneIncidente_Piccolo FasciaEta_Adulto FasciaEta_Anziano FasciaEta_Bambino FasciaEta_Neonato FasciaEta_NonnoSimpson FasciaEta_Ragazzo
0 3 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0
1 1 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0
2 1 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0
3 1 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
4 1 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0

In [22]:
dataset.shape


Out[22]:
(70074, 79)

Dividiamo ora il dataset in training set e test set, mantenendo un rapporto 0,75 0,25 tra i due. Il training set verrà usato per fare cross validation, mentre il test set per misurare le performance del modello.


In [23]:
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectKBest
from sklearn.cross_validation import StratifiedKFold
from sklearn.grid_search import GridSearchCV
from sklearn.ensemble.gradient_boosting import GradientBoostingClassifier
from sklearn.cross_validation import cross_val_score
from sklearn.cross_validation import train_test_split

# Generate training set and test set
targets = dataset['TipoLesione']
dataset.drop('TipoLesione', inplace=True, axis=1)
X_train, X_test, y_train, y_test = train_test_split(dataset, targets, test_size=0.25, random_state=42)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)


(52555, 78)
(17519, 78)
(52555,)
(17519,)

La tecnica del one-hot encoding aumenta notevolmente in numero di features con le quali l'algoritmo deve avere a che fare, appesantendolo di conseguenza e introducendo, in alcuni casi, troppa varianza. Infatti, arrivati a questo punto abbiamo ottenuto 79 features, e sarebbe preferibile lavorare con molte meno. Utilizziamo perciò la tecnica degli extra-tree per selezionare, all'interno del training set le feature più importanti e, successivamente, rimappare training e test in due nuovi set, più facili da trattare.


In [25]:
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.feature_selection import SelectFromModel

# Feature selection
clf = ExtraTreesClassifier(n_estimators=200, class_weight='balanced')
clf = clf.fit(X_train, y_train)

In [26]:
# Show feature importance
features = pd.DataFrame()
features['feature'] = X_train.columns
features['importance'] = clf.feature_importances_
features.sort(['importance'], ascending=False)


Out[26]:
feature importance
49 TipoVeicolo_Autovettura 0.045351
52 TipoVeicolo_Motociclo 0.034510
17 TipoStrada_Una carreggiata a doppio senso 0.031195
14 ParticolaritaStrade_Rettilineo 0.028215
72 FasciaEta_Adulto 0.028083
66 FaseGiorno_PomeriggioTardi 0.026215
73 FasciaEta_Anziano 0.025952
30 Segnaletica_Verticale ed orizzontale 0.024782
15 TipoStrada_Due carreggiate 0.024144
64 FaseGiorno_MattinaTardi 0.023221
65 FaseGiorno_PomeriggioPresto 0.022773
31 CondizioneAtmosferica_Nuvoloso 0.022283
76 FasciaEta_NonnoSimpson 0.022000
4 NaturaIncidente_MarciaVsMarcia 0.021892
33 CondizioneAtmosferica_Sereno 0.021656
35 Traffico_Normale 0.021472
36 Traffico_Scarso 0.020894
27 Segnaletica_Orizzontale 0.020239
77 FasciaEta_Ragazzo 0.020073
19 TipoStrada_Una carreggiata a senso unico di ma... 0.020016
11 ParticolaritaStrade_Incrocio 0.019902
12 ParticolaritaStrade_IntersezioneControllata 0.018531
61 Sesso_F 0.016861
62 Sesso_M 0.016811
16 TipoStrada_Piu di due carreggiate 0.016795
63 FaseGiorno_MattinaPresto 0.016071
37 Visibilità_Buona 0.015988
71 DimensioneIncidente_Piccolo 0.015982
42 Illuminazione_Ore Diurne 0.015888
9 ParticolaritaStrade_Curva 0.015670
70 DimensioneIncidente_Medio 0.015446
43 Illuminazione_Sufficiente 0.015244
3 NaturaIncidente_MarciaVsFermo 0.014990
5 NaturaIncidente_MarciaVsOstacolo 0.014794
21 FondoStradale_Asciutto 0.014254
58 TipoPersona_Conducente 0.014130
39 Visibilità_Sufficiente 0.013661
67 FaseGiorno_SeraPresto 0.013585
22 FondoStradale_Bagnato 0.013377
26 Segnaletica_Assente 0.013327
2 NaturaIncidente_Investimento 0.012705
59 TipoPersona_Passeggero 0.012415
54 TipoVeicolo_Piedi 0.012385
34 Traffico_Intenso 0.012045
60 TipoPersona_Pedone 0.010757
40 Illuminazione_Inesistente 0.010044
68 FaseGiorno_SeraTardi 0.009965
29 Segnaletica_Verticale 0.009303
32 CondizioneAtmosferica_Precipitazioni 0.008247
47 TipoVeicolo_Autocarro 0.007949
38 Visibilità_Insufficiente 0.006669
25 Pavimentazione_Regolare 0.006511
6 NaturaIncidente_Multiplo 0.006404
74 FasciaEta_Bambino 0.005560
50 TipoVeicolo_Bicicletta 0.005493
51 TipoVeicolo_Ciclomotore 0.005277
24 Pavimentazione_Lastricata 0.004777
0 NaturaIncidente_Fuoriuscita 0.004565
1 NaturaIncidente_Infortunio 0.004017
69 DimensioneIncidente_Grande 0.003928
41 Illuminazione_Insufficiente 0.003744
13 ParticolaritaStrade_IntersezioneNonControllata 0.002968
7 NaturaIncidente_Ribaltamento 0.002772
45 TipoVeicolo_Autobus 0.002403
23 Pavimentazione_Dissestata 0.002381
75 FasciaEta_Neonato 0.002370
10 ParticolaritaStrade_Galleria 0.002007
8 ParticolaritaStrade_Altro 0.001686
28 Segnaletica_Temporanea di cantiere 0.001228
20 FondoStradale_Altro 0.001194
18 TipoStrada_Una carreggiata a senso unico alter... 0.000462
55 TipoVeicolo_Soccorso 0.000372
48 TipoVeicolo_Autotreno 0.000290
56 TipoVeicolo_Speciale 0.000282
53 TipoVeicolo_Opera 0.000259
57 TipoVeicolo_Tram 0.000125
44 TipoVeicolo_Altro 0.000114
46 TipoVeicolo_Autocaravan 0.000053

In [27]:
model = SelectFromModel(clf, prefit=True)
X_train_new = model.transform(X_train)
X_test_new = model.transform(X_test)
print(X_train_new.shape)
print(X_test_new.shape)


(52555, 40)
(17519, 40)

Tutto è pronto per lanciare l'algoritmo!!! Andiamo ad impostare i parametri per la cross validation, lanciamo l'algoritmo e aspettiamo che il Random Forrest faccia il suo lavoro.


In [32]:
# Random Forest with grid search Cross Validation
forest = RandomForestClassifier(max_features='sqrt', class_weight='balanced')

parameter_grid = {
                     'max_depth' : [6, 8, 10, 12, 14],
                     'n_estimators': [200, 220, 240, 260],
                     'criterion': ['gini', 'entropy'],     
                 }

cross_validation = StratifiedKFold(y_train, n_folds=5)

grid_search = GridSearchCV(forest,
                           param_grid=parameter_grid,
                           cv=cross_validation)

grid_search.fit(X_train_new, y_train)

print('Best score: {}'.format(grid_search.best_score_))
print('Best parameters: {}'.format(grid_search.best_params_))


Best score: 0.7869279802112074
Best parameters: {'criterion': 'entropy', 'max_depth': 12, 'n_estimators': 200}

Come possiamo vedere, la capacità di generalizzazione del modello è molto buona, dato che lo score ottenuto e pressocchè simile sia per il training set che per il test set. L'accuracy ottenuta è molto buona, considerando che stiamo lavorando su una dataset proveniente dal mondo Open Data. Possiamo certamente migliorare, scegliendo migliori mappature per le feature in fase di preparazione e raffinamento del dataset e provando modelli diversi, come logistic regression, gradient boosted trees o gaussian process.


In [33]:
from sklearn.metrics import accuracy_score

# Compute score
y_pred = grid_search.predict(X_test_new).astype(int)
accuracy_score(y_test, y_pred)


Out[33]:
0.78520463496774928

In [ ]: