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