In [59]:
% matplotlib inline
from __future__ import print_function
Nous allons voir dans cet exemple comment utiliser la bibliothèque numpy
pour récupérer des valeurs dans un fichier csv et commencer à les traiter.
Nous allons utiliser le module csv
qui permet de lire un fichier csv et d'en extraire les valeurs lignes par lignes.
Nous allons travailler sur le fichier de données d'entraînement du Titanic. Le but est de prédire les chance de survie à bord du bateau. Il faut récupérer le fichier train.csv
(voir le premier cours ou téléchargez le depuis https://www.kaggle.com/c/titanic-gettingStarted/data ) et le sauvegarder dans le répertoire dans lequel le notebook s'éxecute. Vous pouvez utiliser la commande pwd
pour connaître ce répertoire. Sinon, vous pouvez déplacer le répertoire courant pour rejoindre l'endroit où vous avez sauvegardé votre fichier avec la commande cd
.
In [61]:
import csv
import numpy as np
fichier_csv = csv.reader(open('train.csv', 'r'))
entetes = fichier_csv.__next__() # on récupère la première ligne qui contient les entetes
donnees = list() # on crée la liste qui va servir à récupérer les données
for ligne in fichier_csv: # pour chaque ligne lue dans le fichier csv
donnees.append(ligne) # on ajoute les valeurs lues dans le tableau donness
#entete = donnees[0]
#donnees[0] = []
donnees = np.array(donnees) # le tableau donnees est transformé en numpy array
Regardons comment sont stockées les données en mémoire:
In [62]:
print (donnees)
Regardons maintenant la colonne de l'âge, n'affichons que les 15 premières valeurs:
In [63]:
print (donnees[1:15, 5])
On peut donc remarquer que les âges sont stockés comme des chaîne de caractères. Transformons les en réels :
In [76]:
donnees[1:15, 5].astype(np.int)
Numpy ne sait pas convertir la chaîne de caractère vide ''
(en 6e position dans notre liste) en réels. Pour traiter ces données, il faudrait écrire un petit algorithme. Nous allons voir comment on peut utiliser pandas
pour faire ces traitements beaucoup plus facilement.
In [14]:
import pandas as pd
import numpy as np
Pour lire le fichier csv nous allons utiliser la fonction read_csv
In [15]:
df = pd.read_csv('train.csv')
Pour vérifier si cela a bien fonctionné, affichons les premières valeurs. On voit apparaître l'identifiant du passager, s'il a survécu, sa classe, son nom, son sexe, son âge, le nombre de frères/soeurs/époux/épouse sur le bâteau, le nombre de parents ou d'enfants, le numéro de ticket, le prix, le numéro de cabine et le port d'embarquement.
In [16]:
df.head(6)
Out[16]:
Comparons le type de donnees
, obtenu précédemment. C'est un numpy array. Le type de df
est un objet spécifique à pandas
.
In [17]:
type(donnees)
Out[17]:
In [18]:
type(df)
Out[18]:
Nous avions vu qu'avec numpy
, toutes les valeurs importées étaient des chaînes de caractères. Vérifions ce qu'il en est avec pandas
In [19]:
df.dtypes
Out[19]:
On peut voir que Pandas
a détecté automatiquement le types des données de notre fichier csv: soit des entiers, soit des réels, soit des objets (chaînes de caractères). Il y a deux commandes importantes à connaître, c'est df.info()
et df.describe()
In [20]:
df.info()
L'âge n'est pas renseigné pour tous les passagers, seulement pour 714 passagers sur 891. Idem pour le numéro de cabine et le port d'embarquement. On peut également utiliser describe()
pour calculer plusieurs indicateurs statistiques utiles.
In [21]:
df.describe()
Out[21]:
On peut voir que pandas
a calculé automatiquement les indicateurs statistiques en tenant compte uniquement des données renseignées. Par exemple, il a calculé la moyenne d'âge uniquement sur les 714 valeurs connues. pandas
a laissé de coté les valeurs non-numériques (nom, sexe, ticket, cabine, port d'embarquement).
Pour afficher uniquement les 15 premières valeurs de la colonne âge :
In [22]:
df['Age'][0:15]
Out[22]:
On peut également utiliser la syntaxe
In [23]:
df.Age[0:15]
Out[23]:
On peut calculer des critères statistiques directement sur les colonnes
In [24]:
df.Age.mean()
Out[24]:
On peut voir que c'est la même valeur que celle affichée dans describe
. Cette syntaxe permet d'utiliser facilement la valeur de la moyenne dans des calculs ou des algorithmes.
Pour filtrer les données, on va passer la liste de colonnes désirées:
In [25]:
colonnes_interessantes = ['Sex', 'Pclass', 'Age']
df[ colonnes_interessantes ]
Out[25]:
En analyse, on est souvent intéressé par filtrer les données en fonction de certains critères. Par exemple, l'âge maximum est 80 ans. On peut examiner les informations relatives aux personnes âgées :
In [26]:
df[df['Age'] > 60]
Out[26]:
Comme on a trop d'informations, on peut les filtrer:
In [27]:
df[df['Age'] > 60][['Pclass', 'Sex', 'Age', 'Survived']]
Out[27]:
On peut voir que parmis les persones âges, il y a principalement des hommes. Les personnes qui ont survécues était principalement des femmes.
Nous allons maintenant voir comment traiter les valeurs manquantes pour l'âge. Nous allons filtrer les données pour afficher uniquement les valeurs manquantes
In [28]:
df[df.Age.isnull()][['Sex', 'Pclass', 'Age']]
Out[28]:
Pour combiner des filtres, on peut utiliser '&'
. Affichons le nombre d'hommes dans chaque classe
In [34]:
for i in range(1, 4):
print ("Dans la classe", i, ", il y a", len( df[ (df['Sex'] == 'male') & (df['Pclass'] == i) ]), "hommes")
print ("Dans la classe", i, ", il y a", len( df[ (df['Sex'] == 'female') & (df['Pclass'] == i) ]), "femmes")
Visualisons maintenant l'histogramme de répartition des âges.
In [32]:
df.Age.hist(bins=20, range=(0,80))
Out[32]:
Pour pouvoir exploiter les informations sur le sexe des personnes, nous allons ajouter une nouvelle colonne, appellée genre, qui vaudra 1 pour les hommes et 0 pour les femmes.
In [35]:
df['Gender'] = 4 # on ajoute une nouvelle colonne dans laquelle toutes les valeurs sont à 4
df.head()
Out[35]:
In [36]:
df['Gender'] = df['Sex'].map( {'female': 0, 'male': 1} ) # la colonne Gender prend 0 pour les femmes et 1 pour les hommes
df.head()
Out[36]:
Pour créer et renommer de nouvelles colonnes, on peut également agréger des informations issues de différentes colonnes. Créons par exemple une colonne pour stocker les nombre de personnes de la même famille à bord du Titanic.
In [37]:
df['FamilySize'] = df.SibSp + df.Parch
df.head()
Out[37]:
Nous allons remplir les valeurs manquantes de l'âge avec la valeur médiane dépendant de la classe et du sexe.
In [38]:
ages_medians = np.zeros((2, 3))
ages_medians
Out[38]:
In [39]:
for i in range(0,2):
for j in range(0,3):
ages_medians[i,j] = df[ (df['Gender'] == i) & (df['Pclass'] == j+1) ]['Age'].median()
ages_medians
Out[39]:
On va créer une nouvelle colonne AgeFill qui va utiliser ces âges médians
In [40]:
for i in range(0, 2):
for j in range (0, 3):
df.loc[ (df.Age.isnull()) & (df.Gender == i) & (df.Pclass == j+1), 'AgeFill'] = ages_medians[i,j]
# pour afficher les 10 premières valeurs qui sont complétées
df [df.Age.isnull()][['Gender', 'Pclass', 'Age', 'AgeFill']].head(10)
Out[40]:
Pour sauvegarder votre travail, vous pouvez utiliser le module pickle
qui compresse et sauvegarde vos données :
In [56]:
import pickle
f = open('masauvegarde.pck', 'wb')
pickle.dump(df, f)
f.close()
Pour récuperer votre travail, on utilise l'opération inverse, toujours avec pickle
In [58]:
with open('masauvegarde.pck', 'rb') as f:
dff = pickle.load(f)
Pour faire de l'apprentissage, et prédire la survie des passagers du Titanic, on peut utiliser scikit-learn. Ce dernier prend en entrée des données sous forme de numpy array, la conversion se fait simplement :
In [43]:
ex = df[ ['Gender', 'Pclass'] ] # on choisit seulement quelques features.
X = ex.as_matrix() # on convertit en numpy array
print(ex.head(5))
print(X[:5,:])
On cherche à prévoir la survie, on extrait donc l'information utile :
In [44]:
y = df['Survived'].as_matrix()
print (y[:5])
In [45]:
from sklearn import svm
clf = svm.SVC()
clf.fit(X,y)
Out[45]:
L'apprentissage du classifieur est fait, c'est-à-dire que nous avons entraîné une SVM sur nos données $X$ pour qu'elle soit capable de prédire la survie $y$. Pour vérifier que notre SVM a bien appris à prédire la survie des passagers, nous pouvons utiliser la méthode predict()
et comparer visuellement pour les dix premières valeurs prédite par la SVM et la survie réelle des passagers.
In [46]:
print(clf.predict(X[:10,:]))
print (y[:10])
La SVM a bien appris à prédire ce que nous lui avons montré. Cela ne permet pas cependant d'évaluer sa capacité à généraliser à des cas qu'elle n'a pas vu. Pour ce faire, une approche classique est de faire de la validation croisée, c'est à dire qu'on entraîne le classifieur sur une partie des données et qu'on le teste sur une autre. Scikit-learn en donne une implémentation très simple d'utilisation.
In [51]:
from sklearn import cross_validation
scores = cross_validation.cross_val_score(clf, X, y, cv=7)
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))
Sur les 7 partitions de nos données, la SVM prédit la survie des passagers dans 77% des cas, avec un écart-type de 0,04.
Pour améliorer les résultats, nous pouvons rajouter l'âge dans les features. Il faut cependant faire attention aux valeurs non-renseignées NaN, nous allons donc utiliser une nouvelle colonne AgeFilled avec l'âge ou la médiane.
In [52]:
df['AgeFilled'] = df.Age # on recopie la colonne Age
df.loc[df.AgeFilled.isnull(), 'AgeFilled'] = df[df.Age.isnull()]['AgeFill'] # on met l'age médian pour les valeurs non renseignées
Nous pouvons maintenant créer un nouveau $X$ incluant l'âge, en plus du sexe et de la classe, et vérifier si cela améliore les performances de la SVM.
In [54]:
X = df[['Gender', 'Pclass', 'AgeFilled']].as_matrix()
scores = cross_validation.cross_val_score(svm.SVC(), X, y, cv=7)
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))
Nous pouvons constater que les performances sont légèrement meilleures. Mais comme l'écart-type a également grandi, il n'est pas possible de conclure sur la significativité de cet apport. Il est facilement possible d'améliorer ce score de prédiction, n'hésitez pas à consulter la page kaggle qui y est consacrée.
Une analyse intéressante, avec de bons résultats en prédiction est disponible ici
Ce notebook est une adaptation de celui proposé sur la page kaggle : https://www.kaggle.com/c/titanic-gettingStarted/details/getting-started-with-python-ii