diff --git a/examples/examples.ipynb b/examples/examples.ipynb index faa8664d1..71ef47e65 100644 --- a/examples/examples.ipynb +++ b/examples/examples.ipynb @@ -6,7 +6,7 @@ "source": [ "# Yellowbrick Examples \n", "\n", - "Ths notebook is a sample of the examples that yellowbrick provids." + "Ths notebook is a sample of the examples that yellowbrick provides." ] }, { diff --git a/examples/figures/pipeline_prototype.png b/examples/figures/pipeline_prototype.png new file mode 100644 index 000000000..bffa05fc3 Binary files /dev/null and b/examples/figures/pipeline_prototype.png differ diff --git a/examples/pipeline.ipynb b/examples/pipeline.ipynb new file mode 100644 index 000000000..a2c8e5b35 --- /dev/null +++ b/examples/pipeline.ipynb @@ -0,0 +1,179 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visual Pipelines \n", + "\n", + "This notebook demonstrates a proof of concept for a visual pipeline for analytics. \n", + "\n", + "![Yellowbrick Prototype Pipeline Objects](figures/pipeline_prototype.png)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "\n", + "import os\n", + "import sys \n", + "\n", + "# Modify the path \n", + "sys.path.append(\"..\")\n", + "\n", + "import pandas as pd\n", + "import yellowbrick as yb \n", + "import matplotlib as mpl \n", + "import matplotlib.pyplot as plt " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Datasets \n", + "\n", + "Note that if datasets do not exist, please see the `download.py` located in this directory. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "FIXTURES = os.path.join(os.getcwd(), \"data\")\n", + "credit = pd.read_excel(os.path.join(FIXTURES, \"credit.xls\"), header=1)\n", + "concrete = pd.read_excel(os.path.join(FIXTURES, \"concrete.xls\"))\n", + "occupancy = pd.read_csv(os.path.join('data','occupancy','datatraining.txt'))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Rename the columns of the datasets for ease of use. \n", + "credit.columns = [\n", + " 'id', 'limit', 'sex', 'edu', 'married', 'age', 'apr_delay', 'may_delay',\n", + " 'jun_delay', 'jul_delay', 'aug_delay', 'sep_delay', 'apr_bill', 'may_bill',\n", + " 'jun_bill', 'jul_bill', 'aug_bill', 'sep_bill', 'apr_pay', 'may_pay', 'jun_pay',\n", + " 'jul_pay', 'aug_pay', 'sep_pay', 'default'\n", + "]\n", + "\n", + "concrete.columns = [\n", + " 'cement', 'slag', 'ash', 'water', 'splast',\n", + " 'coarse', 'fine', 'age', 'strength'\n", + "]\n", + "\n", + "occupancy.columns = [\n", + " 'date', 'temp', 'humid', 'light', 'co2', 'hratio', 'occupied'\n", + "]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('scale', StandardScaler(copy=True, with_mean=True, with_std=True)),\n", + " ('model', LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,\n", + " intercept_scaling=1, loss='squared_hinge', max_iter=1000,\n", + " multi_class='ovr', penalty='l2', random_state=None, tol=0.0001,\n", + " verbose=0))]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.svm import LinearSVC\n", + "from sklearn.preprocessing import StandardScaler \n", + "\n", + "model = Pipeline([\n", + " ('scale', StandardScaler()), \n", + " ('model', LinearSVC())\n", + "])\n", + "\n", + "model.steps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Evaluation Visualization Prototype" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from sklearn.pipeline import Pipeline\n", + "from sklearn.base import BaseEstimator, TransformerMixin\n", + "\n", + "\n", + "class VisualPipeline(Pipeline):\n", + " \n", + " def draw(self):\n", + " \"\"\"\n", + " Calls the draw method on every visual transformer/estimator \n", + " \"\"\"\n", + " for name, estimator in self.steps:\n", + " try:\n", + " estimator.draw()\n", + " except AttributeError:\n", + " continue \n", + "\n", + " \n", + "class ClassifierEvaluation(object):\n", + " \n", + " def draw(self):\n", + " yb.crplot()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.11" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/rank2d.ipynb b/examples/rank2d.ipynb new file mode 100644 index 000000000..3d12f322c --- /dev/null +++ b/examples/rank2d.ipynb @@ -0,0 +1,271 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "%matplotlib inline " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sliders Example \n", + "\n", + "This is an example of interactive iPython workbook that uses widgets to meaningfully interact with visualization. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Imports \n", + "import numpy as np\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from collections import OrderedDict\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import Imputer \n", + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.metrics import mean_squared_error as mse\n", + "from ipywidgets import interact, interactive, fixed\n", + "\n", + "import ipywidgets as widgets" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Data Loading \n", + "columns = OrderedDict([\n", + " (\"DAY\", \"the day of data collection\"),\n", + " (\"Q-E\", \"input flow to plant\"),\n", + " (\"ZN-E\", \"input Zinc to plant\"),\n", + " (\"PH-E\", \"input pH to plant\"),\n", + " (\"DBO-E\", \"input Biological demand of oxygen to plant\"),\n", + " (\"DQO-E\", \"input chemical demand of oxygen to plant\"),\n", + " (\"SS-E\", \"input suspended solids to plant\"),\n", + " (\"SSV-E\", \"input volatile supended solids to plant\"),\n", + " (\"SED-E\", \"input sediments to plant\"),\n", + " (\"COND-E\", \"input conductivity to plant\"),\n", + " (\"PH-P\", \"input pH to primary settler\"),\n", + " (\"DBO-P\", \"input Biological demand of oxygen to primary settler\"),\n", + " (\"SS-P\", \"input suspended solids to primary settler\"),\n", + " (\"SSV-P\", \"input volatile supended solids to primary settler\"),\n", + " (\"SED-P\", \"input sediments to primary settler\"),\n", + " (\"COND-P\", \"input conductivity to primary settler\"),\n", + " (\"PH-D\", \"input pH to secondary settler\"),\n", + " (\"DBO-D\", \"input Biological demand of oxygen to secondary settler\"),\n", + " (\"DQO-D\", \"input chemical demand of oxygen to secondary settler\"),\n", + " (\"SS-D\", \"input suspended solids to secondary settler\"),\n", + " (\"SSV-D\", \"input volatile supended solids to secondary settler\"),\n", + " (\"SED-D\", \"input sediments to secondary settler\"),\n", + " (\"COND-S\", \"input conductivity to secondary settler\"),\n", + " (\"PH-S\", \"output pH\"),\n", + " (\"DBO-S\", \"output Biological demand of oxygen\"),\n", + " (\"DQO-S\", \"output chemical demand of oxygen\"),\n", + " (\"SS-S\", \"output suspended solids\"),\n", + " (\"SSV-S\", \"output volatile supended solids\"),\n", + " (\"SED-S\", \"output sediments\"),\n", + " (\"COND-\", \"output conductivity\"),\n", + " (\"RD-DB-P\", \"performance input Biological demand of oxygen in primary settler\"),\n", + " (\"RD-SSP\", \"performance input suspended solids to primary settler\"),\n", + " (\"RD-SE-P\", \"performance input sediments to primary settler\"),\n", + " (\"RD-DB-S\", \"performance input Biological demand of oxygen to secondary settler\"),\n", + " (\"RD-DQ-S\", \"performance input chemical demand of oxygen to secondary settler\"),\n", + " (\"RD-DB-G\", \"global performance input Biological demand of oxygen\"),\n", + " (\"RD-DQ-G\", \"global performance input chemical demand of oxygen\"),\n", + " (\"RD-SSG\", \"global performance input suspended solids\"),\n", + " (\"RD-SED-G\", \"global performance input sediments\"),\n", + "])\n", + "\n", + "data = pd.read_csv(\"data/water-treatment.data\", names=columns.keys())\n", + "data = data.replace('?', np.nan)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false, + "scrolled": false + }, + "outputs": [], + "source": [ + "# Capture only the numeric columns in the data set. \n", + "numeric_columns = columns.keys()\n", + "numeric_columns.remove(\"DAY\")\n", + "data = data[numeric_columns].apply(pd.to_numeric)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2D Rank Features " + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "collapsed": false, + "scrolled": false + }, + "outputs": [], + "source": [ + "def apply_column_pairs(func):\n", + " \"\"\"\n", + " Applies a function to a pair of columns and returns a new \n", + " dataframe that contains the result of the function as a matrix\n", + " of each pair of columns. \n", + " \"\"\"\n", + " \n", + " def inner(df):\n", + " cols = pd.DataFrame([\n", + " [\n", + " func(df[acol], df[bcol]) for bcol in df.columns\n", + " ] for acol in df.columns\n", + " ])\n", + "\n", + " cols.columns = df.columns\n", + " cols.index = df.columns \n", + " return cols\n", + "\n", + " return inner \n", + "\n", + "\n", + "@apply_column_pairs\n", + "def least_square_error(cola, colb):\n", + " \"\"\"\n", + " Computes the Root Mean Squared Error of a linear regression \n", + " between two columns of data. \n", + " \"\"\"\n", + " x = cola.fillna(np.nanmean(cola))\n", + " y = colb.fillna(np.nanmean(colb))\n", + " \n", + " m, b = np.polyfit(x, y, 1)\n", + " yh = (x * m) + b \n", + " return ((y-yh) ** 2).mean()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\ncjqdWr16tbZv366tW7fqiSee0KBBgzR48OASl58vNTVVHTt2VNWqVXXllVfq9ttv1549e1S5cmU1\na9ZMLVu2lCTdfvvtmjx5sg4ePKjIyMhL/i4k6cyZM/r73/+ua6+9VpKUnZ0th8Oh48ePKzY2VrGx\nsWrbtq169OihjRs3lvg554qhwsLCYnPjzj8PLpdLUVFReumll9zLjh07poYNG+r666/Xxx9/rLS0\nNG3dulWvvPKKkpOT1bhx4xL3CSBw8G07VFgXjjCcExcXp7179+quu+7ShAkTlJ2drZ9//lnR0dFa\ntmyZCgoK5HK59Pzzz+tvf/tbmfffsWNHvfPOO8rJyZEkvfzyyxozZowkKTQ0VIWFhaV+Rvv27bVq\n1SpJZwuFwYMHKz09vdg2aWlp6tevn/r376+rr75a69evdxdEnsRXrVpVrVq10rJly9zbvffee+rQ\noYM2bNigwYMH6+abb9ajjz6qu+66S998802Jyy/09ttvF/vcTz75RFFRUYqMjNTBgwf1/fffS5J2\n7typ/Px8XX311aa/j+joaL355psyDEMFBQV65JFHtGzZMu3atUu1atXSX/7yF3Xo0EHr16+XdPYa\nCAsLK1Yg1q5d2/3Nvo8//rjEfd16661KS0vTd999J0navHmz+vbtq/z8fI0aNUpr1qxRz5499fzz\nz8vpdCojI8M0dwCBg5EnVFi//vqr+xtahmHI4XBo+fLlGj16tF544QX9/e9/l8Ph0KOPPqrf/OY3\nevjhhzV16lT17dtXhmHohhtucN/yKouYmBj9+OOPio2NVUhIiBo2bKikpCRJUlRUlB577DGFh4eb\njrSMGzdOCQkJ6tOnjwzD0EMPPeQerTln6NChev755/Xee++pZs2auv32292jLiXFX/hNxOnTpysx\nMVErV65UUVGR+vTpo759+8rlcmnz5s3q1auXqlSpopo1a2rixIlq0KDBJZdf6MUXX9S4ceOUkpIi\nh8Ohe+65R926dZN09pt4jz/+uBwOhypXrqzZs2dfNDlfKv6tybFjx2ry5Mnq06ePioqK1KFDB/35\nz39WYWGhVq5cqR49eqhOnTrq1q2b6tSpo0OHDqlJkya64YYb1LNnT7399tsaO3asEhMTVb16dXXo\n0EF169a95O/+uuuu04QJEzRy5EhJZwveuXPnqlKlSnr44Yf13HPP6R//+IdCQkL0xz/+Ub///e9L\n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\nSUVORK5CYII=\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "labeled_metrics = {\n", + " 'Pearson': 'pearson', \n", + " 'Kendall Tao': 'kendall', \n", + " 'Spearman': 'spearman', \n", + " 'Pairwise Covariance': 'covariance',\n", + " 'Least Squares Error': 'lse', \n", + "}\n", + "\n", + "@interact(metric=labeled_metrics, data=fixed(data))\n", + "def rank2d(data, metric='pearson'):\n", + " \"\"\"\n", + " Creates a visualization of pairwise ranking by column in the data. \n", + " \"\"\"\n", + " \n", + " # The different rank by 2d metrics. \n", + " metrics = {\n", + " \"pearson\": lambda df: df.corr('pearson'), \n", + " \"kendall\": lambda df: df.corr('kendall'), \n", + " \"spearman\": lambda df: df.corr('spearman'), \n", + " \"covariance\": lambda df: df.cov(), \n", + " \"lse\": least_square_error,\n", + " }\n", + " \n", + " # Quick check to make sure a valid metric is passed in. \n", + " if metric not in metrics:\n", + " raise ValueError(\n", + " \"'{}' not a valid metric, specify one of {}\".format(\n", + " metric, \", \".join(metrics.keys())\n", + " )\n", + " )\n", + " \n", + " \n", + " # Compute the correlation matrix\n", + " corr = metrics[metric](data)\n", + "\n", + " # Generate a mask for the upper triangle\n", + " mask = np.zeros_like(corr, dtype=np.bool)\n", + " mask[np.triu_indices_from(mask)] = True\n", + "\n", + " # Set up the matplotlib figure\n", + " f, ax = plt.subplots(figsize=(11, 9))\n", + " ax.set_title(\"{} metric across {} features\".format(metric.title(), len(data.columns)))\n", + " \n", + " # Draw the heatmap with the mask and correct aspect ratio\n", + " sns.heatmap(corr, mask=mask, vmax=.3,\n", + " square=True, xticklabels=5, yticklabels=5,\n", + " linewidths=.5, cbar_kws={\"shrink\": .5}, ax=ax)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.11" + }, + "widgets": { + "state": { + "2b2e97ac5fb04360a14b241b102d297c": { + "views": [ + { + "cell_index": 7 + } + ] + } + }, + "version": "1.2.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}