Visualisation sous Python

Dans ce TP, nous allons utiliser le package seaborn, ainsi que le jeu de données tips, vu dans le TP précédent

In [1]:
import numpy
import pandas
tips = pandas.read_csv("tips.csv", header = 0, sep = ",")
tips.head()
Out[1]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

seaborn

Ce module, importé ci-dessous, est basé sur matplotlib. Il faut donc ajouter la ligne %matplotlib inline dans un notebook, pour pouvoir voir les graphiques.

Ce module contient toutes les fonctions directement, l'importation est donc assez simple.

In [2]:
import seaborn

%matplotlib inline

Variable quantitative

La fonction distplot() nous permet de réaliser les graphiques de distribution d'une variable quantitative. Par défaut, elle réaliser un histogramme avec une estimation de la densité.

In [3]:
seaborn.distplot(tips.total_bill)
Out[3]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3df099f7b8>

Pour ne garder que l'histogramme, on indique qu'on ne souhaite pas l'estimation de la densité (paramètre kde). Ainsi, l'histogramme devient en effectifs.

In [4]:
seaborn.distplot(tips.total_bill, kde = False)
Out[4]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dee89ea20>

De même, on ne peut vouloir que la densité. Auquel cas, on supprimer l'histogramme avec le paramètre hist.

In [5]:
seaborn.distplot(tips.total_bill, hist = False)
Out[5]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dee18d278>

Il est possible de choisir le nombre de bins, avec le paramètre bins.

In [6]:
seaborn.distplot(tips.total_bill, bins = 6, kde = False)
Out[6]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dee108358>

Il est aussi possible de choisir les limites des intervalles, avec le même paramètre bins. Dans ce cas, il faut bien évidemment veiller à faire un histogramme en densité. Si on ne souhaite pas avoir l'estimation de la densité, on peut l'obtenir tout de même avec le paramètre norm_hist.

In [7]:
seaborn.distplot(tips.total_bill, bins = [0, 10, 25, 60], norm_hist = True, kde = False)
Out[7]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dee0704a8>

C'est la fonction boxplot() qui nous permet de réaliser une boîte à moustache (soit verticale en mettant la variable en y, soit horizontale en la mettant en x).

In [8]:
seaborn.boxplot(y = "total_bill", data = tips)
Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dee070160>
In [9]:
seaborn.boxplot(x = "total_bill", data=tips)
Out[9]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedfbe780>

Une autre représentation possible est obtenue avec la fonction pointplot(), qui représente la moyenne et l'écarte-type, avec le choix entre vertical (y) ou horizontal (x).

In [10]:
seaborn.pointplot(y = "total_bill", data = tips)
Out[10]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedf92898>
In [11]:
seaborn.pointplot(x = "total_bill", data = tips)
Out[11]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedef52e8>

Un autre graphique possible est celui obtenu avec violinplot(), qui représente la densité d'une variable, toujours avec le choix vertical/horizontale (y/x).

In [12]:
seaborn.violinplot(y = "total_bill", data = tips)
Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedebdcf8>
In [13]:
seaborn.violinplot(x = "total_bill", data = tips)
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dede9fa90>

Enfin, il est possible de représenter toutes les valeurs sur un pseudo nuage de points. Avec striplot() dont l'option jitter a été activée, les points sont aléatoirement répartis sur l'axe des $x$ (si on utilise y - inversement sinon).

In [14]:
seaborn.stripplot(y = "total_bill", data = tips, jitter = True)
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3deddf46a0>
In [15]:
seaborn.stripplot(x = "total_bill", data = tips, jitter = True)
Out[15]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3deddd0eb8>

La fonction catplot() (anciennement factorplot() pour information) regroupe l'ensemble des graphiques précédents (sauf l'histogramme) en une seule fonction. On accède aux différentes sous-fonctions en indiquant dans le paramètre kind :

  • point : moyenne +/- écart-type
  • box : boîte à moustaches
  • violin : denisté (en symétrie)
  • strip : pseudo nuage de points
In [16]:
seaborn.catplot(y = "total_bill", data = tips, kind = "point")
Out[16]:
<seaborn.axisgrid.FacetGrid at 0x7f3dede97fd0>

Variable qualitative

Le diagramme en barres en effectifs est obtenu via la fonction countplot(). Il est soit horizontal (avec la variable en x), soit vertical (en y).

In [17]:
seaborn.countplot(x = "sex", data = tips)
Out[17]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedcf43c8>
In [18]:
seaborn.countplot(y = "sex", data = tips)
Out[18]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedc6a710>

