Interactive Python Plotting with plotly

Plotly is a cloud-based graphing and analytics platform with Python, R, & MATLAB APIs.

The Python Quant Platform is a platform for browser-based financial analytics and applications. You can combine the two to make interactive graphs in an IPython Notebook.

To access and execute this notebook register under Plotly Registration.



Run this Notebook to see the integration in action. To run this Notebook, go to the toolbar, press "cell", and then "run all." Here's what we'll be doing:

In [1]:
import plotly
plotly.__version__
Out[1]:
'1.6.6'
In [2]:
import warnings
warnings.simplefilter('ignore')
In [3]:
import plotly.plotly as py
from plotly.graph_objs import *
from plotly.widgets import GraphWidget
import plotly.tools as tls
In [4]:
import pandas as pd
In [5]:
from IPython.html import widgets 
from IPython.display import display, clear_output
In [6]:
py.sign_in('Python-Demo-Account', 'gwt101uhh0')

I. pandas & IPython Widgets

Let's begin by downloading a 75MB csv file with details on NYC 311 calls. We'll be using pandas and IPython to make an interactive widget. Here we're embedding plots in iframes in our IPython Notebook. Widgets let you interactively explore a graph or dataset.

Plotly renders the plot with D3.js, and automatically embeds the plot in the NB. You can also call a static plot. Plots default to public; to make plots private you can use world_readable=False.

In [7]:
from IPython.display import Image
Image(url='http://i.imgur.com/OUVRQ1u.gif')
Out[7]:
In [8]:
df = pd.read_csv('https://raw.githubusercontent.com/plotly/widgets/master/ipython-examples/311_150k.csv', infer_datetime_format=True)
df = df
df.head()
Out[8]:
Unique Key Created Date Closed Date Agency Agency Name Complaint Type Descriptor Location Type Incident Zip Incident Address ... Bridge Highway Name Bridge Highway Direction Road Ramp Bridge Highway Segment Garage Lot Name Ferry Direction Ferry Terminal Name Latitude Longitude Location
0 29300358 11/16/2014 11:46:00 PM 11/16/2014 11:46:00 PM DSNY BCC - Queens East Derelict Vehicles 14 Derelict Vehicles Street 11432 80-25 PARSONS BOULEVARD ... NaN NaN NaN NaN NaN NaN NaN 40.719411 -73.808882 (40.719410639341916, -73.80888158860446)
1 29299837 11/16/2014 02:24:35 AM 11/16/2014 02:24:35 AM DOB Department of Buildings Building/Use Illegal Conversion Of Residential Building/Space NaN 10465 938 HUNTINGTON AVENUE ... NaN NaN NaN NaN NaN NaN NaN 40.827862 -73.830641 (40.827862046105416, -73.83064067165407)
2 29297857 11/16/2014 02:17:12 AM 11/16/2014 02:50:48 AM NYPD New York City Police Department Illegal Parking Blocked Sidewalk Street/Sidewalk 11201 229 DUFFIELD STREET ... NaN NaN NaN NaN NaN NaN NaN 40.691248 -73.984375 (40.69124772858873, -73.98437529459297)
3 29294647 11/16/2014 02:15:13 AM NaN NYPD New York City Police Department Noise - Street/Sidewalk Loud Music/Party Street/Sidewalk 10040 128 NAGLE AVENUE ... NaN NaN NaN NaN NaN NaN NaN 40.861248 -73.926308 (40.861247930170535, -73.92630783362215)
4 29300211 11/16/2014 02:14:01 AM NaN NYPD New York City Police Department Illegal Parking Commercial Overnight Parking Street/Sidewalk 10306 625 LINCOLN AVENUE ... NaN NaN NaN NaN NaN NaN NaN 40.570565 -74.092229 (40.57056460126485, -74.09222907551542)

5 rows × 52 columns

In [9]:
df['Complaint Type'].value_counts()
Out[9]:
HEAT/HOT WATER              32202
Street Light Condition       7558
Blocked Driveway             6997
UNSANITARY CONDITION         6174
PAINT/PLASTER                5388
Illegal Parking              5381
Street Condition             4847
Noise                        4615
PLUMBING                     4284
Water System                 3323
Noise - Commercial           3206
DOOR/WINDOW                  3194
Traffic Signal Condition     2766
WATER LEAK                   2501
Dirty Conditions             2283
...
Lifeguard                               2
Illegal Animal Sold                     2
Internal Code                           2
Special Natural Area District (SNAD)    2
Fire Alarm - Replacement                2
Highway Sign - Dangling                 2
Public Toilet                           2
Radioactive Material                    1
Calorie Labeling                        1
DHS Income Savings Requirement          1
Window Guard                            1
Poison Ivy                              1
Bottled Water                           1
Illegal Fireworks                       1
DWD                                     1
Length: 183, dtype: int64
In [10]:
# create a simple bar chart with plotly

c = df['Complaint Type'].value_counts()  
url = py.plot([Bar(x=c.index, y=c.values)], filename='311 most common complaints', auto_open=False)
plotly.tools.embed(url)
Out[10]:
In [11]:
# turn this graph into an IPython widget

graph = GraphWidget(url)

# Place the graph in a popup, so that we can move it around
popup = widgets.PopupWidget(
    children=[graph]
)
popup.set_css('display', 'none', selector='.btn')

popup
In [12]:
# update the graph widget with the most common counts of a different column

