td2a_cenonce_session_4A

Transcription

td2a_cenonce_session_4A
td2a_cenonce_session_4A
September 28, 2016
1
Machine Learning et Marketting
In [ ]: %matplotlib inline
Populating the interactive namespace from numpy and matplotlib
In [ ]: import matplotlib.pyplot as plt
plt.style.use('ggplot')
In [ ]: from jyquickhelper import add_notebook_menu
add_notebook_menu()
Out[ ]: <IPython.core.display.HTML object>
1.0.1
Données
Le jeu de données Bank Marketing Data Set contient des données destinées à évaluer le fait qu’une
personne souscrive un contrat. La base de données contient 45.000 observations avec 17 attributs
et une variable binaire qui représente le résultat à prédire. Tout d’abord, on récupère la base de
données.
In [ ]: url = "https://archive.ics.uci.edu/ml/machine-learning-databases/00222/"
file = "bank.zip"
import pyensae
data = pyensae.download_data(file, website=url)
downloading of https://archive.ics.uci.edu/ml/machine-learning-databases/00222
unzipped bank-full.csv to .\bank-full.csv
In [ ]: with open("bank.csv","r") as fo :
n = 0
for row in fo :
print(row.strip("\r\n "))
n += 1
if n > 5 : break
1
"age";"job";"marital";"education";"default";"balance";"housing";"loan";"contact";"d
30;"unemployed";"married";"primary";"no";1787;"no";"no";"cellular";19;"oct";79;1;-1
33;"services";"married";"secondary";"no";4789;"yes";"yes";"cellular";11;"may";220;1
35;"management";"single";"tertiary";"no";1350;"yes";"no";"cellular";16;"apr";185;1;
30;"management";"married";"tertiary";"no";1476;"yes";"yes";"unknown";3;"jun";199;4;
59;"blue-collar";"married";"secondary";"no";0;"yes";"no";"unknown";5;"may";226;1;-1
In [ ]: import pandas
df = pandas.read_csv("bank.csv",sep=";")
df.tail()
Out[ ]:
1.0.2
4516
4517
4518
4519
4520
age
33
57
57
28
44
job
services
self-employed
technician
blue-collar
entrepreneur
4516
4517
4518
4519
4520
contact
cellular
unknown
cellular
cellular
cellular
day month
30
jul
9
may
19
aug
6
feb
3
apr
marital
married
married
married
married
single
education default
secondary
no
tertiary
yes
secondary
no
secondary
no
tertiary
no
duration
329
153
151
129
345
campaign
5
1
11
4
2
pdays
-1
-1
-1
211
249
balance housing loan
-333
yes
no
-3313
yes yes
295
no
no
1137
no
no
1136
yes yes
previous poutcome
0 unknown
0 unknown
0 unknown
3
other
7
other
Exercice 1 : prédire y en fonction des attributs
On utilisera pour cela un GradientBoostingClassifier après avoir sciender la base en base
d’apprentissage et tests. Quelques liens :
•
•
•
•
train_test_split
DictVectorizer
Using DictVectorizer with sklearn DecisionTreeClassifier
iterrows
In [ ]:
1.0.3
Exercice 2 : tracer la courbe ROC
La plupart des classifieur produisent deux chiffres en sorties : la classe et un score. Le score est
souvent une probabilité et il traduit la qualité du classifieur. On s’en sert alors pour accepter ou rejeter la prédiction Sensitivity and specificity. La dénomination vrai positif, faux positif,
. . . est assez trompeuse. Il vaut mieux retenir les définitions de précision, rappel.
Comme le classifieur retourne un score de confiance, on décide de valider ou de rejeter sa réponse
si le score est supérieur ou inférieur à ce seuil :
• si score >= seuil, on valide la réponse, qui est soit bonne (TP : True Positive), soit fausse (FP
: False Positive)
2
y
no
no
no
no
no
• si score < seuil, on rejete la réponse qui est soit bonne (FN : False Negative) soit fausse (True
Negative)
La présicion est définie comme étant le nombre de réponses justes sur le nombre de réponses
validées :
TP
TP + FP
Le rappel est défini comme étant le nombre de réponses justes sur le nombre total de réponses
justes :
precision =
TP
TP + FN
Dans notre cas, on définit une réponse juste comme étant le fait qu’on prédit la bonne classe.
Quelques liens :
rappel =
• Receiver Operating Characteristic (ROC)
Et il faudra tracer sur le même dessin la courbe ROC de :
• l’ensemble de la base de test
• deux échantillons aléatoires de la base de test
Les courbes des deux échantillons aléatoires devraient illustrer la stabilité de la courbe ROC. Il est
même possible de calculer un intervalle de confiance en utilisant un bootstrap.
Quelques liens sur matplotlib :
• Our Favorite Recipes
• How to make several plots on a single page using matplotlib?
In [ ]:
1.0.4
Exercice 3 : d3js
Préliminaires Les graphiques interactifs sont écrits en javascript tout comme les notebooks
IPython. Il est difficile d’avoir les deux ensembles. Par défaut, les graphiques ne sont pas interactifs en Python. La conversion automatique des notebooks n’est pas parfaite. Il faudra attendre
encore un peu de temps avant que ces outils ne soient aisément utilisables.
javascript
La librairie d3.js est la librairie la plus connue pour faire des graphiques interactifs. Elle est écrite
en javascript. Néanmoins, sans trop connaître le langage, il est possible de s’en servir pour faire
un graphique dans un notebook. Le plus souvent, on récupère le code javascript d’un graphique,
on change les données et on l’affiche. On va s’inspirer des liens suivants :
• Solution 1 : mpld3, seaborn, . . .
– D3 Plugins: Truly Interactive Matplotlib In Your Browser
– 21 Interactive Plots from matplotlib, ggplot for Python, prettyplotlib, Stack Overflow,
and seaborn (le notebook utilise plot.ly qui n’est pas toutà-fait gratuit)
3
• Solution 2 : bokeh
– Bokeh in IPython Notebook
• Solution 3 : d3.js
– Use of d3.js in IPython notebook
– Basic charts
– Scatter plot
Solution 1 Cette solution est assez simple. Le module mpld3 utilise le graphique créée par
matplotlib et le convertit un graphe interactif qu’on peut sauvegarder au format HTML pour
l’insérer plus tard dans son site web. Le graphique n’est pas interactif dans le notebook.
In [ ]: import matplotlib.pyplot as plt
from mpld3 import plugins, save_html
import numpy as np
fig, ax = plt.subplots(subplot_kw=dict(axisbg='#EEEEEE'))
ax.grid(color='white', linestyle='solid')
N = 50
scatter = ax.scatter(np.random.normal(size=N),
np.random.normal(size=N),
c=np.random.random(size=N),
s = 1000 * np.random.random(size=N),
alpha=0.3,
cmap=plt.cm.jet)
ax.set_title("D3 Scatter Plot (with tooltips!)", size=20)
labels = ['point {0}'.format(i + 1) for i in range(N)]
fig.plugins = [plugins.PointLabelTooltip(scatter, labels)]
# convertit le graphique au format HTML --> peut être inséré dans un site w
save_html(fig, "graph.html")
4
La méthode save_html enregistre le graphe au format HTML. Développer localement un script en
javascript n’est pas toujours évident pour quelqu’un qui débute. On n’abordera pas ce point ici.
Solution 2 : bokeh On reprend l’exemple iris.
In [ ]: from bokeh.plotting import *
output_notebook()
Le projet évolue assez régulièrement. Voici la version dont est tiré l’exemple.
In [ ]: import bokeh
bokeh.__version__
Out[ ]: '0.10.0'
Puis le code du graphe :
In [ ]: from bokeh.sampledata.iris import flowers
from bokeh.plotting import *
colormap = {'setosa': 'red', 'versicolor': 'green', 'virginica': 'blue'}
flowers['color'] = flowers['species'].map(lambda x: colormap[x])
output_file("iris.html", title="iris.py example")
5
p = figure(title = "Iris Morphology")
p.xaxis.axis_label = 'Petal Length'
p.yaxis.axis_label = 'Petal Width'
p.circle(flowers["petal_length"], flowers["petal_width"],
color=flowers["color"], fill_alpha=0.2, size=10)
show(p)
Solution 3 : d3.js Les modules précédents proposent des graphiques types. Si on veut animer
de façon différentes les graphiques ou proposer des scénarios complexes, il faut revenir au framework d3.js.
Il y a trois parties : * les données * la feuille de style * le code javascript
In [ ]: style = """
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
.tooltip {
position: relative;
width: 200px;
height: 28px;
//pointer-events: none;
}
"""
id_graph = "mongraph"
code_graph = """
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xValue = function(d) { return d.Calories;}, // data -> value
xScale = d3.scale.linear().range([0, width]), // value -> display
xMap = function(d) { return xScale(xValue(d));}, // data -> display
xAxis = d3.svg.axis().scale(xScale).orient("bottom");
// setup y
var yValue = function(d) { return d["Protein (g)"];}, // data -> value
6
yScale = d3.scale.linear().range([height, 0]), // value -> display
yMap = function(d) { return yScale(yValue(d));}, // data -> display
yAxis = d3.svg.axis().scale(yScale).orient("left");
// setup fill color
var cValue = function(d) { return d.Manufacturer;},
color = d3.scale.category10();
var svg = d3.select("#__IDGRAPH__").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
var tooltip = d3.select("#__IDGRAPH__").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
d3.csv("__FILE__", function(error, data) {
data.forEach(function(d) {
d.Calories = +d.Calories;
d["Protein (g)"] = +d["Protein (g)"];
//console.log(d);
});
xScale.domain([d3.min(data, xValue)-1, d3.max(data, xValue)+1]);
yScale.domain([d3.min(data, yValue)-1, d3.max(data, yValue)+1]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Calories");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
7
.style("text-anchor", "end")
.text("Protein (g)");
// draw dots
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 7)
// taille des ronds
.attr("cx", xMap)
.attr("cy", yMap)
.style("fill", function(d) { return color(cValue(d));})
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d["Cereal Name"] + "<br/> (" + xValue(d)
+ ", " + yValue(d) + ")")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
})
;
// draw legend
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 +
// draw legend colored rectangles
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
// draw legend text
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
8
.text(function(d) { return d;})
});
"""
La plupart des exemples venant de la gallerie d3.js fonctionnent à condition de bien faire attention
à ces petits détails qu’il faut changer. Je vous suggère de regarder dans le code précédent à quoi
correspondent ces remplacements.
In [ ]: html_src = """
<style>
{0}
</style>
<div id="{1}"></div>
""".format(style, id_graph)
js_src = code_graph.replace("__IDGRAPH__", id_graph) \
.replace("__FILE__", "/notebooks/td2a/cereales.csv")
# ~ http://localhost:8888/notebooks/td2a/cereales.csv
js_libs = ['http://d3js.org/d3.v3.js']
import IPython
from IPython.core.display import display, Javascript, HTML
display( Javascript(data=js_src, lib= js_libs))
display(HTML(html_src))
<IPython.core.display.Javascript object>
<IPython.core.display.HTML object>
Comme la conversion au format HTML ne fonctionne pas, voici ce que cela donnerait :
In [ ]: from pyquickhelper.helpgen import NbImage
NbImage("d3jsex.png")
Out[None]:
9
In [ ]:
10