Pour avoir la version en pourcentages (ou en proportions) de ce graphique, nous devons utiliser la fonction barplot(), sur la table de proportions calculée avant. Cette fonction réalise un calcul (moyenne par défaut) sur une variable (ici freq) en fonction des modalités d'une autre variable (sex ici donc).

In [19]:
t = pandas.crosstab(tips.sex, "freq", normalize=True)
t = t.assign(sex = t.index, freq = 100 * t.freq)
seaborn.barplot(x = "sex", y = "freq", data = t)
Out[19]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedc35550>
In [20]:
seaborn.barplot(x = "freq", y = "sex", data = t)
Out[20]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dedbab668>

Pour réaliser un diagramme en barres empilées, il faudra le créer soi-même. Nous ne verrons pas ici.

Enfin, comme précédemment, la fonction catplot() nous permet d'accéder aux sous-fonctions avec le choix dans kind.

  • count : diagramme en barres (dénombrement)
  • bar : diagramme en barres avec calcul
In [21]:
seaborn.catplot(x = "sex", data = tips, kind = "count")
Out[21]:
<seaborn.axisgrid.FacetGrid at 0x7f3dedbadda0>

Var quantitative - Var quantitative

Pour réaliser le nuage de points, on utilise la fonction jointplot(). Elle a l'avantage d'ajouter par défaut les histogrammes de chaque variable. Elle réalise par défaut le nuage de points simple (scatter). Comme pour catplot(), on va pouvoir choisir le type de graphique avec le paramètre kind.

In [22]:
seaborn.jointplot(x = "total_bill", y = "tip", data = tips)
Out[22]:
<seaborn.axisgrid.JointGrid at 0x7f3dedaf1208>

En choississant le type reg, on obtient en plus l'ajustement linéaire de la variable en y par celle en x.

In [23]:
seaborn.jointplot(x = "total_bill", y = "tip", data = tips, kind = "reg")
Out[23]:
<seaborn.axisgrid.JointGrid at 0x7f3deddefd30>

On peut obtenir une heatmap, non pas avec des rectangles mais un pavage hexagonal, avec kind = "hex".

In [24]:
seaborn.jointplot(x = "total_bill", y = "tip", data = tips, kind = "hex")
Out[24]:
<seaborn.axisgrid.JointGrid at 0x7f3ded88a828>

Enfin, on peut avoir une estimation de la densité en 2d avec le type kde.

In [25]:
seaborn.jointplot(x = "total_bill", y = "tip", data = tips, kind = "kde")
Out[25]:
<seaborn.axisgrid.JointGrid at 0x7f3ded741358>

Si on souhaite ne pas avoir les distributions marginales, la fonction regplot() nous permet de réaliser le nuage de points avec ou sans ajustement (paramètre fit_reg). On peut aussi n'afficher que l'ajustement.

In [26]:
seaborn.regplot("total_bill", "tip", data = tips, fit_reg = False)
Out[26]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3ded3c2a90>
In [27]:
seaborn.regplot("total_bill", "tip", data = tips)
Out[27]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3ded3b5cc0>
In [28]:
seaborn.regplot("total_bill", "tip", data = tips, scatter = False)
Out[28]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3ded3154e0>

Il est possible d'obtenir directement tous les nuages de points 2 à 2, avec la fonction pairplot(). Le paramètre vars permet de sélectionner certaines variables. Par défaut, la fonction utilise toutes les variables numériques.

In [29]:
seaborn.pairplot(data = tips, vars = ["total_bill", "tip", "size"])
Out[29]:
<seaborn.axisgrid.PairGrid at 0x7f3ded64af60>

Var qualitative - Var qualitative

Pour obtenir le diagramme en barres séparées (en effectifs), nous utilisons la fonction catplot() avec le paramètre hue (celui-ci provient de la fonction countplot()).

In [30]:
seaborn.catplot(x = "sex", hue = "smoker", data = tips, kind = "count")
Out[30]:
<seaborn.axisgrid.FacetGrid at 0x7f3ded37eba8>

Cette fonction catplot() permet aussi un découpage en facette avec les paramètres row et col. Ici, nous découpons donc le graphique en fonction des modalités de smoker.

In [31]:
seaborn.catplot(x = "sex", col = "smoker", data = tips, kind = "count")
Out[31]:
<seaborn.axisgrid.FacetGrid at 0x7f3decf177b8>

Pour avoir la version en pourcentages, il faut faire les calculs avant, modifier la structure du résultat et les afficher ensuite.

In [32]:
t = pandas.crosstab(tips.sex, tips.smoker, normalize = "columns")
t = t.assign(sex = t.index)
tm = pandas.melt(t, id_vars = "sex")
tm = tm.assign(value = 100 * tm.value)

seaborn.catplot("sex", y = "value", col = "smoker", data = tm, kind = "bar")
Out[32]:
<seaborn.axisgrid.FacetGrid at 0x7f3decd86240>

Une autre représentation est de visualiser la table de contingence avec une heatmap (la couleur dépendra du nombre d'individus pour chaque couple de modalité).

In [33]:
seaborn.heatmap(pandas.crosstab(tips.sex, tips.smoker))
Out[33]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3decccdd68>

Var quantitative - Var qualitative

Pour réaliser les histogrammes de la variable quantitative pour chaque modalité de la variable qualitative, il faut passer par la fonction FacetGrid(), permettant de faire un découpage en plusieurs lignes (avec row) et/ou en plusieurs colonnes (avec col). On applique ensuite la fonction distplot() avec les paramètres (ici la variable total_bill).

In [34]:
p = seaborn.FacetGrid(tips, row = "sex")
p.map(seaborn.distplot, "total_bill")
Out[34]:
<seaborn.axisgrid.FacetGrid at 0x7f3decef21d0>

Les boîtes à moustaches sont elles faciles à créer avec catplot() (ou boxplot() directement).

In [35]:
seaborn.catplot(x = "sex", y = "total_bill", data = tips, kind = "box")
Out[35]:
<seaborn.axisgrid.FacetGrid at 0x7f3decb62128>

On peut aussi représenter la moyenne et l'écart-type à l'aide du graphique pointplot.

In [36]:
seaborn.catplot(x = "sex", y = "total_bill", data = tips, kind = "point", join = False)
Out[36]:
<seaborn.axisgrid.FacetGrid at 0x7f3decb0d208>

Le graphique violin est bien sûr lui aussi applicable dans ce cas.

In [37]:
seaborn.catplot(x = "sex", y = "total_bill", data = tips, kind = "violin")
Out[37]:
<seaborn.axisgrid.FacetGrid at 0x7f3decb00fd0>

On a aussi la possibilité de représenter le pseudo nuage de points, avec strip.

In [38]:
seaborn.catplot(x = "sex", y = "total_bill", data = tips, kind = "strip")
Out[38]:
<seaborn.axisgrid.FacetGrid at 0x7f3deca70c50>

Multivariables

3 quantitatives

Dans ce cas, nous cherchons à faire une heatmap du croisement entre les versions discrétisées en intervalles de deux variables quantitatives, la couleur d'un rectangle dépendant de la moyenne (ici mean du module numpy) d'une troisième variable. On créé la table puis l'affiche.

In [39]:
t = pandas.crosstab(pandas.cut(tips.total_bill, bins = 6),
                    tips["size"],
                    values = tips.tip, aggfunc = numpy.mean)
seaborn.heatmap(t)
Out[39]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3dec9803c8>

2 quantitatives et 1 qualitative

Dans ce cas, il est possible de faire le nuage de points avec la fonction lmplot(), en utilisant le principe de grille (comme catplot()) avec un découpage en colonnes (via col). On peut aussi améliorer la distinction en ajoutant une couleur différente sur la variable sex avec le paramètre hue.

In [40]:
seaborn.lmplot("total_bill", "tip", hue = "sex", col = "sex", data = tips)
Out[40]:
<seaborn.axisgrid.FacetGrid at 0x7f3deca8f9b0>

1 quantitative et 2 qualitatives

Il est possible de créer les histogrammes de la variable quantitative pour chaque couple de modalités sur les deux variables qualitatives avec FacetGrid().

In [41]:
p = seaborn.FacetGrid(tips, row = "sex", col = "smoker")
p.map(seaborn.distplot, "total_bill")
Out[41]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec87f438>

Pour les boîtes à moustaches, on utilise la fonction catplot() qui permet d'ajouter une couleur en fonction d'une variable qualitative via le paramètre hue.

In [42]:
seaborn.catplot(x = "sex", y = "total_bill", hue = "smoker", data = tips, kind = "box")
Out[42]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec9da940>

Cette même fonction catplot() permet le découpage en lignes (row) ou en colonnes (col), qui nous est utile pour le graphique point.

In [43]:
seaborn.catplot(x = "sex", y = "total_bill", hue = "sex", col = "smoker", data = tips, 
                   kind = "point", join = False)
Out[43]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec5d7550>

Le graphique violin s fait lui très facilement.

In [44]:
seaborn.catplot(x = "sex", y = "total_bill", hue = "smoker", data = tips, kind = "violin")
Out[44]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec70a2e8>

Et pour le graphique strip, on reprend le découpage via col.

In [45]:
seaborn.catplot(x = "sex", y = "total_bill", hue = "smoker", col = "smoker", data = tips, 
                   kind = "strip", jitter = True)
Out[45]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec51e0f0>

3 qualitatives

In [46]:
seaborn.catplot(x = "sex", row = "smoker", col = "time", data = tips, kind = "count")
Out[46]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec4a7518>
In [47]:
seaborn.catplot(x = "sex", hue = "smoker", col = "time", data = tips, kind = "count")
Out[47]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec4dbcc0>

Pour avoir la version sommée à 100%, il faut calculer la table en premier, la restructurer et l'afficher.

In [48]:
t = pandas.crosstab([tips.smoker, tips.time], tips.sex, normalize = "index")
t = t.reset_index().assign(smoker_time = lambda x: x.smoker + "_" + x.time).drop(columns = ["smoker", "time"])
tm = pandas.melt(t, id_vars = "smoker_time")
tm = tm.assign(value = 100 * tm.value)
tm
Out[48]:
smoker_time sex value
0 No_Dinner Female 27.358491
1 No_Lunch Female 55.555556
2 Yes_Dinner Female 32.857143
3 Yes_Lunch Female 43.478261
4 No_Dinner Male 72.641509
5 No_Lunch Male 44.444444
6 Yes_Dinner Male 67.142857
7 Yes_Lunch Male 56.521739
In [49]:
seaborn.catplot(x = "smoker_time", y = "value", hue = "sex", data = tm, kind = "bar")
Out[49]:
<seaborn.axisgrid.FacetGrid at 0x7f3dec04d550>

Compléments

Il est bien évidemment possible de personnaliser le graphique de différentes façons, dont certains sont présentées ci-dessous. On accède aux fonctions de personnalisation soit via des paramètres de la fonction, soit via l'objet renvoyé par la fonction utilisée pour créer le graphique, soit via le module directement. Dans ce cas, ce sont des changements qui affecteront aussi les graphiques futurs.

  • suptitle dans fig : titre global
  • set_axis_labels : titre des axes
  • palette : choix d'une palette de couleurs
  • size et aspect : hauteur et ratio entre hauteur et largeur, pour chaque facette (une seule ici)
In [50]:
seaborn.set_style("white")
p = seaborn.catplot(x = "size", y = "tip", hue = "sex", data = tips, kind = "box", palette = "Set2", height = 4, aspect = 2)
p.fig.suptitle("Taille et pourboire en fonction du sexe")
p.set_axis_labels("Nombre de convives", "Pourboire")
Out[50]:
<seaborn.axisgrid.FacetGrid at 0x7f3debd24b00>

A faire

A partir du jeu de données Computers, vous devez répondre aux questions suivantes, avec seaborn.

In [51]:
ordis = pandas.read_csv("Computers.csv")
ordis.head()
Out[51]:
price speed hd ram screen cd multi premium ads trend
0 1499 25 80 4 14 no no yes 94 1
1 1795 33 85 2 14 no no yes 94 1
2 1595 25 170 4 15 no no yes 94 1
3 1849 25 170 8 14 no no no 94 1
4 3295 33 340 16 14 no no yes 94 1
  1. Représenter graphiquement la variable price (histogramme, boîte à moustaches, ...)
  2. Représenter le lien entre la variable price et les variables
    • speed
    • hd
    • ram
    • cd
    • premium
    • screen
  3. Représenter sur price l'impact de ces couples de variables
    • speed et hd
    • hd et screen
    • speed et premium
    • hd et premium
  4. Proposer des représentations graphiques, toujours pour décrire price en fonction d'autres variables, mais prenant en compte plus de trois variables

anscombe

Représenter sur un même graphique (avec un découpage donc) les quatre séries des données anscombe, avec seaborn. L'idée est dobtenir un graphique de ce type.

In [52]:
anscombe = pandas.read_csv("anscombe.csv")
anscombe
Out[52]:
x1 x2 x3 x4 y1 y2 y3 y4
0 10 10 10 8 8.04 9.14 7.46 6.58
1 8 8 8 8 6.95 8.14 6.77 5.76
2 13 13 13 8 7.58 8.74 12.74 7.71
3 9 9 9 8 8.81 8.77 7.11 8.84
4 11 11 11 8 8.33 9.26 7.81 8.47
5 14 14 14 8 9.96 8.10 8.84 7.04
6 6 6 6 8 7.24 6.13 6.08 5.25
7 4 4 4 19 4.26 3.10 5.39 12.50
8 12 12 12 8 10.84 9.13 8.15 5.56
9 7 7 7 8 4.82 7.26 6.42 7.91
10 5 5 5 8 5.68 4.74 5.73 6.89
In [ ]: