{ "metadata": { "name": "", "signature": "sha256:4e8569619b97a2181c08d098e12cf1c53e4a3c3c878d1c09999ced6e1275267c" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Graphiques Plotly" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "unis aux donn\u00e9es ouvertes de la ville de Montr\u00e9al" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bonjour, je m'appelle \u00c9tienne. \n", "\n", "\n", "\n", "https://twitter.com/MTL_DO\n", "\n", "\n", "\n", "\n", "\n", "
" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import plotly\n", "plotly.__version__" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "'1.0.29'" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "import plotly.plotly as py #\n", "import plotly.tools as tls # " ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you are not familiar with credentials files, refer to the User Guide's homepage." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "sdfds" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from plotly.graph_objs import Data, Layout, Figure\n", "from plotly.graph_objs import Heatmap, Contour\n", "from plotly.graph_objs import XAxis, YAxis, ColorBar, Margin\n", "from plotly.graph_objs import Font, Contours" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 68 }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy as np #\n", "import pandas as pd #\n", "import urllib2 #" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "import datetime #\n", "import locale\n", "locale.setlocale(locale.LC_ALL, 'fr_CA.utf8')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 30, "text": [ "'fr_CA.utf8'" ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "
" ] }, { "cell_type": "code", "collapsed": false, "input": [ "#tls.embed() " ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 36 }, { "cell_type": "code", "collapsed": false, "input": [ "#tls.embed()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 37 }, { "cell_type": "code", "collapsed": false, "input": [ "#tls.embed()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 38 }, { "cell_type": "code", "collapsed": false, "input": [ "#tls.embed()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 39 }, { "cell_type": "code", "collapsed": false, "input": [ "#\n", "url_csv = 'http://donnees.ville.montreal.qc.ca/storage/f/\\\n", "2014-01-20T20%3A48%3A50.296Z/2013.csv'\n", "\n", "fichier_csv = urllib2.urlopen(url_csv) #\n", "df = pd.read_csv(fichier_csv) #" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 40 }, { "cell_type": "code", "collapsed": false, "input": [ "df.head() # affiche les 5 premi\u00e8res lignes du tableau" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
DateUnnamed: 1Berri1CSCMais1Mais2ParcPierDupRachel1Totem_Laurier
0 01/01/2013 00:00 0 0 1 0 6 0 1 0
1 02/01/2013 00:00 69 0 13 0 18 0 2 0
2 03/01/2013 00:00 69 2 21 6 22 1 0 0
3 04/01/2013 00:00 80 0 22 4 16 0 125 0
4 05/01/2013 00:00 52 4 19 3 12 0 97 0
\n", "

5 rows \u00d7 10 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 41, "text": [ " Date Unnamed: 1 Berri1 CSC Mais1 Mais2 Parc PierDup Rachel1 \\\n", "0 01/01/2013 00:00 0 0 1 0 6 0 1 \n", "1 02/01/2013 00:00 69 0 13 0 18 0 2 \n", "2 03/01/2013 00:00 69 2 21 6 22 1 0 \n", "3 04/01/2013 00:00 80 0 22 4 16 0 125 \n", "4 05/01/2013 00:00 52 4 19 3 12 0 97 \n", "\n", " Totem_Laurier \n", "0 0 \n", "1 0 \n", "2 0 \n", "3 0 \n", "4 0 \n", "\n", "[5 rows x 10 columns]" ] } ], "prompt_number": 41 }, { "cell_type": "code", "collapsed": false, "input": [ "df.tail() # affiche les 5 premi\u00e8res lignes du tableau" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
DateUnnamed: 1Berri1CSCMais1Mais2ParcPierDupRachel1Totem_Laurier
256 14/09/2013 00:00 2488 1494 1706 2911 979 1075 3277 2456
257 15/09/2013 00:00 2243 1749 1417 2933 1804 1459 3623 2527
258 16/09/2013 00:00 4206 2466 2799 5485 3272 855 4307 3012
259 17/09/2013 00:00 5506 3153 3563 6626 3882 1485 5332 3745
260 18/09/2013 00:00 1564 3330 1154 1803 4074 1759 1511 3921
\n", "

5 rows \u00d7 10 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 42, "text": [ " Date Unnamed: 1 Berri1 CSC Mais1 Mais2 Parc PierDup \\\n", "256 14/09/2013 00:00 2488 1494 1706 2911 979 1075 \n", "257 15/09/2013 00:00 2243 1749 1417 2933 1804 1459 \n", "258 16/09/2013 00:00 4206 2466 2799 5485 3272 855 \n", "259 17/09/2013 00:00 5506 3153 3563 6626 3882 1485 \n", "260 18/09/2013 00:00 1564 3330 1154 1803 4074 1759 \n", "\n", " Rachel1 Totem_Laurier \n", "256 3277 2456 \n", "257 3623 2527 \n", "258 4307 3012 \n", "259 5332 3745 \n", "260 1511 3921 \n", "\n", "[5 rows x 10 columns]" ] } ], "prompt_number": 42 }, { "cell_type": "markdown", "metadata": {}, "source": [ ".. en faite," ] }, { "cell_type": "code", "collapsed": false, "input": [ "df.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 43, "text": [ "(261, 10)" ] } ], "prompt_number": 43 }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Sites' full names\n", "sites = ['Berri/
de Maisonneuve', \n", " 'C\u00f4te-Ste-Catherine
(parc Beaubien)',\n", " 'de Maisonneuve/
Berri',\n", " 'de Maisonneuve/
Peel',\n", " 'du Parc/
Duluth',\n", " 'Pierre-Dupuy
(Habitat 67)', \n", " 'Rachel/
Marquette', \n", " 'Laurier
(m\u00e9tro)']" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Where we used the `
` HTML tags to break line inside some of the sites' names for better-looking tick labels.\n", "\n", "Moreover, the data set does not include every day of the year 2013:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To have a more homogenous data set, trim the data frame to have 8 full months of data, from January 1st to August 31st:" ] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "1. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, check the total number of cyclists that passed by all the counter sites from January 1st to August 31th. \n", "\n", "To do so, we could plot the data as a time series where the x-axis represents all the dates in the sample and the y-axis the total number of cyclists that passed by all counter sites. But, here with a sample of 243 dates, this would make a pretty big or not-that-precise plot. \n", "\n", "Instead, let's plot the data using a Plotly heatmap where the color scheme represents the total number of cyclists, the x-axis the month (January to August) and the y-axis the day of the month (1 to 31). So, start by importing the heatmap graph object:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, let's start with summing the daily bike traffic count across all 8 sites in the January-to-August dataframe:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Somme de toutes les rang\u00e9es du tableau, \n", "z = np.array(df.sum(axis=1).tolist())\n", "\n", "\n", "\n", "# The resulting array is 1d, 1 entry per date from Jan to Aug\n", "z.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 45, "text": [ "(261,)" ] } ], "prompt_number": 45 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we need to convert the 1D array to a 2D array where the columns correspond to the months (January to August) and the rows to the days of the month (1 to 31).\n", "\n", "The following code cell is a bit technical; hence, readers only interesting in heatmap plot styling ideas are invited to skip it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Define a convertion function \n", "def dates_a_jourXmois(X, zz):\n", " \n", " dates = df_in['Date'].tolist() # make list of dates in dataframe\n", "\n", " # (1.1) List of months for all item in dates \n", " months = np.array([datetime.datetime.strptime(date,'%d/%m/%Y').strftime('%B') \n", " for date in dates])\n", " \n", " # (1.2) Find indices of first day of the months\n", " _,ind_tmp = np.unique(months,return_index=True) # -> array([90,212,31,0, ...])\n", " the_months_ind = np.sort(ind_tmp).tolist() # -> array([0,31,59,90, ...])\n", " \n", " # (1*) Use these indices to make list months' name\n", " the_months = months[the_months_ind]\n", " N_the_months = len(the_months) # 8 months, in our case, from Jan to Aug\n", " \n", " # (2*) Make list of days of the month \n", " N_the_days = 31\n", " the_days = np.arange(1,N_the_days+1) # [1, ..., 31]\n", " \n", " # (3.1) Make tmp array filled with NaNs\n", " Z_tmp = np.empty((N_the_days,N_the_months))\n", " Z_tmp.fill(np.nan)\n", " \n", " # (3.2) Make list of indices to fill in Z_tmp month by month\n", " fill_i = the_months_ind + [len(months)]\n", " \n", " # (3.3) Loop through months\n", " for i in range(N_the_months):\n", " i0 = fill_i[i] # get start\n", " i1 = fill_i[i+1] # and end index\n", " delta_i = i1-i0 # compute their difference (either 30,31 or 28)\n", " Z_tmp[0:delta_i,i] = z_in[i0:i1] # fill in rows of \n", " \n", " # (3*) Copy tmp array to output variable\n", " Z = Z_tmp\n", " \n", " return (the_months, the_days, Z) # output coordinates for our plot\n", " \n", "# Call function, get coordinates for our plot\n", "les_mois, les_jours, Z_jourXmois = dates_a_jourXmois(df,z)\n", "\n", "#\n", "les_mois.shape, les_jours.shape, Z_jourXmois.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 46, "text": [ "((9,), (31,), (31, 9))" ] } ], "prompt_number": 46 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ok. So, now we have a 1D array of x-axis labels, a 1D array of y-axis labels and a 2D array corresponding to our heatmap.\n", "\n", "Before building the Plotly graph object, let's check the dimensions of our arrays:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The arrays are compatible, as the 2D array has as many rows as the array of y-axis labels and has as many columns as the array of x-axis labels." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "data = Data([\n", " Heatmap(\n", " x=les_mois, # x-axis labels\n", " y=les_jours, # y-axis labels\n", " z=Z_jourXmois, # 2D array\n", " scl='YIGnBu', # N.B. select colormap\n", " reversescl=True, # N.B. reverve color ordering\n", " colorbar=ColorBar(\n", " title='Nombre de cyclistes par jour',\n", " titleside='right',\n", " )\n", ")])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 122 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Where we reversed the color ordering with `reversescl=True` so that, in this case, low values will be plotted in white and high values in dark-blue.\n", "\n", "Next, make an instance of the layout object:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# URL of the data source\n", "url = 'http://donnees.ville.montreal.qc.ca/dataset/velos-comptage'\n", "\n", "def make_anno(text='',x=1,y=1):\n", " return Annotation(\n", " text= text, # annotation text\n", " x= x, # position's x-coord\n", " y= y, # and y-coord\n", " xref='paper', # use paper coords\n", " yref='paper', # for both coordinates\n", " font= Font(\n", " size=14), # increase font size (default is 12)\n", " showarrow=False, # remove arrow \n", " )" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 123 }, { "cell_type": "code", "collapsed": false, "input": [ "# \n", "title = \"Totaux journaliers de l'affluence cyclistes
\\\n", "sur 8 sites de comptage montr\u00e9alais en 2013\"\n", "\n", "#Bike traffic daily totals across 8 Montreal sites in 2013\"\n", "\n", "#\n", "anno_text = \"Source et info: \\\n", "Donn\u00e9es ouvertes de la Ville de Montr\u00e9al\"\n", "\n", "plot_width = 650 # plot width \n", "plot_height = 800 # plot height in pixels\n", "\n", "#\n", "layout = Layout(\n", " title= title, # plot's title\n", " font= Font(\n", " family='PT Sans Narrow, sans-serif', # global font\n", " size=13,\n", " color='#635F5D'\n", " ),\n", " xaxis = XAxis(\n", " title='Mois',\n", " showgrid=False\n", " ), # remove grid\n", " yaxis= YAxis(\n", " title='Jours du mois', # y-axis title\n", " autorange='reversed', # N.B. reverse tick ordering\n", " showgrid=False, # remove grid\n", " autotick=False, # custom ticks\n", " dtick=1), # showing 1 tick per day\n", " autosize=False, # custom size\n", " height=650, # plot's height in pixels \n", " width=800, # plot's width in pixels\n", " annotations= [make_anno(anno_text,1,1.03)]\n", ") " ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 124 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above, we reverse the order of the y-axis with `autorange='reversed'` in `YAxis` so that day 1 appears on the top of the y-axis instead of at the bottom. Alternatively, we could have reversed the order of the y-label array and flipped the 2D array accordingly. \n", "\n", "Next, let's add an annotation citing our data source:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "figure = Figure(data=data, layout=layout)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 125 }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "collapsed": false, "input": [ "py.iplot(figure, filename='velo-comptage-1',\n", " width=plot_width, height=plot_height) # set notebook output frame size" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 126 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "These numbers may seem a little high, but note that a particular cyclist can pass by one or multiple counter sites several times during the same day.\n" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "2." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, comparing bike traffic for some day of month (1,2,..,31) across all month may not be the most informative analysis. Why would June 9 and July 9 show the same patterns in bike traffic? That said, comparing day of week totals could be. We expect week day (Monday to Friday) to be higher than weekend totals. Now, let's see by how much.\n", "\n", "For subsequent analysis, we will drop the lesser-trafficked months of January, February and March. The corresponding dataframe and ararys are named without suffixes from this point on. So, first we trim the January to August dataframe:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Liste of booleans corresponding to days in Avril \u00e0 Ao\u00fbt\n", "i_AA = ['/01/' not in date and '/02/' not in date and \n", " '/03/' not in date and '/09/' not in date\n", " for date in df['Date']]\n", "\n", "# Trim the Jan, Feb and Mar days in df_JanAug\n", "df_AA = df[i_AA]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": true, "input": [ "df_AA.head() # dataframe now starts on April 1st" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
DateUnnamed: 1Berri1CSCMais1Mais2ParcPierDupRachel1Totem_Laurier
90 01/04/2013 00:00 371 245 276 608 343 66 2315 524
91 02/04/2013 00:00 1055 547 669 1316 840 105 2835 818
92 03/04/2013 00:00 1030 614 719 1470 869 119 3085 947
93 04/04/2013 00:00 1466 855 1027 2022 1243 261 2492 1147
94 05/04/2013 00:00 1098 543 723 1384 761 145 2553 995
\n", "

5 rows \u00d7 10 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 85, "text": [ " Date Unnamed: 1 Berri1 CSC Mais1 Mais2 Parc PierDup Rachel1 \\\n", "90 01/04/2013 00:00 371 245 276 608 343 66 2315 \n", "91 02/04/2013 00:00 1055 547 669 1316 840 105 2835 \n", "92 03/04/2013 00:00 1030 614 719 1470 869 119 3085 \n", "93 04/04/2013 00:00 1466 855 1027 2022 1243 261 2492 \n", "94 05/04/2013 00:00 1098 543 723 1384 761 145 2553 \n", "\n", " Totem_Laurier \n", "90 524 \n", "91 818 \n", "92 947 \n", "93 1147 \n", "94 995 \n", "\n", "[5 rows x 10 columns]" ] } ], "prompt_number": 85 }, { "cell_type": "code", "collapsed": false, "input": [ "df_AA.tail()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
DateUnnamed: 1Berri1CSCMais1Mais2ParcPierDupRachel1Totem_Laurier
238 27/08/2013 00:00 5566 2805 3662 6355 3373 2153 5888 3738
239 28/08/2013 00:00 5560 2759 3717 6323 3355 1881 5673 3886
240 29/08/2013 00:00 5806 2766 3902 6713 3466 1941 6044 3995
241 30/08/2013 00:00 4381 2209 3019 5409 2724 1632 5109 3106
242 31/08/2013 00:00 2682 1018 1701 3178 1385 1494 3593 2303
\n", "

5 rows \u00d7 10 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 86, "text": [ " Date Unnamed: 1 Berri1 CSC Mais1 Mais2 Parc PierDup \\\n", "238 27/08/2013 00:00 5566 2805 3662 6355 3373 2153 \n", "239 28/08/2013 00:00 5560 2759 3717 6323 3355 1881 \n", "240 29/08/2013 00:00 5806 2766 3902 6713 3466 1941 \n", "241 30/08/2013 00:00 4381 2209 3019 5409 2724 1632 \n", "242 31/08/2013 00:00 2682 1018 1701 3178 1385 1494 \n", "\n", " Rachel1 Totem_Laurier \n", "238 5888 3738 \n", "239 5673 3886 \n", "240 6044 3995 \n", "241 5109 3106 \n", "242 3593 2303 \n", "\n", "[5 rows x 10 columns]" ] } ], "prompt_number": 86 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Perform the same operation as before to get the daily totals for all sites combined:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Row sum of dataframe, get a numpy array\n", "z_AA = np.array(df_AA.sum(axis=1).tolist())\n", "\n", "# The dataframe .sum() method ignores non-number (e.g. the date strings)\n", "# while computing the row (with axis=1) sum.\n", "\n", "# The resulting array is 1d, 1 entry per date from Apr to Aug\n", "z_AA.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 92, "text": [ "(153,)" ] } ], "prompt_number": 92 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we convert the daily total 1D array into a 2D array of mean traffic for each day of week for each month. Readers only interesting in heatmap plot styling ideas are invited to skip it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Define another convertion function\n", "def dates_a_joursemXmois(X, zz):\n", " \n", " dates = X['Date'].tolist() # make list of dates in dataframe\n", " \n", " # (1.1) List of months for all item in dates\n", " months = np.array([datetime.datetime.strptime(i,'%d/%m/%Y').strftime('%B') \n", " for i in dates])\n", " \n", " # (1.2) Find indices of first day of the months\n", " _,ind_tmp = np.unique(months,return_index=True) # -> array([0,122,91,61,30])\n", " the_months_ind = np.sort(ind_tmp).tolist() # -> array([0,30,61,91,122])\n", " \n", " # (1*) Use these indices to make list months' names\n", " the_months = months[the_months_ind]\n", " N_the_months = len(the_months) # 5 months, in our case, from Apr to Aug\n", " \n", " # (2.1) List of week day names for all items in dates\n", " wdays = np.array([datetime.datetime.strptime(i,'%d/%m/%Y').strftime('%A') \n", " for i in dates])\n", " \n", " # (2*) Make list of week day names\n", " the_wdays = np.array(['Lundis','Mardis','Mercredis','Jeudis','Vendredis',\n", " 'Samedis','Dimanches'])\n", " N_the_wdays = len(the_wdays) # 7 days in a week\n", " \n", " # (3.1) Init. tmp array 7 days-of-week by 5 months \n", " Z_tmp = np.empty((N_the_wdays,N_the_months))\n", " \n", " i_month = 0 # init. month counter\n", "\n", " for the_month in the_months: # loop through the months\n", " \n", " # (3.2) Find indices corresp. to the_month, trim wdays and z_in\n", " ind_month = np.where(months == the_month)\n", " wdays_month = wdays[ind_month]\n", " z_month = zz[ind_month]\n", " \n", " i_wday = 0 # init/re-init week day counter\n", " \n", " for the_wday in the_wdays: # loop through the week days\n", " \n", " # (3.3) Find indices corresp. to the week day, trim z_month, \n", " # fill in Z_tmp with mean value\n", " ind_month_wday = np.where(wdays_month == the_wday) \n", " Z_tmp[i_wday,i_month] = np.mean(z_month[ind_month_wday])\n", " \n", " i_wday += 1 # inc. week day counter\n", " \n", " i_month += 1 # inc. month counter\n", " \n", " # (3*) Copy tmp array to output variable\n", " Z = Z_tmp\n", "\n", " return (the_months, the_wdays, Z) # output coordinates for our next plot\n", " \n", "# Call conversion function, get coordinates for our next plot\n", "les_mois, les_joursem, Z_moisXjoursem = dates_a_joursemXmois(df_AA, z_AA)\n", "\n", "les_mois.shape, les_joursem.shape, Z_moisXjoursem.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 130, "text": [ "((5,), (7,), (7, 5))" ] } ], "prompt_number": 130 }, { "cell_type": "markdown", "metadata": {}, "source": [ "A quick check on the dimensions:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, make the y-axis labels plural (e.g. Mondays instead of Monday) and normalized the 2D array by its maximum value:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Normalize 2D array by max value\n", "Z_moisXjoursem_norm = Z_moisXjoursem/Z_moisXjoursem.max()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 131 }, { "cell_type": "code", "collapsed": false, "input": [ "Z_moisXjoursem" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 135, "text": [ "array([[ nan, nan, nan, nan, nan],\n", " [ nan, nan, nan, nan, nan],\n", " [ nan, nan, nan, nan, nan],\n", " [ nan, nan, nan, nan, nan],\n", " [ nan, nan, nan, nan, nan],\n", " [ nan, nan, nan, nan, nan],\n", " [ nan, nan, nan, nan, nan]])" ] } ], "prompt_number": 135 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the colorbar object is meant to be linked to the `'colorbar'` key in `Heatmap` or, more generally, in the trace object in question. Plotly colorbars are considered specifications of each plotted trace. In other words, Plotly allows users to add multiple colorbars to the same figure. So," ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_cbartitle = \"Mean bike traffic
per week day
\\\n", "norm. by max value
(so 1=max value)\"\n", "\n", "# Make instance of data object\n", "data = Data([\n", " Heatmap(\n", " x=les_mois, # x-axis labels\n", " y=les_joursem, # y-axis labels \n", " z=Z_moisXjoursem_norm, # 2D array for heatmap\n", " scl='Greys', # N.B. try other predefined colormap\n", " reversescl=True, # reverse its color ordering\n", " opacity=0.9, # a slightly transparent color scale\n", " colorbar= ColorBar(\n", " title=my_cbartitle, # color bar title\n", " titleside='bottom', # placed below the color bar\n", " thickness=25, # color bar thickness in px\n", " #autotick=False, # custom colorbar ticks\n", " ticks='outside', # tick outside colorbar\n", " dtick=0.1 # distance between ticks\n", " )\n", ")])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 132 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, update the layout object (of the previous figure):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Update the figure's height (in px)\n", "layout.update(height=500)\n", "\n", "# Update y-axis title and set tick angle\n", "layout['yaxis'].update(title='Days of the weeks',\n", " tickangle=-20)\n", "\n", "# Update annotation's position\n", "layout['annotations'][0].update(y=1.08)\n", "\n", "# Update title\n", "#my_title = \"Fig 5.1b: Bike traffic across 8 Montreal sites in 2013\"\n", "#my_layout.update(title=my_title)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 133 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, make a new instance of the figure object and send it to Plotly:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make instance of figure object\n", "figure = Figure(data=data, layout=layout)\n", "\n", "# Send figure object to Plotly's server, show result in notebook\n", "py.iplot(figure, filename='s5_cyclists-per-wday')" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 134 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thursdays in July were on average the busiest days in 2013. Weekends are less busy than weekday by roughly 30 to 40 percent, a pretty interesting result." ] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "5.2 Correlation heatmap between sites" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our next plot will be a correlation heatmap between the 8 sites. The correlations coefficient of two datasets $X$ and $Y$ is computed as follows:\n", "\n", "$$ \\text{Corr}_{X,Y} = \\frac{\\sum_i^N (X_i - \\bar{X})(Y_i-\\bar{Y})}\n", "{\\sqrt{\\sum_i^N (X_i - \\bar{X})}\\sqrt{\\sum_i^N (Y_i - \\bar{Y})}} $$\n", "\n", "Where $N$ is the sample size. $X_i$ and $Y_i$ are the sample values in each datasets.\n", "\n", "Here, for us, we have not two by eight datasets and we would like to compute the correlation coefficient of each combination of dataset, making up a *correlation matrix*. The sample corresponds to the daily traffic totals at each sites. \n", "\n", "So, first extract these values from the dataframe into a numpy 2D array:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Get values of every row of columns 2 and up\n", "Z_dayXsite = df.ix[:,2:].values\n", "\n", "# 153 sample days (rows) at 8 sites (columns)\n", "Z_dayXsite.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 29, "text": [ "(153, 8)" ] } ], "prompt_number": 29 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, compute the correlation matrix " ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Compute correlation, rows (i.e. days of the year) are our sample space\n", "Corr_site = np.corrcoef(Z_dayXsite,rowvar=0)\n", "\n", "# 8 sites correlated with themselves\n", "Corr_site.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 30, "text": [ "(8, 8)" ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The correlation matrix contains 64 entries, one for each permutation (i.e. ordered combinations) of bike counter sites." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before plotting the correlation matrix, we will need to adjust the margin around the heatmap so that the long site names fit on the tick labels." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import Margin\n", "from plotly.graph_objs import Margin\n", "\n", "help(Margin) # help!" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on class Margin in module plotly.graph_objs.graph_objs:\n", "\n", "class Margin(PlotlyDict)\n", " | A dictionary-like object holding plot margin information.\n", " | \n", " | \n", " | Quick method reference:\n", " | \n", " | Margin.update(changes)\n", " | Margin.strip_style()\n", " | Margin.get_data()\n", " | Margin.to_graph_objs()\n", " | Margin.validate()\n", " | Margin.to_string()\n", " | Margin.force_clean()\n", " | \n", " | Valid keys:\n", " | \n", " | l [required=False] (value=number: x >= 0):\n", " | Left margin size in pixels.\n", " | \n", " | r [required=False] (value=number: x >= 0):\n", " | Right margin size in pixels.\n", " | \n", " | b [required=False] (value=number: x >= 0):\n", " | Bottom margin size in pixels.\n", " | \n", " | t [required=False] (value=number: x >= 0):\n", " | Top margin size in pixels.\n", " | \n", " | pad [required=False] (value=number: x >= 0):\n", " | The distance between edge of the plot and the bounding rectangle\n", " | that encloses the plot.\n", " | \n", " | autoexpand [required=False] (value=bool: True | False):\n", " | Aw, snap! Undocumented!\n", " | \n", " | Method resolution order:\n", " | Margin\n", " | PlotlyDict\n", " | __builtin__.dict\n", " | __builtin__.object\n", " | \n", " | Methods inherited from PlotlyDict:\n", " | \n", " | __init__(self, *args, **kwargs)\n", " | \n", " | force_clean(self, caller=True)\n", " | Attempts to convert to graph_objs and call force_clean() on values.\n", " | \n", " | Calling force_clean() on a PlotlyDict will ensure that the object is\n", " | valid and may be sent to plotly. This process will also remove any\n", " | entries that end up with a length == 0.\n", " | \n", " | Careful! This will delete any invalid entries *silently*.\n", " | \n", " | get_data(self)\n", " | Returns the JSON for the plot with non-data elements stripped.\n", " | \n", " | get_ordered(self, caller=True)\n", " | \n", " | strip_style(self)\n", " | Strip style from the current representation.\n", " | \n", " | All PlotlyDicts and PlotlyLists are guaranteed to survive the\n", " | stripping process, though they made be left empty. This is allowable.\n", " | \n", " | Keys that will be stripped in this process are tagged with\n", " | `'type': 'style'` in the INFO dictionary listed in graph_objs_meta.py.\n", " | \n", " | This process first attempts to convert nested collections from dicts\n", " | or lists to subclasses of PlotlyList/PlotlyDict. This process forces\n", " | a validation, which may throw exceptions.\n", " | \n", " | Then, each of these objects call `strip_style` on themselves and so\n", " | on, recursively until the entire structure has been validated and\n", " | stripped.\n", " | \n", " | to_graph_objs(self, caller=True)\n", " | Walk obj, convert dicts and lists to plotly graph objs.\n", " | \n", " | For each key in the object, if it corresponds to a special key that\n", " | should be associated with a graph object, the ordinary dict or list\n", " | will be reinitialized as a special PlotlyDict or PlotlyList of the\n", " | appropriate `kind`.\n", " | \n", " | to_string(self, level=0, indent=4, eol='\\n', pretty=True, max_chars=80)\n", " | Returns a formatted string showing graph_obj constructors.\n", " | \n", " | Example:\n", " | \n", " | print obj.to_string()\n", " | \n", " | Keyword arguments:\n", " | level (default = 0) -- set number of indentations to start with\n", " | indent (default = 4) -- set indentation amount\n", " | eol (default = '\n", " | ') -- set end of line character(s)\n", " | pretty (default = True) -- curtail long list output with a '...'\n", " | max_chars (default = 80) -- set max characters per line\n", " | \n", " | update(self, dict1=None, **dict2)\n", " | Update current dict with dict1 and then dict2.\n", " | \n", " | This recursively updates the structure of the original dictionary-like\n", " | object with the new entries in the second and third objects. This\n", " | allows users to update with large, nested structures.\n", " | \n", " | Note, because the dict2 packs up all the keyword arguments, you can\n", " | specify the changes as a list of keyword agruments.\n", " | \n", " | Examples:\n", " | # update with dict\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj.update(update_dict)\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | # update with list of keyword arguments\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | obj.update(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | This 'fully' supports duck-typing in that the call signature is\n", " | identical, however this differs slightly from the normal update\n", " | method provided by Python's dictionaries.\n", " | \n", " | validate(self, caller=True)\n", " | Recursively check the validity of the keys in a PlotlyDict.\n", " | \n", " | The valid keys constitute the entries in each object\n", " | dictionary in INFO stored in graph_objs_meta.py.\n", " | \n", " | The validation process first requires that all nested collections be\n", " | converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,\n", " | each of these objects call `validate` and so on, recursively,\n", " | until the entire object has been validated.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from PlotlyDict:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from PlotlyDict:\n", " | \n", " | __metaclass__ = \n", " | A meta class for PlotlyDict class creation.\n", " | \n", " | The sole purpose of this meta class is to properly create the __doc__\n", " | attribute so that running help(Obj), where Obj is a subclass of PlotlyDict,\n", " | will return information about key-value pairs for that object.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from __builtin__.dict:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __getitem__(...)\n", " | x.__getitem__(y) <==> x[y]\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __setitem__(...)\n", " | x.__setitem__(i, y) <==> x[i]=y\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from __builtin__.dict:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", "\n" ] } ], "prompt_number": 31 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, let's first check the range of values of the correlation matrix:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "Corr_site.min(), Corr_site.max()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 32, "text": [ "(0.47982343592869092, 1.0)" ] } ], "prompt_number": 32 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we overwrite the default minimum and maximum color scale values (with `'zmin'` and '`zmax'` respectively) to round up the color bar's range. So, consider," ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make data object\n", "my_data = Data([Heatmap(x=sites, # sites on both\n", " y=sites, # axes\n", " z=Corr_site, # correlation as color contours \n", " zauto=False, # N.B. overwrite Plotly default color levels\n", " zmin=0.4, # value of min color level\n", " zmax=1, # value of max color level\n", " scl='YIOrRd', # N.B. light yellow-orange-red colormap\n", " reversescl=True # N.B. inverse colormap order\n", " )])\n", " \n", "# Make layout object\n", "my_title = \"Fig 5.2a: Bike traffic correlations between different sites in 2013\"\n", "my_layout = Layout(title= my_title, # plot title\n", " font= Font(family='Georgia, serif', # global font \n", " color='#635F5D'),\n", " autosize=False, # turn off autosize \n", " height=500, # plot's height in pixels \n", " width=600, # plot's width in pixels\n", " margin= Margin(t=100,b=120,r=100,l=100) # N.B. margin between frame\n", " ) # and axis in pixels" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 33 }, { "cell_type": "code", "collapsed": false, "input": [ "# Make Figure object, send to Plotly and show in notebook \n", "my_fig = Figure(data=my_data, layout=my_layout)\n", "py.iplot(my_fig, filename='s5_correlations')" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 34 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that Plotly automatically tilts the x-axis label to make them fit in the frame. Nice.\n", "\n", "As the correlation coefficient of $X$ and $Y$ is the same as the correlation coefficient of $Y$ and $X$, we have a lot of useless information on the above plot. Let's remedy this situation with some inspiration from the Seaborn project and, more specificaly this plot. Thanks!" ] }, { "cell_type": "heading", "level": 5, "metadata": {}, "source": [ "Seaborn styling in Plotly" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To convert our previous plot to the Seaborn styling, we must:\n", "\n", "* Make a new array with values from correlation matrix but without duplicates,\n", "* Reverse the order of the y-axis,\n", "* Tilt the x-axis labels to the vertical,\n", "* Define a custom color scale." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Convert data and labels array to match Seaborn\n", "def make_seaborn(x_old,y_old,z_old):\n", " x_sns = x_old[0:-1] # remove last entry in x-corrd array\n", " y_sns = y_old[-1:0:-1] # remove first entry, reverse order in y-coord array\n", " m,n = z_old.shape # get dimension of original array\n", " tmp = np.empty((m,n)) # init. tmp array\n", " tmp.fill(np.nan) # with NaNs\n", " for i in range(m): # loop through rows\n", " tmp[i,0:i] = z_old[i,0:i] # add items below the diagonal (the unique entries)\n", " tmp = np.flipud(tmp) # reverse order of all columns in tmp array\n", " z_sns = tmp # copy tmp array to outpur variable\n", " return (x_sns,y_sns,z_sns) # return new coodinates in tuple\n", "\n", "# Call conversion function, get coordinates for our next plot\n", "sites_x_sns, sites_y_sns, Corr_site_sns = make_seaborn(sites,sites,Corr_site)\n", "\n", "# Check position of NaNs in new 2D array\n", "np.isnan(Corr_site_sns)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 35, "text": [ "array([[False, False, False, False, False, False, False, True],\n", " [False, False, False, False, False, False, True, True],\n", " [False, False, False, False, False, True, True, True],\n", " [False, False, False, False, True, True, True, True],\n", " [False, False, False, True, True, True, True, True],\n", " [False, False, True, True, True, True, True, True],\n", " [False, True, True, True, True, True, True, True],\n", " [ True, True, True, True, True, True, True, True]], dtype=bool)" ] } ], "prompt_number": 35 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall that Plotly maps the array's rows to y-axis and the array's columns to x-axis. So when plotted `Corr_site_sns` will have non-NaNs values on the lower diagonal as on the Seaborn plot." ] }, { "cell_type": "heading", "level": 5, "metadata": {}, "source": [ "Custom color scales in Plotly" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we build a custom color scale (or color map or color scheme).\n", "\n", "A custom color scale in Plotly is a list of lists linked to the `'scl'` key in `Heatmap`, `Contour`, `Histogram2d` or `Histogram2dcontour` where the nested lists have the normalized level (more info below) values as first item and the level colors as second item using whatever color model you prefer.\n", "\n", "From this point on, Plotly takes over. The color scale is interpolated in-between the color levels, resulting in a continuous map from values in the 2D array to colors. So," ] }, { "cell_type": "code", "collapsed": false, "input": [ "scl_sns = [[0,\"#00008B\"], # color of minimun level (from 'zmin')\n", " [0.25,\"#6A5ACD\"], [0.5,\"#FFE6F8\"], [0.75, \"#C71585\"], # in-between\n", " [1, \"#8B0000\"]] # color of maximum level (from 'zmax')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 36 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `0` color level is the level of the value linked to the `'zmin'` key in `Heatmap`. Correspondingly, the `1` color level is the level of the value linked to the `'zmax'` key set in `Heatmap`.\n", "\n", "By default, the value of `'zmin'` is the 2D array's minimum value of the value of `'zmax'` is its maxmium value. That said, they can be easily overwritten. Consider the following instance of the data object:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make new instance of data object\n", "my_data = Data([Heatmap(x=sites_x_sns, # x-labels \n", " y=sites_y_sns, # y-labels\n", " z=Corr_site_sns, # 2D array\n", " scl=scl_sns, # N.B. custom color scales list of lists\n", " zauto=False, # N.B. custom color levels\n", " zmin=0.3, # value of min color level\n", " zmax=1, # value of max color level\n", " )])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 37 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ok, now tilt the axes' labels and add two annotations:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Add a few style options to XAxis and YAxis objects\n", "def make_axes(tickangle_in):\n", " return dict(autotick=False, # custom ticks \n", " tickangle=tickangle_in, # rotate tick labels\n", " showgrid=False, # remove grid\n", " showline=False) # remove axes line\n", "\n", "# Tilt the labels of both axes\n", "my_layout.update(xaxis=XAxis(make_axes(90)))\n", "my_layout.update(yaxis=YAxis(make_axes(0)))\n", "\n", "# Add an annotation citing the data source\n", "my_anno_text1 = \"Source and info:
\\\n", "Donn\u00e9es ouvertes de la Ville de Montr\u00e9al\"\n", "my_anno1 = make_anno(my_anno_text1,0.9,0.875)\n", "\n", "# Add an annotation citing start and end of sample\n", "my_anno_text2 = \"Data used:
\\\n", "April 01, 2013
to
August 31, 2013\"\n", "my_anno2 = make_anno(my_anno_text2,0.8,0.55)\n", "\n", "# Update 'annotations' in layout object \n", "my_layout.update(annotations=[my_anno1,my_anno2])\n", "\n", "# Update title\n", "my_title = \"Fig 5.2b: Bike traffic correlations between different sites\"\n", "my_layout.update(title=my_title)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, make a new instance of the figure object and send it to Plotly:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make new instance of figure object\n", "my_fig = Figure(data=my_data, layout=my_layout)\n", "\n", "# Send figure object to Plotly\n", "py.iplot(my_fig, filename='s5_correlations-seaborn')" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 39 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Much better. We now have room to spare for some annotations.\n", "\n", "From the above plot, all the sites are well-correlated and five of them are almost perfectly correlation (correlation coefficients > 0.9). This means that we do not need to check the 8 sites to evaluate day-to-day traffic fluctuations. In other words, if traffic increases at one site, it is very likely that traffic increases at the 7 other sites." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "5.3 Contours of traffic progression from most to least trafficked sites" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Positive trends in bike traffic are expected to *propage* from the most trafficked counter site to the least trafficked counter site over time and vice versa. In our next plot, we attempt to assess this claim with a contour plot of daily bike traffic totals as a function of the date and counter site." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import Contour\n", "from plotly.graph_objs import Contour\n", "\n", "help(Contour)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on class Contour in module plotly.graph_objs.graph_objs:\n", "\n", "class Contour(PlotlyTrace)\n", " | A dictionary-like object for representing a contour plot in plotly.\n", " | \n", " | Example:\n", " | \n", " | z = [[0, 1, 0, 1, 0],\n", " | [1, 0, 1, 0, 1],\n", " | [0, 1, 0, 1, 0],]\n", " | y = ['a', 'b', 'c']\n", " | x = [1, 2, 3, 4, 5]\n", " | py.plot([Contour(z=z, x=x, y=y)])\n", " | \n", " | \n", " | Quick method reference:\n", " | \n", " | Contour.update(changes)\n", " | Contour.strip_style()\n", " | Contour.get_data()\n", " | Contour.to_graph_objs()\n", " | Contour.validate()\n", " | Contour.to_string()\n", " | Contour.force_clean()\n", " | \n", " | Valid keys:\n", " | \n", " | z [required=True] (value=matrix-like: list of lists, numpy.matrix):\n", " | The data that describes the mapping. The dimensions of the 'z'\n", " | matrix are (n x m) where there are 'n' ROWS defining the number of\n", " | partitions along the y-axis; this is equal to the length of the 'y'\n", " | array. There are 'm' COLUMNS defining the number of partitions along\n", " | the x-axis; this is equal to the length of the 'x' array. Therefore,\n", " | the color of the cell z[i][j] is mapped to the ith partition of the\n", " | y-axis (starting from the bottom of the plot) and the jth partition\n", " | of the x-axis (starting from the left of the plot). In Python, a\n", " | (non-numpy) matrix is best thought of as a list of lists (of lists,\n", " | of lists, etc.). Therefore, running len(z) will give you the number\n", " | of ROWS and running len(z[0]) will give you the number of COLUMNS.\n", " | If you ARE using numpy, then running z.shape will give you the\n", " | tuple, (n, m), e.g., (3, 5).\n", " | \n", " | x [required=False] (value=array-like of numbers, strings, datetimes):\n", " | This array-like value contains the HORIZONTAL labels referring to\n", " | the COLUMNS of the 'z' matrix. If strings, the x-labels are spaced\n", " | evenly. If the dimensions of z are (n x m), the length of the 'x'\n", " | array should be 'm'.\n", " | \n", " | y [required=False] (value=array-like of numbers, strings, datetimes):\n", " | This array-like value contains the VERTICAL labels referring to the\n", " | ROWS of the 'z' matrix. If strings, the y-labels are spaced evenly.\n", " | If the dimensions of z are (n x m), the length of the 'y' array\n", " | should be 'n'.\n", " | \n", " | name [required=False] (value=string):\n", " | The label associated with this trace. This name will appear in the\n", " | legend, in the column header in the spreadsheet, and on hover.\n", " | \n", " | autocontour [required=False] (value=bool: True | False):\n", " | If True, the contours settings are set automatically. If False, the\n", " | contours settings must be set manually with the contours object.\n", " | \n", " | ncontours [required=False] (value=bool: True | False):\n", " | Speficy the number of countours lines that will appear.\n", " | \n", " | contours [required=False] (value=Contours object | dictionary-like):\n", " | A dictionary-like object defining parameters of the contours in this\n", " | plot like spacing, whether or not to show lines, etc.\n", " | \n", " | For more, run `help(plotly.graph_objs.Contours)`\n", " | \n", " | line [required=False] (value=Line object | dictionary-like):\n", " | Set contour line style (e.g. dash) and width. If showlines=True in\n", " | Contour, select line color. Select smoothing level.\n", " | \n", " | For more, run `help(plotly.graph_objs.Line)`\n", " | \n", " | scl [required=False] (value=array_like of value-color pairs | 'Greys' |\n", " | 'Greens' | 'Bluered' | 'Hot' | 'Picnic' | 'Portland' | 'Jet' | 'RdBu' |\n", " | 'Blackbody' | 'Earth' | 'Electric' | 'YIOrRd' | 'YIGnBu'):\n", " | The color scale. The strings are pre-defined color scales. For\n", " | custom color scales, define a list of color-value pairs, where the\n", " | first element of the pair corresponds to a normalized value of z\n", " | from 0-1 (i.e. (z-zmin)/(zmax-zmin)), and the second element of\n", " | pair corresponds to a color.\n", " | \n", " | Examples:\n", " | ['Greys', [[0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)']], [[0,\n", " | 'rgb(8, 29, 88)'], [0.125, 'rgb(37, 52, 148)'], [0.25, 'rgb(34,\n", " | 94, 168)'], [0.375, 'rgb(29, 145, 192)'], [0.5, 'rgb(65, 182,\n", " | 196)'], [0.625, 'rgb(127, 205, 187)'], [0.75, 'rgb(199, 233,\n", " | 180)'], [0.875, 'rgb(237, 248, 217)'], [1, 'rgb(255, 255,\n", " | 217)']]]\n", " | \n", " | reversescl [required=False] (value=bool: True | False):\n", " | Aw, snap! Undocumented!\n", " | \n", " | showscale [required=False] (value=bool: True | False):\n", " | Toggle whether or not the color scale associated with this mapping\n", " | will be shown alongside the rendered figure.\n", " | \n", " | colorbar [required=False] (value=ColorBar object | dictionary-like):\n", " | This object represents a color bar that will be shown on the figure\n", " | where the color is related to the data being shown.\n", " | \n", " | For more, run `help(plotly.graph_objs.ColorBar)`\n", " | \n", " | zauto [required=False] (value=bool: True | False):\n", " | Set to False to overwrite the default values of 'zmax' and 'zmax'.\n", " | \n", " | zmin [required=False] (value=number):\n", " | The value used as the minimum in the color scale normalization in\n", " | 'scl'. The default is the minimum of the 'z' data values.\n", " | \n", " | zmax [required=False] (value=number):\n", " | The value used as the maximum in the color scale normalization in\n", " | 'scl'. The default is the minimum of the 'z' data values.\n", " | \n", " | opacity [required=False] (value=number: x in [0, 1]):\n", " | Sets the opacity, or transparency, of this object. Also known as the\n", " | alpha channel of colors, if the object's color is given in terms of\n", " | 'rgba', this does not need to be defined.\n", " | \n", " | showlegend [required=False] (value=bool: True | False):\n", " | Toggle whether or not this trace will show up in the legend.\n", " | \n", " | stream [required=False] (value=dictionary-like):\n", " | The stream dict that initializes traces as writable-streams, for use\n", " | with the real-time streaming API. Learn more here:\n", " | https://plot.ly/python/streaming/\n", " | \n", " | xaxis [required=False] (value=string: 'x1' | 'x2' | 'x3' | etc.):\n", " | This key determines which xaxis the x coordinates in this trace will\n", " | reference in the figure. 'x' references layout['xaxis'] and 'x2'\n", " | references layout['xaxis2']. 'x1' will always refer to\n", " | layout['xaxis'] or layout['xaxis1'], they are the same.\n", " | \n", " | yaxis [required=False] (value=string: 'y1' | 'y2' | 'y3' | etc.):\n", " | This key determines which yaxis the y coordinates in this trace will\n", " | reference in the figure. 'y' references layout['yaxis'] and 'y2'\n", " | references layout['yaxis2']. 'y1' will always refer to\n", " | layout['yaxis'] or layout['yaxis1'], they are the same.\n", " | \n", " | visible [required=False] (value=bool: True | False):\n", " | Toggles whether this will actually be visible in the rendered\n", " | figure.\n", " | \n", " | x0 [required=False] (value=number):\n", " | The location of the first coordinate of the x-axis.Use with 'dx' an\n", " | alternative to an 'x' list/array.\n", " | \n", " | dx [required=False] (value=number):\n", " | Spacing between x-axis coordinates. Use with 'x0' an alternative to\n", " | an 'x' list/array.\n", " | \n", " | y0 [required=False] (value=number):\n", " | The location of the first coordinate of the y-axis.Use with 'dy' an\n", " | alternative to an 'y' list/array.\n", " | \n", " | dy [required=False] (value=number):\n", " | Spacing between y-axis coordinates. Use with 'y0' an alternative to\n", " | an 'y' list/array.\n", " | \n", " | xtype [required=False] (value='array' | 'scaled'):\n", " | If set to 'scaled' and 'x' is linked to a list/array, then the\n", " | horizontal labels are scaled to a list of integers of unit step\n", " | starting from 0.\n", " | \n", " | ytype [required=False] (value='array' | 'scaled'):\n", " | If set to 'scaled' and 'y' is linked to a list/array, then the\n", " | vertical labels are scaled to a list of integers of unit step\n", " | starting from 0.\n", " | \n", " | type [required=True] (value=contour):\n", " | Plotly identifier for this data's trace type. This defines how this\n", " | data dictionary will be handled. For example, 'scatter' type expects\n", " | x and y data-arrays corresponding to (x, y) coordinates wheras a\n", " | 'histogram' only requires a single x or y array and a 'heatmap' type\n", " | requires a z matrix.\n", " | \n", " | Method resolution order:\n", " | Contour\n", " | PlotlyTrace\n", " | PlotlyDict\n", " | __builtin__.dict\n", " | __builtin__.object\n", " | \n", " | Methods inherited from PlotlyTrace:\n", " | \n", " | __init__(self, *args, **kwargs)\n", " | \n", " | to_string(self, level=0, indent=4, eol='\\n', pretty=True, max_chars=80)\n", " | Returns a formatted string showing graph_obj constructors.\n", " | \n", " | Example:\n", " | \n", " | print obj.to_string()\n", " | \n", " | Keyword arguments:\n", " | level (default = 0) -- set number of indentations to start with\n", " | indent (default = 4) -- set indentation amount\n", " | eol (default = '\n", " | ') -- set end of line character(s)\n", " | pretty (default = True) -- curtail long list output with a '...'\n", " | max_chars (default = 80) -- set max characters per line\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from PlotlyDict:\n", " | \n", " | force_clean(self, caller=True)\n", " | Attempts to convert to graph_objs and call force_clean() on values.\n", " | \n", " | Calling force_clean() on a PlotlyDict will ensure that the object is\n", " | valid and may be sent to plotly. This process will also remove any\n", " | entries that end up with a length == 0.\n", " | \n", " | Careful! This will delete any invalid entries *silently*.\n", " | \n", " | get_data(self)\n", " | Returns the JSON for the plot with non-data elements stripped.\n", " | \n", " | get_ordered(self, caller=True)\n", " | \n", " | strip_style(self)\n", " | Strip style from the current representation.\n", " | \n", " | All PlotlyDicts and PlotlyLists are guaranteed to survive the\n", " | stripping process, though they made be left empty. This is allowable.\n", " | \n", " | Keys that will be stripped in this process are tagged with\n", " | `'type': 'style'` in the INFO dictionary listed in graph_objs_meta.py.\n", " | \n", " | This process first attempts to convert nested collections from dicts\n", " | or lists to subclasses of PlotlyList/PlotlyDict. This process forces\n", " | a validation, which may throw exceptions.\n", " | \n", " | Then, each of these objects call `strip_style` on themselves and so\n", " | on, recursively until the entire structure has been validated and\n", " | stripped.\n", " | \n", " | to_graph_objs(self, caller=True)\n", " | Walk obj, convert dicts and lists to plotly graph objs.\n", " | \n", " | For each key in the object, if it corresponds to a special key that\n", " | should be associated with a graph object, the ordinary dict or list\n", " | will be reinitialized as a special PlotlyDict or PlotlyList of the\n", " | appropriate `kind`.\n", " | \n", " | update(self, dict1=None, **dict2)\n", " | Update current dict with dict1 and then dict2.\n", " | \n", " | This recursively updates the structure of the original dictionary-like\n", " | object with the new entries in the second and third objects. This\n", " | allows users to update with large, nested structures.\n", " | \n", " | Note, because the dict2 packs up all the keyword arguments, you can\n", " | specify the changes as a list of keyword agruments.\n", " | \n", " | Examples:\n", " | # update with dict\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj.update(update_dict)\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | # update with list of keyword arguments\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | obj.update(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | This 'fully' supports duck-typing in that the call signature is\n", " | identical, however this differs slightly from the normal update\n", " | method provided by Python's dictionaries.\n", " | \n", " | validate(self, caller=True)\n", " | Recursively check the validity of the keys in a PlotlyDict.\n", " | \n", " | The valid keys constitute the entries in each object\n", " | dictionary in INFO stored in graph_objs_meta.py.\n", " | \n", " | The validation process first requires that all nested collections be\n", " | converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,\n", " | each of these objects call `validate` and so on, recursively,\n", " | until the entire object has been validated.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from PlotlyDict:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from PlotlyDict:\n", " | \n", " | __metaclass__ = \n", " | A meta class for PlotlyDict class creation.\n", " | \n", " | The sole purpose of this meta class is to properly create the __doc__\n", " | attribute so that running help(Obj), where Obj is a subclass of PlotlyDict,\n", " | will return information about key-value pairs for that object.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from __builtin__.dict:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __getitem__(...)\n", " | x.__getitem__(y) <==> x[y]\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __setitem__(...)\n", " | x.__setitem__(i, y) <==> x[i]=y\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from __builtin__.dict:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", "\n" ] } ], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "To better visualize the *traffic propagation*, the counter sites will be placed on the x-axis in order, from the most trafficked to the least in the yearly total.\n", "\n", "So, first, sum the traffic total of all dates from April 1 to August 31:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Column sum of dataframe, keep columns correps. to sites, get a numpy array\n", "z_site = np.array(df.sum(axis=0).tolist()[2:])\n", "\n", "# The dataframe .sum() method ignores non-number (e.g. the date strings)\n", "# while computing the column (with axis=0) sum.\n", "\n", "# Show list of sites and total cyclist count\n", "zip(sites, z_site)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 41, "text": [ "[('Berri/
de Maisonneuve', '617472'),\n", " ('C\\xc3\\xb4te-Ste-Catherine
(parc Beaubien)', '272218'),\n", " ('de Maisonneuve/
Berri', '393995'),\n", " ('de Maisonneuve/
Peel', '716427'),\n", " ('du Parc/
Duluth', '367109'),\n", " ('Pierre-Dupuy
(Habitat 67)', '273008'),\n", " ('Rachel/
Marquette', '693354'),\n", " ('Laurier
(m\\xc3\\xa9tro)', '414548')]" ] } ], "prompt_number": 41 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, order the sites from most trafficked to least (the x-axis labels), swap the dataframe's columns accordingly and make a list of dates (the y-axis labels):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# with the datetime module\n", "\n", "def convert_in_order(df_in,sites,z_site):\n", "\n", " dates = df_in['Date'].tolist() # make list of dates in dataframe\n", " \n", " # Get values of every row of columns 2 and up (as in subsection 5.2)\n", " Z_dayXsite = df.ix[:,2:].values\n", " \n", " # (1-) Get list of dates in words (e.g. Mon, Jan 1)\n", " dates_in_words = [datetime.datetime.strptime(i,'%d/%m/%Y').strftime('%a, %b %d') \n", " for i in dates]\n", " \n", " # (2.1-3.1) Get indices of sorted array in decreasing order\n", " ind_in_order = np.argsort(z_site)[::-1]\n", "\n", " # (2-) Shuffle sites list in order\n", " sites_in_order = [sites[i] for i in ind_in_order]\n", " \n", " # (3-) Shuffle columns (corresp. to the sites) of 2D array in order\n", " Z_dayXsite_in_order = Z_dayXsite[:,ind_in_order]\n", "\n", " # Output coordinates for our plot\n", " return (dates_in_words, sites_in_order, Z_dayXsite_in_order) \n", " \n", "# Get plot coordinates\n", "dates_in_words,sites_in_order,Z_dayXsite_in_order = convert_in_order(df,sites,z_site)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 42 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Data for contours plot is input in the `Contour` object. The `Contours` object (N.B. plural) contains the style options for the contours, similar to what `XBins` and `YBins` are for `Histogram`. \n", "\n", "Ultimately, the `Contours` is meant to be linked to the `'contours'` key in `Contour`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import Contours\n", "from plotly.graph_objs import Contours\n", "\n", "help(Contours) # run help()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on class Contours in module plotly.graph_objs.graph_objs:\n", "\n", "class Contours(PlotlyDict)\n", " | A dictionary-like object for representing a contours object in plotly.\n", " | \n", " | This object exists inside definitions for a contour plot.\n", " | \n", " | \n", " | Quick method reference:\n", " | \n", " | Contours.update(changes)\n", " | Contours.strip_style()\n", " | Contours.get_data()\n", " | Contours.to_graph_objs()\n", " | Contours.validate()\n", " | Contours.to_string()\n", " | Contours.force_clean()\n", " | \n", " | Valid keys:\n", " | \n", " | showlines [required=Aw, snap! Undocumented!] (value=bool: True | False):\n", " | Toggle whether the contour lines appear on the plot.\n", " | \n", " | start [required=False] (value=number):\n", " | Sets the value of the first contour level.\n", " | \n", " | end [required=False] (value=number):\n", " | Sets the value of the last contour level.\n", " | \n", " | size [required=False] (value=number):\n", " | Sets the size of each contour level.\n", " | \n", " | coloring [required=False] (value=number: x < 'fill' | 'heatmap' |\n", " | 'lines' | 'none' ):\n", " | Choose coloring method. The default value is 'fill' where coloring\n", " | is done even between each contour line. 'heatmap' colors on a grid\n", " | point-by-grid point basis. 'lines' colors only the contour lines,\n", " | each with respect to the color scale.'none' prints all contour lines\n", " | with the same color,choose color in Line.\n", " | \n", " | Method resolution order:\n", " | Contours\n", " | PlotlyDict\n", " | __builtin__.dict\n", " | __builtin__.object\n", " | \n", " | Methods inherited from PlotlyDict:\n", " | \n", " | __init__(self, *args, **kwargs)\n", " | \n", " | force_clean(self, caller=True)\n", " | Attempts to convert to graph_objs and call force_clean() on values.\n", " | \n", " | Calling force_clean() on a PlotlyDict will ensure that the object is\n", " | valid and may be sent to plotly. This process will also remove any\n", " | entries that end up with a length == 0.\n", " | \n", " | Careful! This will delete any invalid entries *silently*.\n", " | \n", " | get_data(self)\n", " | Returns the JSON for the plot with non-data elements stripped.\n", " | \n", " | get_ordered(self, caller=True)\n", " | \n", " | strip_style(self)\n", " | Strip style from the current representation.\n", " | \n", " | All PlotlyDicts and PlotlyLists are guaranteed to survive the\n", " | stripping process, though they made be left empty. This is allowable.\n", " | \n", " | Keys that will be stripped in this process are tagged with\n", " | `'type': 'style'` in the INFO dictionary listed in graph_objs_meta.py.\n", " | \n", " | This process first attempts to convert nested collections from dicts\n", " | or lists to subclasses of PlotlyList/PlotlyDict. This process forces\n", " | a validation, which may throw exceptions.\n", " | \n", " | Then, each of these objects call `strip_style` on themselves and so\n", " | on, recursively until the entire structure has been validated and\n", " | stripped.\n", " | \n", " | to_graph_objs(self, caller=True)\n", " | Walk obj, convert dicts and lists to plotly graph objs.\n", " | \n", " | For each key in the object, if it corresponds to a special key that\n", " | should be associated with a graph object, the ordinary dict or list\n", " | will be reinitialized as a special PlotlyDict or PlotlyList of the\n", " | appropriate `kind`.\n", " | \n", " | to_string(self, level=0, indent=4, eol='\\n', pretty=True, max_chars=80)\n", " | Returns a formatted string showing graph_obj constructors.\n", " | \n", " | Example:\n", " | \n", " | print obj.to_string()\n", " | \n", " | Keyword arguments:\n", " | level (default = 0) -- set number of indentations to start with\n", " | indent (default = 4) -- set indentation amount\n", " | eol (default = '\n", " | ') -- set end of line character(s)\n", " | pretty (default = True) -- curtail long list output with a '...'\n", " | max_chars (default = 80) -- set max characters per line\n", " | \n", " | update(self, dict1=None, **dict2)\n", " | Update current dict with dict1 and then dict2.\n", " | \n", " | This recursively updates the structure of the original dictionary-like\n", " | object with the new entries in the second and third objects. This\n", " | allows users to update with large, nested structures.\n", " | \n", " | Note, because the dict2 packs up all the keyword arguments, you can\n", " | specify the changes as a list of keyword agruments.\n", " | \n", " | Examples:\n", " | # update with dict\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj.update(update_dict)\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | # update with list of keyword arguments\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | obj.update(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | This 'fully' supports duck-typing in that the call signature is\n", " | identical, however this differs slightly from the normal update\n", " | method provided by Python's dictionaries.\n", " | \n", " | validate(self, caller=True)\n", " | Recursively check the validity of the keys in a PlotlyDict.\n", " | \n", " | The valid keys constitute the entries in each object\n", " | dictionary in INFO stored in graph_objs_meta.py.\n", " | \n", " | The validation process first requires that all nested collections be\n", " | converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,\n", " | each of these objects call `validate` and so on, recursively,\n", " | until the entire object has been validated.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from PlotlyDict:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from PlotlyDict:\n", " | \n", " | __metaclass__ = \n", " | A meta class for PlotlyDict class creation.\n", " | \n", " | The sole purpose of this meta class is to properly create the __doc__\n", " | attribute so that running help(Obj), where Obj is a subclass of PlotlyDict,\n", " | will return information about key-value pairs for that object.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from __builtin__.dict:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __getitem__(...)\n", " | x.__getitem__(y) <==> x[y]\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __setitem__(...)\n", " | x.__setitem__(i, y) <==> x[i]=y\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from __builtin__.dict:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", "\n" ] } ], "prompt_number": 43 }, { "cell_type": "markdown", "metadata": {}, "source": [ "To enable custom contours (i.e. by setting `'start'`, `'end'` and `'size'` in `Contours`) set `autocontour` to `False` in `Contour`. \n", "\n", "By default, Plotly contours are colored uniformly between contour lines, corresponding to `'coloring':'fill'` in `Contours`. With the `'heatmap'` value coloring is done on a grid point-by-grid point basis and with `lines` only the contour lines are colored with respect to the color scale. Finally, with `'coloring:'none'` contour lines and the areas between them are not colored, use this value to make a contour plot with all contour lines of the same color.\n", "\n", "To modify the appearance of the contour lines (e.g. their color), use the `Line` object: " ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import Line\n", "from plotly.graph_objs import Line\n", "\n", "help(Line)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on class Line in module plotly.graph_objs.graph_objs:\n", "\n", "class Line(PlotlyDict)\n", " | A dictionary-like object representing the style of a line in plotly.\n", " | \n", " | \n", " | Quick method reference:\n", " | \n", " | Line.update(changes)\n", " | Line.strip_style()\n", " | Line.get_data()\n", " | Line.to_graph_objs()\n", " | Line.validate()\n", " | Line.to_string()\n", " | Line.force_clean()\n", " | \n", " | Valid keys:\n", " | \n", " | dash [required=Aw, snap! Undocumented!] (value='dash' | 'dashdot' |\n", " | 'dot' | 'solid'):\n", " | The style of the line.\n", " | \n", " | color [required=False] (value=str describing color):\n", " | The color of the line.\n", " | \n", " | width [required=False] (value=number: x >= 0):\n", " | The width of the line.\n", " | \n", " | opacity [required=False] (value=number: x in [0, 1]):\n", " | Sets the opacity, or transparency, of this object. Also known as the\n", " | alpha channel of colors, if the object's color is given in terms of\n", " | 'rgba', this does not need to be defined.\n", " | \n", " | smoothing [required=Aw, snap! Undocumented!] (value=Aw, snap!\n", " | Undocumented!):\n", " | Only applies to contours\n", " | \n", " | Method resolution order:\n", " | Line\n", " | PlotlyDict\n", " | __builtin__.dict\n", " | __builtin__.object\n", " | \n", " | Methods inherited from PlotlyDict:\n", " | \n", " | __init__(self, *args, **kwargs)\n", " | \n", " | force_clean(self, caller=True)\n", " | Attempts to convert to graph_objs and call force_clean() on values.\n", " | \n", " | Calling force_clean() on a PlotlyDict will ensure that the object is\n", " | valid and may be sent to plotly. This process will also remove any\n", " | entries that end up with a length == 0.\n", " | \n", " | Careful! This will delete any invalid entries *silently*.\n", " | \n", " | get_data(self)\n", " | Returns the JSON for the plot with non-data elements stripped.\n", " | \n", " | get_ordered(self, caller=True)\n", " | \n", " | strip_style(self)\n", " | Strip style from the current representation.\n", " | \n", " | All PlotlyDicts and PlotlyLists are guaranteed to survive the\n", " | stripping process, though they made be left empty. This is allowable.\n", " | \n", " | Keys that will be stripped in this process are tagged with\n", " | `'type': 'style'` in the INFO dictionary listed in graph_objs_meta.py.\n", " | \n", " | This process first attempts to convert nested collections from dicts\n", " | or lists to subclasses of PlotlyList/PlotlyDict. This process forces\n", " | a validation, which may throw exceptions.\n", " | \n", " | Then, each of these objects call `strip_style` on themselves and so\n", " | on, recursively until the entire structure has been validated and\n", " | stripped.\n", " | \n", " | to_graph_objs(self, caller=True)\n", " | Walk obj, convert dicts and lists to plotly graph objs.\n", " | \n", " | For each key in the object, if it corresponds to a special key that\n", " | should be associated with a graph object, the ordinary dict or list\n", " | will be reinitialized as a special PlotlyDict or PlotlyList of the\n", " | appropriate `kind`.\n", " | \n", " | to_string(self, level=0, indent=4, eol='\\n', pretty=True, max_chars=80)\n", " | Returns a formatted string showing graph_obj constructors.\n", " | \n", " | Example:\n", " | \n", " | print obj.to_string()\n", " | \n", " | Keyword arguments:\n", " | level (default = 0) -- set number of indentations to start with\n", " | indent (default = 4) -- set indentation amount\n", " | eol (default = '\n", " | ') -- set end of line character(s)\n", " | pretty (default = True) -- curtail long list output with a '...'\n", " | max_chars (default = 80) -- set max characters per line\n", " | \n", " | update(self, dict1=None, **dict2)\n", " | Update current dict with dict1 and then dict2.\n", " | \n", " | This recursively updates the structure of the original dictionary-like\n", " | object with the new entries in the second and third objects. This\n", " | allows users to update with large, nested structures.\n", " | \n", " | Note, because the dict2 packs up all the keyword arguments, you can\n", " | specify the changes as a list of keyword agruments.\n", " | \n", " | Examples:\n", " | # update with dict\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj.update(update_dict)\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | # update with list of keyword arguments\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | obj.update(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | This 'fully' supports duck-typing in that the call signature is\n", " | identical, however this differs slightly from the normal update\n", " | method provided by Python's dictionaries.\n", " | \n", " | validate(self, caller=True)\n", " | Recursively check the validity of the keys in a PlotlyDict.\n", " | \n", " | The valid keys constitute the entries in each object\n", " | dictionary in INFO stored in graph_objs_meta.py.\n", " | \n", " | The validation process first requires that all nested collections be\n", " | converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,\n", " | each of these objects call `validate` and so on, recursively,\n", " | until the entire object has been validated.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from PlotlyDict:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from PlotlyDict:\n", " | \n", " | __metaclass__ = \n", " | A meta class for PlotlyDict class creation.\n", " | \n", " | The sole purpose of this meta class is to properly create the __doc__\n", " | attribute so that running help(Obj), where Obj is a subclass of PlotlyDict,\n", " | will return information about key-value pairs for that object.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from __builtin__.dict:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __getitem__(...)\n", " | x.__getitem__(y) <==> x[y]\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __setitem__(...)\n", " | x.__setitem__(i, y) <==> x[i]=y\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from __builtin__.dict:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", "\n" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Contour lines are smoothed by default, corresponding to `'smoothing': 1` in `Line`. Set `'smoothing'` to a value larger than 1 to smooth the contours more and to a value between 0 and 1 to for less smoothing.\n", "\n", "Now, fill in an instance of the contour object with our data:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_cbartitle = \"Daily cyclist count per site\"\n", "\n", "# Make instance of the colorbar object\n", "my_colorbar = ColorBar(title=my_cbartitle, # colorbar title\n", " titleside='right', # colorbar title at right of colorbar\n", " thickness=30, # colorbar thickness in px\n", " len=0.8, # colorbar length in plot fraction\n", " ticks='outside') # tick outside colorbar\n", "\n", "# Make instance of the data object\n", "my_data = Data([Contour(x=sites_in_order, # sites on the x-axis\n", " y=dates_in_words, # dates on the y-axis\n", " z=Z_dayXsite_in_order, # 2D array a f(sites,dates)\n", " scl='Greens', # choose a color scl from the pre-defined\n", " reversescl=True, # and reversed its order\n", " line= Line(smoothing=1.5, # N.B. default is 1\n", " color='#999999', # default is black\n", " width=1), # default is 0.5 \n", " colorbar=my_colorbar # link colorbar object\n", " )])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 45 }, { "cell_type": "markdown", "metadata": {}, "source": [ "On to the layout," ] }, { "cell_type": "code", "collapsed": false, "input": [ "plot_width = 650 # plot width in px\n", "plot_height = 1000 # plot height in px\n", "\n", "my_title = \"Fig 5.3: Montral bike traffic daily progression
\\\n", "from the most trafficked site to the least in 2013
\"\n", "\n", "my_xtitle = 'Sites [from the most trafficked to the least in 2013]'\n", "\n", "my_layout = Layout(title=my_title, # plot's title\n", " font= Font(family='Raleway, sans-serif', # global font \n", " color='#635F5D'),\n", " xaxis = XAxis(title=my_xtitle), # x-axis title\n", " yaxis= YAxis(autorange='reversed', # N.B. start y-axis at top\n", " autotick=False, # custom tick\n", " dtick=7), # 1 tick per week\n", " autosize=False, # custom size\n", " height=plot_height, \n", " width=plot_width,\n", " margin= Margin(b=120)) # increase bottom margin" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 46 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get a Plotly plot:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Package data and layout into a figure object, send it to Plotly\n", "my_fig = Figure(data=my_data, layout=my_layout)\n", "py.iplot(my_fig, filename='s5_cyclist-time-progression',\n", " width=plot_width, height=plot_height) # set notebook output frame size" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 47 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Traffic progressions are noticeable in April, but overall all counter sites got busier at the same time.\n", "\n", "The above contour plot contains information about all counter totals between April and September for all 8 sites. Hovering over the contours reveals each daily cyclist count, which on a static figure would be impossible. This is just another example of how Plotly makes graphing come alive." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "5.4 Joint daily traffic probability distribution for 2 sites" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our upcoming plot will evaluate the probability distribution of daily cyclist counts at two different sites simultaneously using 2-dimensional histograms. To make things insteresting we choose the two least correlated counter sites.\n", "\n", "Roughly speaking, 2-dimensional histograms present the likelihood of two event happening of the same time. In our case, the two events are *site 1* getting *x* cyclists in a day and *site 2* getting *y* cyclists during the same day.\n", "\n", "As for 1-dimensional histograms (more in section 4), Plotly does the computations for you. All you need to input are the two samples corresponding to the variables of interest (e.g. the daily cyclist count at each site)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import Histogram2d\n", "from plotly.graph_objs import Histogram2d\n", "\n", "help(Histogram2d)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on class Histogram2d in module plotly.graph_objs.graph_objs:\n", "\n", "class Histogram2d(PlotlyTrace)\n", " | A dictionary-like object for representing a histogram2d plot in plotly.\n", " | \n", " | Example:\n", " | \n", " | import numpy as np\n", " | x = np.random.randn(500)\n", " | y = np.random.randn(500)+1\n", " | py.iplot([Histogram2d(x=x, y=y)])\n", " | \n", " | \n", " | Quick method reference:\n", " | \n", " | Histogram2d.update(changes)\n", " | Histogram2d.strip_style()\n", " | Histogram2d.get_data()\n", " | Histogram2d.to_graph_objs()\n", " | Histogram2d.validate()\n", " | Histogram2d.to_string()\n", " | Histogram2d.force_clean()\n", " | \n", " | Valid keys:\n", " | \n", " | x [required=True] (value=array-like of numbers, strings, datetimes):\n", " | The x data that is binned and plotted as bars along the x-axis.\n", " | \n", " | y [required=True] (value=array-like of numbers, strings, datetimes):\n", " | The y data that is binned and plotted as bars along the y-axis.\n", " | \n", " | name [required=False] (value=string):\n", " | The label associated with this trace. This name will appear in the\n", " | legend, in the column header in the spreadsheet, and on hover.\n", " | \n", " | scl [required=False] (value=array_like of value-color pairs | 'Greys' |\n", " | 'Greens' | 'Bluered' | 'Hot' | 'Picnic' | 'Portland' | 'Jet' | 'RdBu' |\n", " | 'Blackbody' | 'Earth' | 'Electric' | 'YIOrRd' | 'YIGnBu'):\n", " | The color scale. The strings are pre-defined color scales. For\n", " | custom color scales, define a list of color-value pairs, where the\n", " | first element of the pair corresponds to a normalized value of z\n", " | from 0-1 (i.e. (z-zmin)/(zmax-zmin)), and the second element of\n", " | pair corresponds to a color.\n", " | \n", " | Examples:\n", " | ['Greys', [[0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)']], [[0,\n", " | 'rgb(8, 29, 88)'], [0.125, 'rgb(37, 52, 148)'], [0.25, 'rgb(34,\n", " | 94, 168)'], [0.375, 'rgb(29, 145, 192)'], [0.5, 'rgb(65, 182,\n", " | 196)'], [0.625, 'rgb(127, 205, 187)'], [0.75, 'rgb(199, 233,\n", " | 180)'], [0.875, 'rgb(237, 248, 217)'], [1, 'rgb(255, 255,\n", " | 217)']]]\n", " | \n", " | reversescl [required=False] (value=bool: True | False):\n", " | Aw, snap! Undocumented!\n", " | \n", " | showscale [required=False] (value=bool: True | False):\n", " | Toggle whether or not the color scale associated with this mapping\n", " | will be shown alongside the rendered figure.\n", " | \n", " | colorbar [required=False] (value=ColorBar object | dictionary-like):\n", " | This object represents a color bar that will be shown on the figure\n", " | where the color is related to the data being shown.\n", " | \n", " | For more, run `help(plotly.graph_objs.ColorBar)`\n", " | \n", " | zauto [required=False] (value=bool: True | False):\n", " | Set to False to overwrite the default values of 'zmax' and 'zmax'.\n", " | \n", " | zmin [required=False] (value=number):\n", " | The value used as the minimum in the color scale normalization in\n", " | 'scl'. The default is the minimum of the 'z' data values.\n", " | \n", " | zmax [required=False] (value=number):\n", " | The value used as the maximum in the color scale normalization in\n", " | 'scl'. The default is the minimum of the 'z' data values.\n", " | \n", " | zsmooth [required=False] (value= False | 'best' | 'fast' ):\n", " | Choose between algorithms ('best' or 'fast') to smooth data linked\n", " | to 'z'.\n", " | \n", " | autobinx [required=False] (value=bool: True | False):\n", " | Toggle whether or not to allow plotly to automatically pick the bin\n", " | sizing in the x direction for this histogram.\n", " | \n", " | autobiny [required=False] (value=bool: True | False):\n", " | Toggle whether or not to allow plotly to automatically pick the bin\n", " | sizing in the y direction for this histogram.\n", " | \n", " | xbins [required=False] (value=XBins object | dictionary-like):\n", " | A dictionary-like object explaining how the bins should be created\n", " | in the x direction for this histogram.\n", " | \n", " | For more, run `help(plotly.graph_objs.XBins)`\n", " | \n", " | ybins [required=False] (value=YBins object | dictionary-like):\n", " | A dictionary-like object explaining how the bins should be created\n", " | in the y direction for this histogram.\n", " | \n", " | For more, run `help(plotly.graph_objs.YBins)`\n", " | \n", " | nbinsx [required=False] (value=number: x > 0):\n", " | Specifies the number of bins in the x-direction.\n", " | \n", " | nbinsy [required=False] (value=number: x > 0):\n", " | Specifies the number of bins in the y-direction.\n", " | \n", " | histnorm [required=Aw, snap! Undocumented!] (value='' | 'percent' |\n", " | 'probability' | 'density' | 'probability density'):\n", " | If histnorm is not specified, or histnorm='' (empty string), the\n", " | height of each bar displays the frequency of occurrence, i.e., the\n", " | number of times this value was found in the corresponding bin. If\n", " | histnorm='percent', the height of each bar displays the percentage\n", " | of total occurrences found within the corresponding bin. If\n", " | histnorm='probability', the height of each bar displays the\n", " | probability that an event will fall into the corresponding bin. If\n", " | histnorm='density', the height of each bar is equal to the number of\n", " | occurrences in a bin divided by the size of the bin interval such\n", " | that summing the area of all bins will yield the total number of\n", " | occurrences. If histnorm='probability density', the height of each\n", " | bar is equal to the number of probability that an event will fall\n", " | into the corresponding bin divided by the size of the bin interval\n", " | such that summing the area of all bins will yield 1, i.e. an event\n", " | must fall into one of the bins.\n", " | \n", " | opacity [required=False] (value=number: x in [0, 1]):\n", " | Sets the opacity, or transparency, of this object. Also known as the\n", " | alpha channel of colors, if the object's color is given in terms of\n", " | 'rgba', this does not need to be defined.\n", " | \n", " | showlegend [required=False] (value=bool: True | False):\n", " | Toggle whether or not this trace will show up in the legend.\n", " | \n", " | xaxis [required=False] (value=string: 'x1' | 'x2' | 'x3' | etc.):\n", " | This key determines which xaxis the x coordinates in this trace will\n", " | reference in the figure. 'x' references layout['xaxis'] and 'x2'\n", " | references layout['xaxis2']. 'x1' will always refer to\n", " | layout['xaxis'] or layout['xaxis1'], they are the same.\n", " | \n", " | yaxis [required=False] (value=string: 'y1' | 'y2' | 'y3' | etc.):\n", " | This key determines which yaxis the y coordinates in this trace will\n", " | reference in the figure. 'y' references layout['yaxis'] and 'y2'\n", " | references layout['yaxis2']. 'y1' will always refer to\n", " | layout['yaxis'] or layout['yaxis1'], they are the same.\n", " | \n", " | visible [required=False] (value=bool: True | False):\n", " | Toggles whether this will actually be visible in the rendered\n", " | figure.\n", " | \n", " | stream [required=False] (value=dictionary-like):\n", " | The stream dict that initializes traces as writable-streams, for use\n", " | with the real-time streaming API. Learn more here:\n", " | https://plot.ly/python/streaming/\n", " | \n", " | marker [required=False] (value=Marker object | dictionary-like):\n", " | Aw, snap! Undocumented!\n", " | \n", " | For more, run `help(plotly.graph_objs.Marker)`\n", " | \n", " | line [required=False] (value=Line object | dictionary-like):\n", " | Aw, snap! Undocumented!\n", " | \n", " | For more, run `help(plotly.graph_objs.Line)`\n", " | \n", " | type [required=True] (value=histogram2d):\n", " | Plotly identifier for this data's trace type. This defines how this\n", " | data dictionary will be handled. For example, 'scatter' type expects\n", " | x and y data-arrays corresponding to (x, y) coordinates wheras a\n", " | 'histogram' only requires a single x or y array and a 'heatmap' type\n", " | requires a z matrix.\n", " | \n", " | Method resolution order:\n", " | Histogram2d\n", " | PlotlyTrace\n", " | PlotlyDict\n", " | __builtin__.dict\n", " | __builtin__.object\n", " | \n", " | Methods inherited from PlotlyTrace:\n", " | \n", " | __init__(self, *args, **kwargs)\n", " | \n", " | to_string(self, level=0, indent=4, eol='\\n', pretty=True, max_chars=80)\n", " | Returns a formatted string showing graph_obj constructors.\n", " | \n", " | Example:\n", " | \n", " | print obj.to_string()\n", " | \n", " | Keyword arguments:\n", " | level (default = 0) -- set number of indentations to start with\n", " | indent (default = 4) -- set indentation amount\n", " | eol (default = '\n", " | ') -- set end of line character(s)\n", " | pretty (default = True) -- curtail long list output with a '...'\n", " | max_chars (default = 80) -- set max characters per line\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from PlotlyDict:\n", " | \n", " | force_clean(self, caller=True)\n", " | Attempts to convert to graph_objs and call force_clean() on values.\n", " | \n", " | Calling force_clean() on a PlotlyDict will ensure that the object is\n", " | valid and may be sent to plotly. This process will also remove any\n", " | entries that end up with a length == 0.\n", " | \n", " | Careful! This will delete any invalid entries *silently*.\n", " | \n", " | get_data(self)\n", " | Returns the JSON for the plot with non-data elements stripped.\n", " | \n", " | get_ordered(self, caller=True)\n", " | \n", " | strip_style(self)\n", " | Strip style from the current representation.\n", " | \n", " | All PlotlyDicts and PlotlyLists are guaranteed to survive the\n", " | stripping process, though they made be left empty. This is allowable.\n", " | \n", " | Keys that will be stripped in this process are tagged with\n", " | `'type': 'style'` in the INFO dictionary listed in graph_objs_meta.py.\n", " | \n", " | This process first attempts to convert nested collections from dicts\n", " | or lists to subclasses of PlotlyList/PlotlyDict. This process forces\n", " | a validation, which may throw exceptions.\n", " | \n", " | Then, each of these objects call `strip_style` on themselves and so\n", " | on, recursively until the entire structure has been validated and\n", " | stripped.\n", " | \n", " | to_graph_objs(self, caller=True)\n", " | Walk obj, convert dicts and lists to plotly graph objs.\n", " | \n", " | For each key in the object, if it corresponds to a special key that\n", " | should be associated with a graph object, the ordinary dict or list\n", " | will be reinitialized as a special PlotlyDict or PlotlyList of the\n", " | appropriate `kind`.\n", " | \n", " | update(self, dict1=None, **dict2)\n", " | Update current dict with dict1 and then dict2.\n", " | \n", " | This recursively updates the structure of the original dictionary-like\n", " | object with the new entries in the second and third objects. This\n", " | allows users to update with large, nested structures.\n", " | \n", " | Note, because the dict2 packs up all the keyword arguments, you can\n", " | specify the changes as a list of keyword agruments.\n", " | \n", " | Examples:\n", " | # update with dict\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj.update(update_dict)\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | # update with list of keyword arguments\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | obj.update(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | This 'fully' supports duck-typing in that the call signature is\n", " | identical, however this differs slightly from the normal update\n", " | method provided by Python's dictionaries.\n", " | \n", " | validate(self, caller=True)\n", " | Recursively check the validity of the keys in a PlotlyDict.\n", " | \n", " | The valid keys constitute the entries in each object\n", " | dictionary in INFO stored in graph_objs_meta.py.\n", " | \n", " | The validation process first requires that all nested collections be\n", " | converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,\n", " | each of these objects call `validate` and so on, recursively,\n", " | until the entire object has been validated.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from PlotlyDict:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from PlotlyDict:\n", " | \n", " | __metaclass__ = \n", " | A meta class for PlotlyDict class creation.\n", " | \n", " | The sole purpose of this meta class is to properly create the __doc__\n", " | attribute so that running help(Obj), where Obj is a subclass of PlotlyDict,\n", " | will return information about key-value pairs for that object.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from __builtin__.dict:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __getitem__(...)\n", " | x.__getitem__(y) <==> x[y]\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __setitem__(...)\n", " | x.__setitem__(i, y) <==> x[i]=y\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from __builtin__.dict:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", "\n" ] } ], "prompt_number": 48 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The style keys in `Histogram2d` are the same as in `Heatmap`.\n", "\n", "First, find the two least correlated sites using the correlation matrix `Corr_site` computed in [subsection 5.2](#5.2-Correlation-heatmap-between-sites):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Get 2D index (as a tuple) of the least correlated sites \n", "ind_least_corr = np.unravel_index(Corr_site.argmin(), Corr_site.shape)\n", "\n", "# Get 1st site, remove
tag from string\n", "site1 = sites[ind_least_corr[0]].replace('
','')\n", "z_day1 = Z_dayXsite[:,ind_least_corr[0]]\n", "\n", "# And similarly for the 2nd site, remove
tag from string\n", "site2 = sites[ind_least_corr[1]].replace('
',' ')\n", "z_day2 = Z_dayXsite[:,ind_least_corr[1]]\n", "\n", "site1, site2 # the 2 least-correlated sites" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 49, "text": [ "('du Parc/Duluth', 'Pierre-Dupuy (Habitat 67)')" ] } ], "prompt_number": 49 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our plot will feature custom axes ranges and bin sizes. \n", "\n", "So, let's get an idea of the range of each sample:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "z_day1.max(), z_day2.max()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 50, "text": [ "(4103, 4841)" ] } ], "prompt_number": 50 }, { "cell_type": "markdown", "metadata": {}, "source": [ "For presentation purposes, the axes' and bins' upper limits will be rounded up to 5000.\n", "\n", "Moreover, we will smooth the counters using `'zsmooth` key in `Histogram2d` and overlay scatter points of both two sites' daily cyclist counts, plotted against each other. Each scatter point will be label by the date using `dates_in_words` list defined in [subsection 5.3](#5.3-Contours-of-traffic-progression-from-most-to-least-trafficked-sites). " ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import a few more graph objects \n", "from plotly.graph_objs import Scatter, Marker, XBins, YBins" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 51 }, { "cell_type": "code", "collapsed": false, "input": [ "# Select axis and bin range and bin size\n", "my_range = [0,5000]\n", "my_bin_size = 500\n", "my_bins = dict(start=my_range[0], # store them in dictionary\n", " end=my_range[1], # to be places in XBins and YBins instances\n", " size=my_bin_size) \n", "\n", "my_cbar_title='Number of occurences from Apr 1 to Aug 31' # colorbar title\n", "\n", "# Make instance of Histogram2d object\n", "# with no histogram normalization (the default)\n", "my_hist2d = Histogram2d(x=z_day1, # sample of the x-axis\n", " y=z_day2, # sample of the y-axis\n", " xbins= XBins(my_bins), # custom x-axis bins\n", " ybins= YBins(my_bins), # custom y-axis bins\n", " zsmooth='best', # N.B. apply smoothing to contours\n", " scl='Portland', # N.B. choose a pre-defined color scale\n", " colorbar= ColorBar(title=my_cbar_title,# colorbar title\n", " titleside='right', # title right of colorbar\n", " ticks='outside')) # ticks outside colorbar\n", "\n", "# Make instance of Scatter\n", "my_scatter = Scatter(x=z_day1, # x coordinates\n", " y=z_day2, # y coordinates\n", " mode='markers', # just marker pts\n", " name='', # no name\n", " text=dates_in_words, # text label corresp. to date\n", " marker= Marker(size=5, # marker size\n", " color='#e0e0e0')) # and color\n", "\n", "# Package in Data, plot scatter on top of 2d histogram\n", "my_data = Data([my_hist2d, my_scatter])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 52 }, { "cell_type": "markdown", "metadata": {}, "source": [ "On to the latyout:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Define figure and axis titles\n", "my_title = 'Fig 5.4a: Joint frequency distribution
\\\n", "of daily cyclist counts at two Montreal sites in 2013'\n", "my_xtitle = 'Daily cyclist count at {}'.format(site1)\n", "my_ytitle = 'Daily cyclist count at {}'.format(site2)\n", "\n", "# Make instance of Layout\n", "my_layout = Layout(title=my_title, # figure title\n", " font= Font(family='PT Sans Narrow, sans-serif', # global font\n", " size=13),\n", " xaxis= XAxis(title=my_xtitle, # x-axis title\n", " range=my_range, # x-axis range\n", " zeroline=False), # remove x=0 line\n", " yaxis= YAxis(title=my_ytitle, # y-axis title\n", " range=my_range, # y-axis range\n", " zeroline=False), # remove y=0 line\n", " showlegend=False, # remove legend\n", " autosize=False, # custom size\n", " width=650, # figure width \n", " height=520) # and height\n", "\n", "# Add an annotation citing the data source\n", "my_anno_text = \"Source and info: \\\n", "Donn\u00e9es ouvertes de la Ville de Montr\u00e9al\"\n", "my_layout.update(annotations=[make_anno(my_anno_text,1,1.055)])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 53 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, package data and layout object into an instance of `Figure` and send it to Plotly:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make instance of figure object\n", "my_fig = Figure(data=my_data, layout=my_layout)\n", "\n", "# Define filename in relation to the sites chosen, call Plotly\n", "my_filename = 's5_hist2d-sites-{}-{}'.format(ind_least_corr[0],ind_least_corr[1])\n", "py.iplot(my_fig, filename=my_filename)" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 54 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that there are a few hotspots of probability. By hovering over the scatter points, we notice that the Pierre-Dupuy site gets most of its traffic during weekends and holidays." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we add two subplots to the above figure showing 1D histograms of daily cyclist counts for each of the two counter site (i.e. their marginal distibutions).\n", "\n", "From our current instance of the figure object, we first adjust the domains of the axes in place and add one x-axis and one y-axis:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import Histogram\n", "from plotly.graph_objs import Histogram" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 55 }, { "cell_type": "code", "collapsed": false, "input": [ "# Adjust the existing axes\n", "my_layout['xaxis'].update(domain=[0, 0.7]) # decrease domain of x-axis1 (norm. coord.)\n", "my_layout['yaxis'].update(domain=[0, 0.7], # decrease domain of y-axis1\n", " showgrid=False) # remove vertical grid lines\n", "\n", "# Set up new axes\n", "my_layout.update(xaxis2= XAxis(domain=[0.75, 1], # domain of x-axis2\n", " zeroline=False, # remove x=0 line\n", " showgrid=True)) # show horizontal grid line\n", "my_layout.update(yaxis2= YAxis(domain=[0.75, 1], # domain of y-axis2\n", " zeroline=False, # remove y=0 line\n", " showgrid=True)) # show vertical line\n", "\n", "# Change location of the annotation citing the data source\n", "my_anno_text = \"Source and info:
\\\n", "Donn\u00e9es ouvertes
de la Ville de Montr\u00e9al\"\n", "my_layout['annotations'][0].update(make_anno(my_anno_text,0.98,0.95))" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 56 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, make two instances of `Histogram` (N.B. the 1D version) and append to the current instance of the data object:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make instance of Histogram, with vertical bins and no normalization\n", "my_histx = Histogram(x=z_day1, # x sample, bind bins to x-axis\n", " name='', # no name on hover\n", " xbins= XBins(my_bins), # use same bins as in the 2D histogram \n", " marker= Marker(color=\"rgb(242,211,56)\"), # choose bin color\n", " xaxis='x1', # bind x coords to xaxis1\n", " yaxis='y2') # bind y coords to yaxis2\n", "\n", "# Make instance of Histogram, with horizontal bins and no normalization\n", "my_histy = Histogram(y=z_day2, # y sample, bind bins to y-axis\n", " name='', # no name on hover\n", " ybins= YBins(my_bins), # use same bins as in the 2D histogram\n", " marker= Marker(color=\"rgb(242,211,56)\"), # choose bin color\n", " xaxis='x2', # bind x coords to xaxis2\n", " yaxis='y1') # bind y coords to yaxis2\n", "\n", "# Append data object\n", "my_data += [my_histx, my_histy]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 57 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tweak the colorbar's appearance, update the figure's titls and call Plotly:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Updata colorbar position, length and thickness\n", "my_data[0]['colorbar'].update(x=0.99,y=0.35,len=0.7,thickness=20)\n", " \n", "# Update title\n", "my_title = 'Fig 5.4b: Joint frequency distribution
\\\n", "of daily cyclist counts at two Montreal sites in 2013'\n", "my_layout.update(title=my_title)\n", "\n", "# Mkae new instance of figure object, send it to Plotly\n", "my_fig = Figure(data=my_data, layout=my_layout)\n", "my_filename = 's5_hist2dsplt-sites-{}-{}'.format(ind_least_corr[0],ind_least_corr[1])\n", "py.iplot(my_fig, filename=my_filename)" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 58 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The axes are shared (try zooming on the 2D histogram). It is interesting to see that the distribution of the Pierre Dupuy site is much more skewed than Parc/Duluth. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plotly allows users to plot 2D histograms of similar in appearance to ones generated by the `Contour` graph object using `Histogram2dContour`," ] }, { "cell_type": "code", "collapsed": false, "input": [ "# (*) Import Histogram2dContour\n", "from plotly.graph_objs import Histogram2dContour\n", "\n", "help(Histogram2dContour)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on class Histogram2dContour in module plotly.graph_objs.graph_objs:\n", "\n", "class Histogram2dContour(PlotlyTrace)\n", " | A dict-like object for representing a histogram2d-contour plot in plotly.\n", " | \n", " | Example:\n", " | \n", " | import numpy as np\n", " | x = np.random.randn(500)\n", " | y = np.random.randn(500)+1\n", " | py.iplot([Histogram2dcountour(x=x, y=y)])\n", " | \n", " | \n", " | Quick method reference:\n", " | \n", " | Histogram2dContour.update(changes)\n", " | Histogram2dContour.strip_style()\n", " | Histogram2dContour.get_data()\n", " | Histogram2dContour.to_graph_objs()\n", " | Histogram2dContour.validate()\n", " | Histogram2dContour.to_string()\n", " | Histogram2dContour.force_clean()\n", " | \n", " | Valid keys:\n", " | \n", " | x [required=True] (value=array-like of numbers, strings, datetimes):\n", " | The x data that is binned and plotted as bars along the x-axis.\n", " | \n", " | y [required=True] (value=array-like of numbers, strings, datetimes):\n", " | The y data that is binned and plotted as bars along the y-axis.\n", " | \n", " | name [required=False] (value=string):\n", " | The label associated with this trace. This name will appear in the\n", " | legend, in the column header in the spreadsheet, and on hover.\n", " | \n", " | autocontour [required=False] (value=bool: True | False):\n", " | If True, the contours settings are set automatically. If False, the\n", " | contours settings must be set manually with the contours object.\n", " | \n", " | ncontours [required=False] (value=bool: True | False):\n", " | Speficy the number of countours lines that will appear.\n", " | \n", " | contours [required=False] (value=Contours object | dictionary-like):\n", " | A dictionary-like object defining parameters of the contours in this\n", " | plot like spacing, whether or not to show lines, etc.\n", " | \n", " | For more, run `help(plotly.graph_objs.Contours)`\n", " | \n", " | line [required=False] (value=Line object | dictionary-like):\n", " | Set contour line style (e.g. dash) and width. If showlines=True in\n", " | Contour, select line color. Select smoothing level.\n", " | \n", " | For more, run `help(plotly.graph_objs.Line)`\n", " | \n", " | scl [required=False] (value=array_like of value-color pairs | 'Greys' |\n", " | 'Greens' | 'Bluered' | 'Hot' | 'Picnic' | 'Portland' | 'Jet' | 'RdBu' |\n", " | 'Blackbody' | 'Earth' | 'Electric' | 'YIOrRd' | 'YIGnBu'):\n", " | The color scale. The strings are pre-defined color scales. For\n", " | custom color scales, define a list of color-value pairs, where the\n", " | first element of the pair corresponds to a normalized value of z\n", " | from 0-1 (i.e. (z-zmin)/(zmax-zmin)), and the second element of\n", " | pair corresponds to a color.\n", " | \n", " | Examples:\n", " | ['Greys', [[0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)']], [[0,\n", " | 'rgb(8, 29, 88)'], [0.125, 'rgb(37, 52, 148)'], [0.25, 'rgb(34,\n", " | 94, 168)'], [0.375, 'rgb(29, 145, 192)'], [0.5, 'rgb(65, 182,\n", " | 196)'], [0.625, 'rgb(127, 205, 187)'], [0.75, 'rgb(199, 233,\n", " | 180)'], [0.875, 'rgb(237, 248, 217)'], [1, 'rgb(255, 255,\n", " | 217)']]]\n", " | \n", " | reversescl [required=False] (value=bool: True | False):\n", " | Aw, snap! Undocumented!\n", " | \n", " | showscale [required=False] (value=bool: True | False):\n", " | Toggle whether or not the color scale associated with this mapping\n", " | will be shown alongside the rendered figure.\n", " | \n", " | colorbar [required=False] (value=ColorBar object | dictionary-like):\n", " | This object represents a color bar that will be shown on the figure\n", " | where the color is related to the data being shown.\n", " | \n", " | For more, run `help(plotly.graph_objs.ColorBar)`\n", " | \n", " | zauto [required=False] (value=bool: True | False):\n", " | Set to False to overwrite the default values of 'zmax' and 'zmax'.\n", " | \n", " | zmin [required=False] (value=number):\n", " | The value used as the minimum in the color scale normalization in\n", " | 'scl'. The default is the minimum of the 'z' data values.\n", " | \n", " | zmax [required=False] (value=number):\n", " | The value used as the maximum in the color scale normalization in\n", " | 'scl'. The default is the minimum of the 'z' data values.\n", " | \n", " | autobinx [required=False] (value=bool: True | False):\n", " | Toggle whether or not to allow plotly to automatically pick the bin\n", " | sizing in the x direction for this histogram.\n", " | \n", " | autobiny [required=False] (value=bool: True | False):\n", " | Toggle whether or not to allow plotly to automatically pick the bin\n", " | sizing in the y direction for this histogram.\n", " | \n", " | xbins [required=False] (value=XBins object | dictionary-like):\n", " | A dictionary-like object explaining how the bins should be created\n", " | in the x direction for this histogram.\n", " | \n", " | For more, run `help(plotly.graph_objs.XBins)`\n", " | \n", " | ybins [required=False] (value=YBins object | dictionary-like):\n", " | A dictionary-like object explaining how the bins should be created\n", " | in the y direction for this histogram.\n", " | \n", " | For more, run `help(plotly.graph_objs.YBins)`\n", " | \n", " | nbinsx [required=False] (value=number: x > 0):\n", " | Specifies the number of bins in the x-direction.\n", " | \n", " | nbinsy [required=False] (value=number: x > 0):\n", " | Specifies the number of bins in the y-direction.\n", " | \n", " | histnorm [required=Aw, snap! Undocumented!] (value='' | 'percent' |\n", " | 'probability' | 'density' | 'probability density'):\n", " | If histnorm is not specified, or histnorm='' (empty string), the\n", " | height of each bar displays the frequency of occurrence, i.e., the\n", " | number of times this value was found in the corresponding bin. If\n", " | histnorm='percent', the height of each bar displays the percentage\n", " | of total occurrences found within the corresponding bin. If\n", " | histnorm='probability', the height of each bar displays the\n", " | probability that an event will fall into the corresponding bin. If\n", " | histnorm='density', the height of each bar is equal to the number of\n", " | occurrences in a bin divided by the size of the bin interval such\n", " | that summing the area of all bins will yield the total number of\n", " | occurrences. If histnorm='probability density', the height of each\n", " | bar is equal to the number of probability that an event will fall\n", " | into the corresponding bin divided by the size of the bin interval\n", " | such that summing the area of all bins will yield 1, i.e. an event\n", " | must fall into one of the bins.\n", " | \n", " | opacity [required=False] (value=number: x in [0, 1]):\n", " | Sets the opacity, or transparency, of this object. Also known as the\n", " | alpha channel of colors, if the object's color is given in terms of\n", " | 'rgba', this does not need to be defined.\n", " | \n", " | showlegend [required=False] (value=bool: True | False):\n", " | Toggle whether or not this trace will show up in the legend.\n", " | \n", " | xaxis [required=False] (value=string: 'x1' | 'x2' | 'x3' | etc.):\n", " | This key determines which xaxis the x coordinates in this trace will\n", " | reference in the figure. 'x' references layout['xaxis'] and 'x2'\n", " | references layout['xaxis2']. 'x1' will always refer to\n", " | layout['xaxis'] or layout['xaxis1'], they are the same.\n", " | \n", " | yaxis [required=False] (value=string: 'y1' | 'y2' | 'y3' | etc.):\n", " | This key determines which yaxis the y coordinates in this trace will\n", " | reference in the figure. 'y' references layout['yaxis'] and 'y2'\n", " | references layout['yaxis2']. 'y1' will always refer to\n", " | layout['yaxis'] or layout['yaxis1'], they are the same.\n", " | \n", " | visible [required=False] (value=bool: True | False):\n", " | Toggles whether this will actually be visible in the rendered\n", " | figure.\n", " | \n", " | stream [required=False] (value=dictionary-like):\n", " | The stream dict that initializes traces as writable-streams, for use\n", " | with the real-time streaming API. Learn more here:\n", " | https://plot.ly/python/streaming/\n", " | \n", " | marker [required=False] (value=Marker object | dictionary-like):\n", " | Aw, snap! Undocumented!\n", " | \n", " | For more, run `help(plotly.graph_objs.Marker)`\n", " | \n", " | type [required=True] (value=histogram2dcontour):\n", " | Plotly identifier for this data's trace type. This defines how this\n", " | data dictionary will be handled. For example, 'scatter' type expects\n", " | x and y data-arrays corresponding to (x, y) coordinates wheras a\n", " | 'histogram' only requires a single x or y array and a 'heatmap' type\n", " | requires a z matrix.\n", " | \n", " | Method resolution order:\n", " | Histogram2dContour\n", " | PlotlyTrace\n", " | PlotlyDict\n", " | __builtin__.dict\n", " | __builtin__.object\n", " | \n", " | Methods inherited from PlotlyTrace:\n", " | \n", " | __init__(self, *args, **kwargs)\n", " | \n", " | to_string(self, level=0, indent=4, eol='\\n', pretty=True, max_chars=80)\n", " | Returns a formatted string showing graph_obj constructors.\n", " | \n", " | Example:\n", " | \n", " | print obj.to_string()\n", " | \n", " | Keyword arguments:\n", " | level (default = 0) -- set number of indentations to start with\n", " | indent (default = 4) -- set indentation amount\n", " | eol (default = '\n", " | ') -- set end of line character(s)\n", " | pretty (default = True) -- curtail long list output with a '...'\n", " | max_chars (default = 80) -- set max characters per line\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from PlotlyDict:\n", " | \n", " | force_clean(self, caller=True)\n", " | Attempts to convert to graph_objs and call force_clean() on values.\n", " | \n", " | Calling force_clean() on a PlotlyDict will ensure that the object is\n", " | valid and may be sent to plotly. This process will also remove any\n", " | entries that end up with a length == 0.\n", " | \n", " | Careful! This will delete any invalid entries *silently*.\n", " | \n", " | get_data(self)\n", " | Returns the JSON for the plot with non-data elements stripped.\n", " | \n", " | get_ordered(self, caller=True)\n", " | \n", " | strip_style(self)\n", " | Strip style from the current representation.\n", " | \n", " | All PlotlyDicts and PlotlyLists are guaranteed to survive the\n", " | stripping process, though they made be left empty. This is allowable.\n", " | \n", " | Keys that will be stripped in this process are tagged with\n", " | `'type': 'style'` in the INFO dictionary listed in graph_objs_meta.py.\n", " | \n", " | This process first attempts to convert nested collections from dicts\n", " | or lists to subclasses of PlotlyList/PlotlyDict. This process forces\n", " | a validation, which may throw exceptions.\n", " | \n", " | Then, each of these objects call `strip_style` on themselves and so\n", " | on, recursively until the entire structure has been validated and\n", " | stripped.\n", " | \n", " | to_graph_objs(self, caller=True)\n", " | Walk obj, convert dicts and lists to plotly graph objs.\n", " | \n", " | For each key in the object, if it corresponds to a special key that\n", " | should be associated with a graph object, the ordinary dict or list\n", " | will be reinitialized as a special PlotlyDict or PlotlyList of the\n", " | appropriate `kind`.\n", " | \n", " | update(self, dict1=None, **dict2)\n", " | Update current dict with dict1 and then dict2.\n", " | \n", " | This recursively updates the structure of the original dictionary-like\n", " | object with the new entries in the second and third objects. This\n", " | allows users to update with large, nested structures.\n", " | \n", " | Note, because the dict2 packs up all the keyword arguments, you can\n", " | specify the changes as a list of keyword agruments.\n", " | \n", " | Examples:\n", " | # update with dict\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj.update(update_dict)\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | # update with list of keyword arguments\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | obj.update(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | This 'fully' supports duck-typing in that the call signature is\n", " | identical, however this differs slightly from the normal update\n", " | method provided by Python's dictionaries.\n", " | \n", " | validate(self, caller=True)\n", " | Recursively check the validity of the keys in a PlotlyDict.\n", " | \n", " | The valid keys constitute the entries in each object\n", " | dictionary in INFO stored in graph_objs_meta.py.\n", " | \n", " | The validation process first requires that all nested collections be\n", " | converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,\n", " | each of these objects call `validate` and so on, recursively,\n", " | until the entire object has been validated.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from PlotlyDict:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from PlotlyDict:\n", " | \n", " | __metaclass__ = \n", " | A meta class for PlotlyDict class creation.\n", " | \n", " | The sole purpose of this meta class is to properly create the __doc__\n", " | attribute so that running help(Obj), where Obj is a subclass of PlotlyDict,\n", " | will return information about key-value pairs for that object.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from __builtin__.dict:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __getitem__(...)\n", " | x.__getitem__(y) <==> x[y]\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __setitem__(...)\n", " | x.__setitem__(i, y) <==> x[i]=y\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from __builtin__.dict:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", "\n" ] } ], "prompt_number": 59 }, { "cell_type": "markdown", "metadata": {}, "source": [ "which shares the same style keys as `Contour` (more in [subsection 5.3](#5.3-Contours-of-traffic-progression-from-most-to-least-trafficked-sites))." ] }, { "cell_type": "heading", "level": 5, "metadata": {}, "source": [ "Colorbrewer color scales with Plotly" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we take a color scheme from the popular ColorBrewer website and convert it to Plotly syntax. Thanks to the colorbrewer Python package, ColorBrewer color schemes can be easily importing in a Python/IPython session. You can download the package using pip:\n", "\n", " $ pip install colorbrewer\n", "\n", "As we saw [previously](#Custom-color-scales-in-Plotly), a custom color scale in Plotly is simply a list of lists linked to the `'scl'` key where the nested lists have the normalized level values as first item and the level colors as second item. So, consider the following function" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import colorbrewer as cb # import colorbrewer\n", " # uses numpy as well\n", "\n", "def convert_cb_to_scl(cb_color,N=5):\n", " '''\n", " cb_color (positional): colorbrewer color dictionary\n", " N (keyword): number of colors in color scale\n", " '''\n", " colors = cb_color[N] # get list of N color tuples from cb dict\n", " levels = np.linspace(0,1,N).tolist() # get list of N levels \n", " \n", " # Make color scale list of lists, conveting each tuple to 'rgb( , , )'\n", " scl_cb = []\n", " scl_cb += [[i, \"rgb(\"+','.join(map(str,color))+\")\"] \n", " for i,color in zip(levels,colors)]\n", " return scl_cb" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 60 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, choose a color scheme on colorbrewer2.org. The names are listed near the `EXPORT` button in the middle of the page," ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Convert the Purples colorbrewer scale to Plotly syntax\n", "scl_cb = convert_cb_to_scl(cb.Purples) " ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 61 }, { "cell_type": "markdown", "metadata": {}, "source": [ "and fill in an instance of `Histogram2dContour`. For this plot, color only the contour lines by setting `'coloring':'lines'` in `Contours`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_cbar_title='Number of occurences from Apr 1 to Aug 31' # colorbar title\n", "\n", "# Make instance of Histogram2dContour object\n", "# with no histogram normalization (the default)\n", "my_data = Data([Histogram2dContour(\n", " x=z_day1, # sample of the x-axis\n", " y=z_day2, # sample of the y-axis\n", " xbins= XBins(my_bins), # custom x-axis bins (as in fig 5.4a & 5.4b)\n", " ybins= YBins(my_bins), # custom y-axis bins \n", " scl= scl_cb, # N.B. colorbrewer color scale\n", " colorbar= ColorBar(title=my_cbar_title, # colorbar title\n", " titleside='right', # title below colorbar\n", " autotick=False, # custom ticks\n", " dtick=1, # 1 tick for every number of occurences\n", " ticks=''), # no colorbar ticks \n", " contours= Contours(coloring='lines'), # N.B. color only the lines\n", " line= Line(width=3))]) # N.B. increse line width" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 62 }, { "cell_type": "markdown", "metadata": {}, "source": [ "With a similar layout to fig 5.4a:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Define figure and axis titles\n", "my_title = 'Fig 5.4c: Joint frequency distribution
\\\n", "of daily cyclist counts at two Montreal sites in 2013'\n", "my_xtitle = 'Daily cyclist count at {}'.format(site1)\n", "my_ytitle = 'Daily cyclist count at {}'.format(site2)\n", "\n", "# Make instance of Layout\n", "my_layout = Layout(title=my_title, # figure title\n", " font= Font(family='PT Sans Narrow, sans-serif', # global font\n", " size=13),\n", " xaxis= XAxis(title=my_xtitle, # x-axis title\n", " range=my_range, # x-axis range\n", " zeroline=False), # remove x=0 line\n", " yaxis= YAxis(title=my_ytitle, # y-axis title\n", " range=my_range, # y-axis range\n", " zeroline=False), # remove y=0 line\n", " showlegend=False, # remove legend\n", " autosize=False, # custom size\n", " width=650, # figure width \n", " height=520) # and height\n", "\n", "# Add an annotation citing the data source\n", "my_anno_text = \"Source and info: \\\n", "Donn\u00e9es ouvertes de la Ville de Montr\u00e9al\"\n", "my_layout.update(annotations=[make_anno(my_anno_text,1,1.055)])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 63 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, send the figure object to Plotly and get a plot:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_fig = Figure(data=my_data, layout=my_layout)\n", "my_filename = 's5_hist2contour-sites-{}-{}'.format(ind_least_corr[0],ind_least_corr[1])\n", "py.iplot(my_fig, filename=my_filename)" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 64 }, { "cell_type": "markdown", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "

Go to\n", "\n", "[Section 6 --- Convert your Matplotlib plots to Plotly](http://nbviewer.ipython.org/github/plotly/python-user-guide/blob/master/s6_matplotlylib/s6_matplotlylib.ipynb)

\n", "\n", "

Go to\n", "\n", "[Section 4 --- Histograms & Box Plots](http://nbviewer.ipython.org/github/plotly/python-user-guide/blob/master/s4_histograms/s4_histograms.ipynb)

\n", "\n", "

Go back to\n", "\n", "[top of page](http://nbviewer.ipython.org/github/plotly/python-user-guide/blob/master/s5_heatmaps/s5_heatmaps.ipynb#)

\n", "\n", "

Go back to User Guide's\n", "\n", "[homepage](http://nbviewer.ipython.org/github/plotly/python-user-guide/blob/master/s00_homepage/s00_homepage.ipynb)

\n", " \n", "
\n", " \n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "
\n", "\n", "

Got Questions or Feedback?

\n", "\n", "About Plotly\n", "\n", "* email: feedback@plot.ly \n", "* tweet: \n", "@plotlygraphs\n", "\n", "About the User Guide\n", " \n", "* email: etienne@plot.ly\n", "* tweet: @etpinard\n", "\n", "

Notebook styling ideas

\n", "\n", "Big thanks to\n", "\n", "* Cam Davidson-Pilon\n", "* Lorena A. Barba\n", "\n", "
" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from IPython.display import display, HTML\n", "url = 'https://raw.githubusercontent.com/plotly/python-user-guide/master/custom.css'\n", "display(HTML(urllib2.urlopen(url).read()))" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 97 } ], "metadata": {} } ] }