a = df['Agency Name'].value_counts()
graph.restyle({'x': [a.index], 'y': [a.values]})
In [13]:
# IPython widget dropdowns

column_headers_dropdown = widgets.DropdownWidget()
column_headers_dropdown.values  = {column: column for column in df.columns}
column_headers_dropdown
In [14]:
# Re-graph the data on dropdown selection

def on_dropdown_selection(_, old_selection, new_selection):
    clear_output()
    display(new_selection)
    
    vc = df[new_selection].value_counts()

    graph.restyle({'x': [vc.index], 'y': [vc.values]})
    
column_headers_dropdown.on_trait_change(on_dropdown_selection, 'value')
In [15]:
# search complaints
idx = df['Complaint Type'].str.contains('Tree').fillna(False)
df[idx]['Complaint Type'].value_counts()
Out[15]:
Damaged Tree               2072
Overgrown Tree/Branches    1171
Dead Tree                   668
Illegal Tree Damage         150
dtype: int64
In [16]:
# simple dataframe filter
t = widgets.TextWidget()
t.description = 'Search complaint types (e.g. "Tree")'
t
In [17]:
def on_text_input(_, old_text, new_text):
    clear_output()
    display(new_text)
    
    idx = df['Complaint Type'].str.contains(new_text).fillna(False)
    vc = df[idx][column_headers_dropdown.value].value_counts()
    
    graph.restyle({'x': [vc.index], 'y': [vc.values]})    
    
t.on_trait_change(on_text_input, 'value', remove=False)

III. Interactive matplotlib

We can also turn matplotlib plots into interactive Plotly graphs. See our matplotlib docs for more. Click and drag to zoom, hover to see data from the plot.

In [18]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
In [19]:
fig = plt.figure()

plt.subplots_adjust(hspace=0.4)
t = np.arange(0.01, 20.0, 0.01)

# log y axis
plt.subplot(221)
plt.semilogy(t, np.exp(-t/5.0))
plt.title('semilogy')
plt.grid(True)

# log x axis
plt.subplot(222)
plt.semilogx(t, np.sin(2*np.pi*t))
plt.title('semilogx')
plt.grid(True)

# log x and y axis
plt.subplot(223)
plt.loglog(t, 20*np.exp(-t/10.0), basex=2)
plt.grid(True)
plt.title('loglog base 4 on x')

plt.show()
In [20]:
py.iplot_mpl(fig, filename='styled-mpl-plot') 
Out[20]:
In [21]:
fig2 = plt.figure()

x = np.linspace(0, 10)

plt.plot(x, np.sin(x) + x + np.random.randn(50))
plt.plot(x, np.sin(x) + 0.5 * x + np.random.randn(50))
plt.plot(x, np.sin(x) + 2 * x + np.random.randn(50))


plt.show()
In [22]:
py.iplot_mpl(fig2, strip_style=True, filename='stripped-style') #  remove the matplotlib defaults 
Out[22]:

You can filter via the legend, embed the plot in dashboards and blogs, share and edit with others in a GUI, and translate the plot between languages. Head to the URL for more: https://plot.ly/~Python-Demo-Account/1492/.

IV. 3D

We can also make 3D plots with Plotly's Python API. Here we'll make a 3D Gaussian plot.

In [23]:
import numpy as np
 
def makeGaussian(A, L, fwhm = 3, center=None):
    """ Make a square gaussian kernel.
 
    A is the amplitude of the curve
    L is the length of a side of the square
    fwhm is full-width-half-maximum, which
    can be thought of as an effective radius.
    """
 
    x = np.arange(0, L, 0.1, float)
    y = x[:,np.newaxis]
    
    if center is None:
        x0 = y0 = L // 2
    else:
        x0 = center[0]
        y0 = center[1]
    
    return A*np.exp(-4*np.log(2) * ((x-x0)**2 + (y-y0)**2) / fwhm**2)
In [24]:
A = 10  # choose a maximum amplitude
L = 8   # choose length of square domain

# Get coordinate arrays
z = makeGaussian(A,L)
In [25]:
# Print shape of z
z.shape 
Out[25]:
(80, 80)
In [26]:
my_surface = dict(z=z,            # z coords, a 2D array
                  type='surface', # N.B. 'surface' plot type
                  )
In [27]:
my_fig = dict(data=[my_surface])  # N.B. value link to 'data' must be a list

my_fig  # print figure dictionary below
Out[27]:
{'data': [{'type': 'surface',
   'z': array([[ 0.00052322,  0.00066739,  0.00084606, ...,  0.00106596,
            0.00084606,  0.00066739],
          [ 0.00066739,  0.00085128,  0.00107918, ...,  0.00135968,
            0.00107918,  0.00085128],
          [ 0.00084606,  0.00107918,  0.00136808, ...,  0.00172368,
            0.00136808,  0.00107918],
          ..., 
          [ 0.00106596,  0.00135968,  0.00172368, ...,  0.0021717 ,
            0.00172368,  0.00135968],
          [ 0.00084606,  0.00107918,  0.00136808, ...,  0.00172368,
            0.00136808,  0.00107918],
          [ 0.00066739,  0.00085128,  0.00107918, ...,  0.00135968,
            0.00107918,  0.00085128]])}]}
In [28]:
py.iplot(my_fig, validate=False, filename='3d')
Out[28]:

We can also style the plot inside Plotly, then call it back into the NB.





In [29]:
tls.embed('MattSundquist', 3807)
Out[29]: