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]:
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()
Out[4]:
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]:
In [22]:
dataset.shape
Out[22]:
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)
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]:
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)
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_))
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]:
In [ ]: