Plotly's Python API User Guide

Section 3: Bubble Charts

Welcome to Plotly's Python API User Guide.

Links to the other sections are on the User Guide's homepage
The Github repository is available here

Quickstart (make a bubble charts of 3 bubbles):

>>> import plotly.plotly as py
>>> from plotly.graph_objs import *
>>> # auto sign-in with credentials or use py.sign_in()
>>> trace1 = Scatter(
        x=[1, 2, 3],
        y=[5, 6, 7],
        mode='markers',
        marker=Marker(
            color=['blue', 'red', 'yellow'],
            size=[10, 20, 30]
        )
    )
>>> data = Data([trace1])
>>> py.plot(data)


Check which version is installed on your machine and please upgrade if needed.

In [1]:
# (*) Import plotly package
import plotly

# Check plolty version (if not latest, please upgrade)
plotly.__version__
Out[1]:
'1.6.6'

See the User Guide's homepage for more info on installation and upgrading.


In this section, we analyze GapMinder data of life expectancy and gross domestic product (GDP) of all the world's countries using bubble charts, inspired by Hans Rosling's work. We also cover some of Plotly's marker object options,

We first import a few modules and sign in to Plotly using our credentials file:

In [2]:
# (*) To communicate with Plotly's server, sign in with credentials file
import plotly.plotly as py  

# (*) Useful Python/Plotly tools
import plotly.tools as tls   

# (*) Graph objects to piece together plots
from plotly.graph_objs import *

import numpy as np  # (*) numpy for math functions and arrays

If you are not familiar with credentials files, refer to the User Guide's homepage.

The data is stored in a file called gapminderDataFiveYear.txt. For readers looking to run this notebook, the file is in this section's Github folder. It was originally downloaded here, thanks to Jennifer Bryan.

To help us manipulate the date, we use the pandas module in this section.
You can download the module using pip. Simply run:

$ pip install pandas

For more info on this module, we recommand Hernán Rojas' Pandas tutorial.

So, first read the data and define a dataframe object:

In [3]:
# (*) Pandas for data manipulation
import pandas as pd 

# Read csv file and define dataframe object (df for dataframe)
df = pd.read_csv('gapminderDataFiveYear.txt', sep='\t')

df.head()  # show dataframe header to stdout
Out[3]:
country year pop continent lifeExp gdpPercap
0 Afghanistan 1952 8425333 Asia 28.801 779.445314
1 Afghanistan 1957 9240934 Asia 30.332 820.853030
2 Afghanistan 1962 10267083 Asia 31.997 853.100710
3 Afghanistan 1967 11537966 Asia 34.020 836.197138
4 Afghanistan 1972 13079460 Asia 36.088 739.981106

5 rows × 6 columns

The data file gives us information about life expentancy, GDP per capita and population for several years, continents and conuntry. Would it be too much to ask to condense all this information into a single plot? Using Plotly bubble chart, the answer is no.

3.1 A Plotly version of Hans Rosling's bubble chart

Bubble charts a great for way to show information about several variables on a single plot. Bubble charts in Plotly are generated using the Scatter graph object where two variables are plotted against each other following a particular color scheme and marker size distribution (i.e. the bubbles) thus relating up to four different variables.

So, let's plot:

  • Life expentancy versus GDP per capita for a specific year, for all countries in the data file, filling in the 'y' and 'x' keys in Scatter.
  • Marker sizes incresing with population size (the bubbles!), with the 'size' key in the Marker object linked to 'marker' in Scatter.
  • Marker colors correspond to different continents with the 'color' key in the Marker object linked to 'marker' in Scatter.

Finally, to make sure that each bubble is visible, we modify the opacity of each marker. This is done by linking the 'opacity' key in Marker to a value between 0 and 1.

In [4]:
help(Marker)  # call help()

# notice 'color', 'size', 'sizeref', 'sizemode' and 'opacity'
Help on class Marker in module plotly.graph_objs.graph_objs:

class Marker(PlotlyDict)
 |  A dictionary-like object containing specifications of the marker points.
 |  
 |  Online examples:
 |  
 |      https://plot.ly/python/line-and-scatter/
 |      https://plot.ly/python/bubble-charts/
 |  
 |  Parent key:
 |  
 |      marker
 |  
 |  Quick method reference:
 |  
 |      Marker.update(changes)
 |      Marker.strip_style()
 |      Marker.get_data()
 |      Marker.to_graph_objs()
 |      Marker.validate()
 |      Marker.to_string()
 |      Marker.force_clean()
 |  
 |  Valid keys:
 |  
 |      color [required=False] (value=list or 1d numpy array of string
 |      describing color) (streamable):
 |          Sets the color of the face of the marker object. If 'color' is
 |          linked to a list or 1d numpy array of color strings, color values
 |          are mapped to individual marker points in the same order as in the
 |          data lists or arrays. To set the color of the marker's bordering
 |          line, use 'line' in 'marker'. The 'color' key can also accept list
 |          or 1d numpy array of numbers, where each number is then mapped to a
 |          color using the color scale set in 'colorscale'.
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      size [required=False] (value=number: x > 0, or list of these numbers)
 |      (streamable):
 |          Sets the size of the markers (in pixels). If 'size' is linked to a
 |          list or 1d numpy array of numbers, size values are mapped to
 |          individual marker points in the same order as in the 'x', 'y (or
 |          'z') list or 1d numpy array. In this case, use 'size' in conjunction
 |          with 'sizeref' and 'sizemode' to fine-tune the map from the numbers
 |          linked to 'size' and the marker points' rendered sizes.
 |  
 |      symbol [required=False] (value='dot' | 'cross' | 'diamond' | 'square' |
 |      'triangle-down' | 'triangle-left' | 'triangle-right' | 'triangle-up' |
 |      'x' OR list of these string values):
 |          The symbol that is drawn on the plot for each marker. Supported only
 |          in scatter traces. If 'symbol' is linked to a list or 1d numpy
 |          array, the symbol values are mapped to individual marker points in
 |          the same order as in the list or 1d numpy array linked to 'x', 'y'
 |          (or 'z').
 |  
 |      line [required=False] (value=Line object | dictionary-like object)
 |      (streamable):
 |          Links a dictionary-like object containing line parameters for the
 |          line segments associated with this marker. For example, the line
 |          segments around each marker point in a scatter trace or the line
 |          segments around each bar in a bar trace.
 |  
 |          For more, run `help(plotly.graph_objs.Line)`
 |  
 |      opacity [required=False] (value=number: x in [0, 1], or list of these
 |      numbers):
 |          Sets the opacity, or transparency also known as the alpha channel of
 |          colors) of the marker points. If the marker points' color is given
 |          in terms of 'rgba' color model, this does not need to be defined. If
 |          'opacity' is linked to a list or an array of numbers, opacity values
 |          are mapped to individual marker points in the same order as in the
 |          'x', 'y' (or 'z') list or 1d numpy array.
 |  
 |      sizeref [required=False] (value=number: x >= 0):
 |          Sets the scale factor used to determine the rendered size of each
 |          marker point in this trace. Applies only to scatter traces that have
 |          their 'size' key in 'marker' linked to a list or 1d numpy array. If
 |          set, the value linked to 'sizeref' is used to divide each entry
 |          linked to 'size'. Specifically, setting 'sizeref' to less (greater)
 |          than 1, increases (decreases) the rendered marker sizes.
 |  
 |      sizemode [required=False] (value='diameter'| 'area'):
 |          Choose between marker size scaling options for the marker points in
 |          this trace. Applies only to scatter traces that have their 'size'
 |          key in 'marker' linked to a list or 1d numpy array. If 'diameter'
 |          ('area'), then the diameter (area) of the rendered marker points (in
 |          pixels) are proportional to the numbers linked to 'size'.For
 |          example, set 'sizemode' to 'area' for a more a smaller range of
 |          rendered marker sizes.
 |  
 |      colorscale [required=False] (value=list or 1d numpy array of value-color
 |      pairs | 'Greys' | 'Greens' | 'Bluered' | 'Hot' | 'Picnic' | 'Portland' |
 |      'Jet' | 'RdBu' | 'Blackbody' | 'Earth' | 'Electric' | 'YIOrRd' |
 |      'YIGnBu'):
 |          Sets and/or defines the color scale for this trace. The string
 |          values are pre-defined color scales. For custom color scales, define
 |          a list or 1d numpy array of value-color pairs where, the first
 |          element of the pair corresponds to a normalized value of color from
 |          0-1, i.e. (c-cmin)/ (cmax-cmin), and the second element of pair
 |          corresponds to a color. Use with 'cauto', 'cmin' and 'cmax to fine-
 |          tune the map from 'color' to rendered colors.
 |  
 |          Examples:
 |              'Greys' | [[0, 'rgb(0,0,0)'], [0.5, 'rgb(65, 182, 196)'], [1,
 |              'rgb(255,255,255)']]
 |  
 |      cauto [required=False] (value=a boolean: True | False):
 |          Toggle whether or not the default values of 'cmax' and 'cmax' can be
 |          overwritten.
 |  
 |      cmin [required=False] (value=number):
 |          Sets the minimum 'color' data value to be resolved by the color
 |          scale. Its default value is the minimum of the 'color' data values.
 |          This value will be used as the minimum in the color scale
 |          normalization. For more info see 'colorscale'. Has only an effect if
 |          'color' is linked to a list or 1d numpy array nd 'colorscale' is
 |          set.
 |  
 |      cmax [required=False] (value=number):
 |          Sets the maximum 'color' data value to be resolved by the color
 |          scale. Its default value is the maximum of the 'color' data values.
 |          This value will be used as the maximum in the color scale
 |          normalization. For more info see 'colorscale'. Has only an effect if
 |          'color' is linked to a list or 1d numpy array nd 'colorscale' is
 |          set.
 |  
 |      outliercolor [required=False] (value=a string describing color):
 |          For box plots only. Has an effect only if 'boxpoints' is set to
 |          'suspectedoutliers'. Sets the face color of the outlier points.
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      maxdisplayed [required=False] (value=number: x >= 0):
 |          Sets maximum number of displayed points for this trace. Applies only
 |          to scatter traces.
 |  
 |  Method resolution order:
 |      Marker
 |      PlotlyDict
 |      __builtin__.dict
 |      __builtin__.object
 |  
 |  Methods inherited from PlotlyDict:
 |  
 |  __init__(self, *args, **kwargs)
 |  
 |  __setitem__(self, key, value)
 |  
 |  force_clean(self, caller=True)
 |      Attempts to convert to graph_objs and call force_clean() on values.
 |      
 |      Calling force_clean() on a PlotlyDict will ensure that the object is
 |      valid and may be sent to plotly. This process will also remove any
 |      entries that end up with a length == 0.
 |      
 |      Careful! This will delete any invalid entries *silently*.
 |  
 |  get_data(self)
 |      Returns the JSON for the plot with non-data elements stripped.
 |  
 |  get_ordered(self, caller=True)
 |  
 |  strip_style(self)
 |      Strip style from the current representation.
 |      
 |      All PlotlyDicts and PlotlyLists are guaranteed to survive the
 |      stripping process, though they made be left empty. This is allowable.
 |      
 |      Keys that will be stripped in this process are tagged with
 |      `'type': 'style'` in graph_objs_meta.json.
 |      
 |      This process first attempts to convert nested collections from dicts
 |      or lists to subclasses of PlotlyList/PlotlyDict. This process forces
 |      a validation, which may throw exceptions.
 |      
 |      Then, each of these objects call `strip_style` on themselves and so
 |      on, recursively until the entire structure has been validated and
 |      stripped.
 |  
 |  to_graph_objs(self, caller=True)
 |      Walk obj, convert dicts and lists to plotly graph objs.
 |      
 |      For each key in the object, if it corresponds to a special key that
 |      should be associated with a graph object, the ordinary dict or list
 |      will be reinitialized as a special PlotlyDict or PlotlyList of the
 |      appropriate `kind`.
 |  
 |  to_string(self, level=0, indent=4, eol='\n', pretty=True, max_chars=80)
 |      Returns a formatted string showing graph_obj constructors.
 |      
 |      Example:
 |      
 |          print(obj.to_string())
 |      
 |      Keyword arguments:
 |      level (default = 0) -- set number of indentations to start with
 |      indent (default = 4) -- set indentation amount
 |      eol (default = '\n') -- set end of line character(s)
 |      pretty (default = True) -- curtail long list output with a '...'
 |      max_chars (default = 80) -- set max characters per line
 |  
 |  update(self, dict1=None, **dict2)
 |      Update current dict with dict1 and then dict2.
 |      
 |      This recursively updates the structure of the original dictionary-like
 |      object with the new entries in the second and third objects. This
 |      allows users to update with large, nested structures.
 |      
 |      Note, because the dict2 packs up all the keyword arguments, you can
 |      specify the changes as a list of keyword agruments.
 |      
 |      Examples:
 |      # update with dict
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj.update(update_dict)
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      # update with list of keyword arguments
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      obj.update(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      This 'fully' supports duck-typing in that the call signature is
 |      identical, however this differs slightly from the normal update
 |      method provided by Python's dictionaries.
 |  
 |  validate(self, caller=True)
 |      Recursively check the validity of the keys in a PlotlyDict.
 |      
 |      The valid keys constitute the entries in each object
 |      dictionary in graph_objs_meta.json
 |      
 |      The validation process first requires that all nested collections be
 |      converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,
 |      each of these objects call `validate` and so on, recursively,
 |      until the entire object has been validated.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from PlotlyDict:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from __builtin__.dict:
 |  
 |  __cmp__(...)
 |      x.__cmp__(y) <==> cmp(x,y)
 |  
 |  __contains__(...)
 |      D.__contains__(k) -> True if D has a key k, else False
 |  
 |  __delitem__(...)
 |      x.__delitem__(y) <==> del x[y]
 |  
 |  __eq__(...)
 |      x.__eq__(y) <==> x==y
 |  
 |  __ge__(...)
 |      x.__ge__(y) <==> x>=y
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __getitem__(...)
 |      x.__getitem__(y) <==> x[y]
 |  
 |  __gt__(...)
 |      x.__gt__(y) <==> x>y
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  __le__(...)
 |      x.__le__(y) <==> x<=y
 |  
 |  __len__(...)
 |      x.__len__() <==> len(x)
 |  
 |  __lt__(...)
 |      x.__lt__(y) <==> x<y
 |  
 |  __ne__(...)
 |      x.__ne__(y) <==> x!=y
 |  
 |  __repr__(...)
 |      x.__repr__() <==> repr(x)
 |  
 |  __sizeof__(...)
 |      D.__sizeof__() -> size of D in memory, in bytes
 |  
 |  clear(...)
 |      D.clear() -> None.  Remove all items from D.
 |  
 |  copy(...)
 |      D.copy() -> a shallow copy of D
 |  
 |  fromkeys(...)
 |      dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
 |      v defaults to None.
 |  
 |  get(...)
 |      D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
 |  
 |  has_key(...)
 |      D.has_key(k) -> True if D has a key k, else False
 |  
 |  items(...)
 |      D.items() -> list of D's (key, value) pairs, as 2-tuples
 |  
 |  iteritems(...)
 |      D.iteritems() -> an iterator over the (key, value) items of D
 |  
 |  iterkeys(...)
 |      D.iterkeys() -> an iterator over the keys of D
 |  
 |  itervalues(...)
 |      D.itervalues() -> an iterator over the values of D
 |  
 |  keys(...)
 |      D.keys() -> list of D's keys
 |  
 |  pop(...)
 |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 |      If key is not found, d is returned if given, otherwise KeyError is raised
 |  
 |  popitem(...)
 |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
 |      2-tuple; but raise KeyError if D is empty.
 |  
 |  setdefault(...)
 |      D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
 |  
 |  values(...)
 |      D.values() -> list of D's values
 |  
 |  viewitems(...)
 |      D.viewitems() -> a set-like object providing a view on D's items
 |  
 |  viewkeys(...)
 |      D.viewkeys() -> a set-like object providing a view on D's keys
 |  
 |  viewvalues(...)
 |      D.viewvalues() -> an object providing a view on D's values
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from __builtin__.dict:
 |  
 |  __hash__ = None
 |  
 |  __new__ = <built-in method __new__ of type object>
 |      T.__new__(S, ...) -> a new object with type S, a subtype of T

Next, select one year and slice the dataframe correspondingly:

In [5]:
# Choose a year, find other years with df['year'].unique()
the_year = 2007   

# Find indices corresponding to 'the_year'
i_year = (df['year'] == the_year)

# Grab all rows correponding to 'the_year'
df_year = df[i_year] 

Define a dictionary of colors, one for each continent:

In [6]:
colors = dict(
    Asia='#1f77b4', 
    Europe='#ff7f0e', 
    Africa='#2ca02c',
    Americas='#d62728',
    Oceania='#9467bd'
)

We choose to plot data associated with each continent as a different trace. To taking advantage of the regularity between each trace, we next define a trace-generating function.

Here, 'size' in Marker will be linked to an array with the population count of each country in a given continent. To fine-tune the map between to the values linked to 'size' to rendered pixel size, we use the 'sizeref' and 'size' in Marker. Consider,

In [7]:
# (!) Set 'size' values to be proportional to rendered area,
#     instead of diameter. This makes the range of bubble sizes smaller
sizemode = 'area'       

# (!) Set a reference for 'size' values (i.e. a population-to-pixel scaling).
#     Here the max bubble area will be on the order of 100 pixels
sizeref = df_year['pop'].max() / 1e2**2

# Define a trace-generating function (returns a Scatter object)
def make_trace(X, continent, sizes, color):  
    return Scatter(
        x=X['gdpPercap'],  # GDP on the x-xaxis
        y=X['lifeExp'],    # life Exp on th y-axis
        name=continent,    # label continent names on hover
        mode='markers',    # (!) point markers only on this plot
        marker= Marker(
            color=color,          # marker color
            size=sizes,           # (!) marker sizes (sizes is a list)
            sizeref=sizeref,      # link sizeref
            sizemode=sizemode,    # link sizemode
            opacity=0.6,          # (!) partly transparent markers
            line=Line(width=0.0)  # remove marker borders
        )
    )

Next, fill the data objects:

In [8]:
# Initialize data object 
data = Data()

# Group data frame by continent sub-dataframe (named X), 
#   make one trace object per continent and append to data object
for continent, X in df_year.groupby('continent'):
    
    sizes = X['pop']                            # get population array 
    color = colors[continent]                   # get bubble color
    
    data.append(
        make_trace(X, continent, sizes, color)  # append trace to data object
    )                             

Onto layout:

In [9]:
# Set plot and axis titles
title = "Fig 3.1a: Hans Rosling's Bubble Chart for the year {}".format(the_year)
x_title = "Gross Domestic Product per Capita [in USD of the year 2000]"
y_title = "Life Expentancy [in years]"

# Define a dictionary of axis style options
axis_style = dict(     
    zeroline=False,       # remove thick zero line
    gridcolor='#FFFFFF',  # white grid lines
    ticks='outside',      # draw ticks outside axes 
    ticklen=8,            # tick length
    tickwidth=1.5         #   and width
)

# Make layout object
layout = Layout(
    title=title,             # set plot title
    plot_bgcolor='#EFECEA',  # set plot color to grey
    xaxis=XAxis(
        axis_style,      # add axis style dictionary
        title=x_title,   # x-axis title
    ),
    yaxis=YAxis(
        axis_style,      # add axis style dictionary
        title=y_title,   # y-axis title
    )
)

Package the data and layout instances into Figure, send it to Plotly and get a plot:

In [10]:
# Make Figure object
fig = Figure(data=data, layout=layout)

# (@) Send to Plotly and show in notebook
py.iplot(fig, filename='s3_life-gdp')
Out[10]:

Not bad!

Notice how Plotly automatically converted the x-axis units to k's (i.e. thousands) of USD. No need to change the ticks' labels here (imagine having tick labels of 0, 10000, 20000, 30000, etc. instead).

Axis types

More generally, Plotly allow users to choose between different exponent format with the 'exponentformat' key in XAxis and YAxis. The available values for 'exponentformat' are:

  • 'none', for no exponents,
  • 'e', for lower-case exponent form, e.g. 1000 is displayed as 1e-3,
  • 'E', for upper-case exponent form, e.g. 1000 is displayed as 1E-3,
  • 'power', for powers of ten exponents, e.g. 1000 is displayed as $10^3$,
  • 'SI', for Standard International prefixes, e.g. 10,000 is 10k and 1 billion is 1G,
  • 'B', for alternative prefixes e.g. 1 billion is 1B.

The key 'showexponent', also in XAxis and YAxis, determines which ticks labels are displayed in exponent form. The available values for 'showexponent' are:

  • 'none', for no exponents,
  • 'all', for exponents at all tick labels,
  • 'first', for an exponent only at the first tick label,
  • 'last', for an exponent only at the last tick label.

In our next plot, we'll set all our exponents to 'power'.

In addition, as the relationship between GDP per capita and life expentancy appears to be logarithmic, we replot our first bubble chart on log x-axis using the type key.

In [11]:
help(XAxis) # call help()!

# notice the 'type' key!
Help on class XAxis in module plotly.graph_objs.graph_objs:

class XAxis(PlotlyDict)
 |  A dictionary-like object for representing an x-axis in plotly.
 |  
 |  Online examples:
 |  
 |      https://plot.ly/python/axes/
 |      https://plot.ly/python/multiple-axes/
 |      https://plot.ly/python/subplots/
 |      https://plot.ly/python/insets/
 |  
 |  Parent key:
 |  
 |      xaxis
 |  
 |  Quick method reference:
 |  
 |      XAxis.update(changes)
 |      XAxis.strip_style()
 |      XAxis.get_data()
 |      XAxis.to_graph_objs()
 |      XAxis.validate()
 |      XAxis.to_string()
 |      XAxis.force_clean()
 |  
 |  Valid keys:
 |  
 |      title [required=False] (value=a string):
 |          The x-axis title.
 |  
 |      titlefont [required=False] (value=Font object | dictionary-like object):
 |          Links a dictionary-like object describing the font settings of the
 |          x-axis title.
 |  
 |          For more, run `help(plotly.graph_objs.Font)`
 |  
 |      range [required=False] (value=number array of length 2):
 |          Defines the start and end point of this x-axis.
 |  
 |          Examples:
 |              [-13, 20] | [0, 1]
 |  
 |      domain [required=False] (value=number array of length 2):
 |          Sets the domain of this x-axis; that is, the available space for
 |          this x-axis to live in. Domain coordinates are given in normalized
 |          coordinates with respect to the paper.
 |  
 |          Examples:
 |              [0, 0.4] | [0.6, 1]
 |  
 |      type [required=False] (value='linear' | 'log' | 'date' | 'category'):
 |          Sets the format of this axis.
 |  
 |      rangemode [required=False] (value='normal' | 'tozero' | 'nonnegative'):
 |          Choose between Plotly's automated axis generation modes: 'normal'
 |          (the default) sets the axis range in relation to the extrema in the
 |          data object, 'tozero' extends the axes to x=0 no matter the data
 |          plotted and 'nonnegative' sets a non-negative range no matter the
 |          data plotted.
 |  
 |      autorange [required=False] (value=True | False | 'reversed'):
 |          Toggle whether or not the range of this x-axis is automatically
 |          picked by Plotly. If 'range' is set, then 'autorange' is set to
 |          False automatically. Otherwise, if 'autorange' is set to True (the
 |          default behavior), the range of this x-axis can respond to
 |          adjustments made in the web GUI automatically. If 'autorange' is set
 |          to 'reversed', then this x-axis is drawn in reverse, e.g. in a 2D
 |          plot, from right to left instead of from left to right (the default
 |          behavior).
 |  
 |      showgrid [required=False] (value=a boolean: True | False):
 |          Toggle whether or not this axis features grid lines.
 |  
 |      zeroline [required=False] (value=a boolean: True | False):
 |          Toggle whether or not an additional grid line (thicker than the
 |          other grid lines, by default) will appear on this axis along x=0.
 |  
 |      showline [required=False] (value=a boolean: True | False):
 |          Toggle whether or not the line bounding this x-axis will be shown on
 |          the figure.
 |  
 |      autotick [required=False] (value=a boolean: True | False):
 |          Toggle whether or not the axis ticks parameters are picked
 |          automatically by Plotly. Once 'autotick' is set to False, the axis
 |          ticks parameters can be declared with 'ticks', 'tick0', 'dtick0' and
 |          other tick-related key in this axis object.
 |  
 |      nticks [required=False] (value=number: x > 0):
 |          Sets the number of axis ticks. No need to set 'autoticks' to False
 |          for 'nticks' to apply.
 |  
 |      ticks [required=False] (value='' | 'inside' | 'outside'):
 |          Sets the format of the ticks on this axis. For hidden ticks, link
 |          'ticks' to an empty string.
 |  
 |      showticklabels [required=False] (value=a boolean: True | False):
 |          Toggle whether or not the axis ticks will feature tick labels.
 |  
 |      tick0 [required=False] (value=number):
 |          Sets the starting point of the ticks of this axis.
 |  
 |      dtick [required=False] (value=number):
 |          Sets the distance between ticks on this axis.
 |  
 |      ticklen [required=False] (value=number):
 |          Sets the length of the tick lines on this axis.
 |  
 |      tickwidth [required=False] (value=number: x > 0):
 |          Sets the width of the tick lines on this axis.
 |  
 |      tickcolor [required=False] (value=a string describing color):
 |          Sets the color of the tick lines on this axis.
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      tickangle [required=False] (value=number: x in [-90, 90]):
 |          Sets the angle in degrees of the ticks on this axis.
 |  
 |      tickfont [required=False] (value=Font object | dictionary-like object):
 |          Links a dictionary-like object defining the parameters of the ticks'
 |          font.
 |  
 |          For more, run `help(plotly.graph_objs.Font)`
 |  
 |      exponentformat [required=False] (value='none' | 'e' | 'E' | 'power' |
 |      'SI' | 'B'):
 |          Sets how exponents show up. Here's how the number 1000000000 (1
 |          billion) shows up in each. If set to 'none': 1,000,000,000. If set
 |          to 'e': 1e+9. If set to 'E': 1E+9. If set to 'power': 1x10^9 (where
 |          the 9 will appear super-scripted). If set to 'SI': 1G. If set to
 |          'B': 1B (useful when referring to currency).
 |  
 |      showexponent [required=False] (value='all' | 'first' | 'last' | 'none'):
 |          If set to 'all', ALL exponents will be shown appended to their
 |          significands. If set to 'first', the first tick's exponent will be
 |          appended to its significand, however no other exponents will appear
 |          --only the significands. If set to 'last', the last tick's exponent
 |          will be appended to its significand, however no other exponents will
 |          appear--only the significands. If set to 'none', no exponents will
 |          appear, only the significands.
 |  
 |      mirror [required=False] (value=True | False | 'ticks' | 'all' |
 |      'allticks'):
 |          Toggle the axis line and/or ticks across the plots or subplots. If
 |          True, mirror the axis line across the primary subplot (i.e. the axis
 |          that this axis is anchored to). If 'ticks', mirror the axis line and
 |          the ticks. If 'all', mirror the axis line to all subplots containing
 |          this axis. If 'allticks', mirror the line and ticks to all subplots
 |          containing this axis. If False, don't mirror the axis or the ticks.
 |  
 |      gridcolor [required=False] (value=a string describing color):
 |          Sets the axis grid color.
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      gridwidth [required=False] (value=number: x > 0):
 |          Sets the grid width (in pixels).
 |  
 |      zerolinecolor [required=False] (value=a string describing color):
 |          Sets the color of this axis' zeroline.
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      zerolinewidth [required=False] (value=number: x > 0):
 |          Sets the width of this axis' zeroline (in pixels).
 |  
 |      linecolor [required=False] (value=a string describing color):
 |          Sets the axis line color.
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      linewidth [required=False] (value=number: x > 0):
 |          Sets the width of the axis line (in pixels).
 |  
 |      anchor [required=False] (value='y' | 'y1' | 'y2' | ... | 'free' ):
 |          Choose whether the position of this x-axis will be anchored to a
 |          corresponding y-axis or will be 'free' to appear anywhere in the
 |          vertical space of this figure. Has no effect in 3D plots.
 |  
 |      overlaying [required=False] (value='x' | 'x1' | 'x2' | ... | False):
 |          Choose to overlay the data bound to this x-axis on the same plotting
 |          area as a corresponding y-axis or choose not overlay other x-the
 |          other axis/axes of this figure.Has no effect in 3D plots.
 |  
 |      side [required=False] (value='bottom' | 'top'):
 |          Sets whether this x-axis sits at the 'bottom' of the plot or at the
 |          'top' of the plot.Has no effect in 3D plots.
 |  
 |      position [required=False] (value=number: x in [0, 1]):
 |          Sets where this x-axis is positioned in the plotting space. For
 |          example if 'position' is set to 0.5, then this axis is placed at the
 |          exact center of the plotting space. Has an effect only if 'anchor'
 |          is set to 'free'.Has no effect in 3D plots.
 |  
 |      showbackground [required=False] (value=a boolean: True | False):
 |          Toggle whether or not this x-axis will have a background color. Has
 |          an effect only in 3D plots.
 |  
 |      backgroundcolor [required=False] (value=a string describing color):
 |          Sets the background color of this x-axis. Has an effect only in 3D
 |          plots and if 'showbackground' is set to True.
 |  
 |      showspikes [required=False] (value=a boolean: True | False):
 |          Toggle whether or not spikes will link up to this x-axis when
 |          hovering over data points. Has an effect only in 3D plots.
 |  
 |      spikesides [required=False] (value=a boolean: True | False):
 |          Toggle whether or not the spikes will expand out to the x-axis
 |          bounds when hovering over data points. Has an effect only in 3D
 |          plots and if 'showspikes' is set to True.
 |  
 |      spikethickness [required=False] (value=number: x > 0):
 |          Sets the thickness (in pixels) of the x-axis spikes.Has an effect
 |          only in 3D plots and if 'showspikes' is set to True.
 |  
 |  Method resolution order:
 |      XAxis
 |      PlotlyDict
 |      __builtin__.dict
 |      __builtin__.object
 |  
 |  Methods inherited from PlotlyDict:
 |  
 |  __init__(self, *args, **kwargs)
 |  
 |  __setitem__(self, key, value)
 |  
 |  force_clean(self, caller=True)
 |      Attempts to convert to graph_objs and call force_clean() on values.
 |      
 |      Calling force_clean() on a PlotlyDict will ensure that the object is
 |      valid and may be sent to plotly. This process will also remove any
 |      entries that end up with a length == 0.
 |      
 |      Careful! This will delete any invalid entries *silently*.
 |  
 |  get_data(self)
 |      Returns the JSON for the plot with non-data elements stripped.
 |  
 |  get_ordered(self, caller=True)
 |  
 |  strip_style(self)
 |      Strip style from the current representation.
 |      
 |      All PlotlyDicts and PlotlyLists are guaranteed to survive the
 |      stripping process, though they made be left empty. This is allowable.
 |      
 |      Keys that will be stripped in this process are tagged with
 |      `'type': 'style'` in graph_objs_meta.json.
 |      
 |      This process first attempts to convert nested collections from dicts
 |      or lists to subclasses of PlotlyList/PlotlyDict. This process forces
 |      a validation, which may throw exceptions.
 |      
 |      Then, each of these objects call `strip_style` on themselves and so
 |      on, recursively until the entire structure has been validated and
 |      stripped.
 |  
 |  to_graph_objs(self, caller=True)
 |      Walk obj, convert dicts and lists to plotly graph objs.
 |      
 |      For each key in the object, if it corresponds to a special key that
 |      should be associated with a graph object, the ordinary dict or list
 |      will be reinitialized as a special PlotlyDict or PlotlyList of the
 |      appropriate `kind`.
 |  
 |  to_string(self, level=0, indent=4, eol='\n', pretty=True, max_chars=80)
 |      Returns a formatted string showing graph_obj constructors.
 |      
 |      Example:
 |      
 |          print(obj.to_string())
 |      
 |      Keyword arguments:
 |      level (default = 0) -- set number of indentations to start with
 |      indent (default = 4) -- set indentation amount
 |      eol (default = '\n') -- set end of line character(s)
 |      pretty (default = True) -- curtail long list output with a '...'
 |      max_chars (default = 80) -- set max characters per line
 |  
 |  update(self, dict1=None, **dict2)
 |      Update current dict with dict1 and then dict2.
 |      
 |      This recursively updates the structure of the original dictionary-like
 |      object with the new entries in the second and third objects. This
 |      allows users to update with large, nested structures.
 |      
 |      Note, because the dict2 packs up all the keyword arguments, you can
 |      specify the changes as a list of keyword agruments.
 |      
 |      Examples:
 |      # update with dict
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj.update(update_dict)
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      # update with list of keyword arguments
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      obj.update(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      This 'fully' supports duck-typing in that the call signature is
 |      identical, however this differs slightly from the normal update
 |      method provided by Python's dictionaries.
 |  
 |  validate(self, caller=True)
 |      Recursively check the validity of the keys in a PlotlyDict.
 |      
 |      The valid keys constitute the entries in each object
 |      dictionary in graph_objs_meta.json
 |      
 |      The validation process first requires that all nested collections be
 |      converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,
 |      each of these objects call `validate` and so on, recursively,
 |      until the entire object has been validated.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from PlotlyDict:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from __builtin__.dict:
 |  
 |  __cmp__(...)
 |      x.__cmp__(y) <==> cmp(x,y)
 |  
 |  __contains__(...)
 |      D.__contains__(k) -> True if D has a key k, else False
 |  
 |  __delitem__(...)
 |      x.__delitem__(y) <==> del x[y]
 |  
 |  __eq__(...)
 |      x.__eq__(y) <==> x==y
 |  
 |  __ge__(...)
 |      x.__ge__(y) <==> x>=y
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __getitem__(...)
 |      x.__getitem__(y) <==> x[y]
 |  
 |  __gt__(...)
 |      x.__gt__(y) <==> x>y
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  __le__(...)
 |      x.__le__(y) <==> x<=y
 |  
 |  __len__(...)
 |      x.__len__() <==> len(x)
 |  
 |  __lt__(...)
 |      x.__lt__(y) <==> x<y
 |  
 |  __ne__(...)
 |      x.__ne__(y) <==> x!=y
 |  
 |  __repr__(...)
 |      x.__repr__() <==> repr(x)
 |  
 |  __sizeof__(...)
 |      D.__sizeof__() -> size of D in memory, in bytes
 |  
 |  clear(...)
 |      D.clear() -> None.  Remove all items from D.
 |  
 |  copy(...)
 |      D.copy() -> a shallow copy of D
 |  
 |  fromkeys(...)
 |      dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
 |      v defaults to None.
 |  
 |  get(...)
 |      D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
 |  
 |  has_key(...)
 |      D.has_key(k) -> True if D has a key k, else False
 |  
 |  items(...)
 |      D.items() -> list of D's (key, value) pairs, as 2-tuples
 |  
 |  iteritems(...)
 |      D.iteritems() -> an iterator over the (key, value) items of D
 |  
 |  iterkeys(...)
 |      D.iterkeys() -> an iterator over the keys of D
 |  
 |  itervalues(...)
 |      D.itervalues() -> an iterator over the values of D
 |  
 |  keys(...)
 |      D.keys() -> list of D's keys
 |  
 |  pop(...)
 |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 |      If key is not found, d is returned if given, otherwise KeyError is raised
 |  
 |  popitem(...)
 |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
 |      2-tuple; but raise KeyError if D is empty.
 |  
 |  setdefault(...)
 |      D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
 |  
 |  values(...)
 |      D.values() -> list of D's values
 |  
 |  viewitems(...)
 |      D.viewitems() -> a set-like object providing a view on D's items
 |  
 |  viewkeys(...)
 |      D.viewkeys() -> a set-like object providing a view on D's keys
 |  
 |  viewvalues(...)
 |      D.viewvalues() -> an object providing a view on D's values
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from __builtin__.dict:
 |  
 |  __hash__ = None
 |  
 |  __new__ = <built-in method __new__ of type object>
 |      T.__new__(S, ...) -> a new object with type S, a subtype of T

So, changing the axis type is as simple as adding a key-value pair in XAxis (or YAxis).

Hover-mode options

Moreover, let's utilize Plotly's intractability by adding some hover text (the 'text' key in Scatter) and tweaking the plot's hover mode.

In [12]:
help(Layout)  # call help()! 

# notice 'hovermode'!
Help on class Layout in module plotly.graph_objs.graph_objs:

class Layout(PlotlyDict)
 |  A dictionary-like object containing specification of the layout of a plotly
 |      figure.
 |  
 |  Online examples:
 |  
 |      https://plot.ly/python/figure-labels/
 |      https://plot.ly/python/axes/
 |      https://plot.ly/python/bar-charts/
 |      https://plot.ly/python/log-plot/
 |  
 |  Parent key:
 |  
 |      layout
 |  
 |  Quick method reference:
 |  
 |      Layout.update(changes)
 |      Layout.strip_style()
 |      Layout.get_data()
 |      Layout.to_graph_objs()
 |      Layout.validate()
 |      Layout.to_string()
 |      Layout.force_clean()
 |  
 |  Valid keys:
 |  
 |      title [required=False] (value=a string):
 |          The title of the figure.
 |  
 |      titlefont [required=False] (value=Font object | dictionary-like object):
 |          Links a dictionary-like object describing the font settings of the
 |          figure's title.
 |  
 |          For more, run `help(plotly.graph_objs.Font)`
 |  
 |      font [required=False] (value=Font object | dictionary-like object):
 |          Links a dictionary-like object describing the global font settings
 |          for this figure (e.g. all axis titles and labels).
 |  
 |          For more, run `help(plotly.graph_objs.Font)`
 |  
 |      showlegend [required=False] (value=a boolean: True | False):
 |          Toggle whether or not the legend will be shown in this figure.
 |  
 |      autosize [required=False] (value=a boolean: True | False):
 |          Toggle whether or not the dimensions of the figure are automatically
 |          picked by Plotly. Plotly picks figure's dimensions as a function of
 |          your machine's display resolution. Once 'autosize' is set to False,
 |          the figure's dimensions can be set with 'width' and 'height'.
 |  
 |      width [required=False] (value=number: x > 0):
 |          Sets the width in pixels of the figure you are generating.
 |  
 |      height [required=False] (value=number: x > 0):
 |          Sets the height in pixels of the figure you are generating.
 |  
 |      xaxis [required=False] (value=XAxis object | dictionary-like object):
 |          Links a dictionary-like object describing an x-axis (i.e. an
 |          horizontal axis). The first XAxis object can be entered into
 |          'layout' by linking it to 'xaxis' OR 'xaxis1', both keys are
 |          identical to Plotly.  To create references other than x-axes, you
 |          need to define them in 'layout' using keys 'xaxis2', 'xaxis3' and so
 |          on. Note that in 3D plots, XAxis objects must be linked from a Scene
 |          object.
 |  
 |          For more, run `help(plotly.graph_objs.XAxis)`
 |  
 |      yaxis [required=False] (value=YAxis object | dictionary-like object):
 |          Links a dictionary-like object describing an y-axis (i.e. an
 |          vertical axis). The first YAxis object can be entered into 'layout'
 |          by linking it to 'yaxis' OR 'yaxis1', both keys are identical to
 |          Plotly.  To create references other than y-axes, you need to define
 |          them in 'layout' using keys 'yaxis2', 'yaxis3' and so on. Note that
 |          in 3D plots, YAxis objects must be linked from a Scene object.
 |  
 |          For more, run `help(plotly.graph_objs.YAxis)`
 |  
 |      legend [required=False] (value=Legend object | dictionary-like object):
 |          Links a dictionary-like object containing the legend parameters for
 |          this figure.
 |  
 |          For more, run `help(plotly.graph_objs.Legend)`
 |  
 |      annotations [required=False] (value=Annotations object | list-like
 |      object of one or several dictionary-like object):
 |          Links a list-like object that contains one or multiple annotation
 |          dictionary-like objects.
 |  
 |          For more, run `help(plotly.graph_objs.Annotations)`
 |  
 |      margin [required=False] (value=Margin object | dictionary-like object):
 |          Links a dictionary-like object containing the margin parameters for
 |          this figure.
 |  
 |          For more, run `help(plotly.graph_objs.Margin)`
 |  
 |      paper_bgcolor [required=False] (value=a string describing color):
 |          Sets the color of the figure's paper (i.e. area representing the
 |          canvas of the figure).
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      plot_bgcolor [required=False] (value=a string describing color):
 |          Sets the background color of the plot (i.e. the area laying inside
 |          this figure's axes.
 |  
 |          Examples:
 |              'green' | 'rgb(0, 255, 0)' | 'rgba(0, 255, 0, 0.3)' |
 |              'hsl(120,100%,50%)' | 'hsla(120,100%,50%,0.3)' | '#434F1D'
 |  
 |      hovermode [required=False] (value='closest' | 'x' | 'y'):
 |          Sets this figure's behavior when a user hovers over it. When set to
 |          'x', all data sharing the same 'x' coordinate will be shown on
 |          screen with corresponding trace labels. When set to 'y' all data
 |          sharing the same 'y' coordinates will be shown on the screen with
 |          corresponding trace labels. When set to 'closest', information about
 |          the data point closest to where the viewer is hovering will appear.
 |  
 |      dragmode [required=False] (value='zoom' | 'pan' | 'rotate' (in 3D
 |      plots)):
 |          Sets this figure's behavior when a user preforms a mouse 'drag' in
 |          the plot area. When set to 'zoom', a portion of the plot will be
 |          highlighted, when the viewer exits the drag, this highlighted
 |          section will be zoomed in on. When set to 'pan', data in the plot
 |          will move along with the viewers dragging motions. A user can always
 |          depress the 'shift' key to access the whatever functionality has not
 |          been set as the default. In 3D plots, the default drag mode is
 |          'rotate' which rotates the scene.
 |  
 |      separators [required=False] (value=a two-character string):
 |          Sets the decimal (the first character) and thousands (the second
 |          character) separators to be displayed on this figure's tick labels
 |          and hover mode. This is meant for internationalization purposes. For
 |          example, if 'separator' is set to ', ', then decimals are separated
 |          by commas and thousands by spaces. One may have to set
 |          'exponentformat' to 'none' in the corresponding axis object(s) to
 |          see the effects.
 |  
 |      barmode [required=False] (value='stack' | 'group' | 'overlay'):
 |          For bar and histogram plots only. This sets how multiple bar objects
 |          are plotted together. In other words, this defines how bars at the
 |          same location appear on the plot. If set to 'stack' the bars are
 |          stacked on top of one another. If set to 'group', the bars are
 |          plotted next to one another, centered around the shared location. If
 |          set to 'overlay', the bars are simply plotted over one another, you
 |          may need to set the opacity to see this.
 |  
 |      bargap [required=False] (value=number: x in [0, 1)):
 |          For bar and histogram plots only. Sets the gap between bars (or sets
 |          of bars) at different locations.
 |  
 |      bargroupgap [required=False] (value=number: x in [0, 1)):
 |          For bar and histogram plots only. Sets the gap between bars in the
 |          same group. That is, when multiple bar objects are plotted and share
 |          the same locations, this sets the distance between bars at each
 |          location.
 |  
 |      boxmode [required=False] (value='overlay' | 'group'):
 |          For box plots only. Sets how groups of box plots appear. If set to
 |          'overlay', a group of boxes will be plotted directly on top of one
 |          another at their specified location. If set to 'group', the boxes
 |          will be centered around their shared location, but they will not
 |          overlap.
 |  
 |      boxgap [required=False] (value=number: x in [0, 1)):
 |          For box plots only. Sets the gap between boxes at different
 |          locations (i.e. x-labels). If there are multiple boxes at a single
 |          x-label, then this sets the gap between these sets of boxes.For
 |          example, if 0, then there is no gap between boxes. If 0.25, then
 |          this gap occupies 25% of the available space and the box width (or
 |          width of the set of boxes) occupies the remaining 75%.
 |  
 |      boxgroupgap [required=False] (value=number: x in [0, 1)):
 |          For box plots only. Sets the gap between boxes in the same group,
 |          where a group is the set of boxes with the same location (i.e.
 |          x-label). For example, if 0, then there is no gap between boxes. If
 |          0.25, then this gap occupies 25% of the available space and the box
 |          width occupies the remaining 75%.
 |  
 |      radialaxis [required=False] (value=RadialAxis object | dictionary-like
 |      object):
 |          Links a dictionary-like object describing the radial axis in a polar
 |          plot.
 |  
 |          For more, run `help(plotly.graph_objs.RadialAxis)`
 |  
 |      angularaxis [required=False] (value=AngularAxis object | dictionary-like
 |      object):
 |          Links a dictionary-like object describing the angular axis in a
 |          polar plot.
 |  
 |          For more, run `help(plotly.graph_objs.AngularAxis)`
 |  
 |      scene [required=False] (value=Scene object | dictionary-like object):
 |          Links a dictionary-like object describing a scene in a 3D plot. The
 |          first Scene object can be entered into 'layout' by linking it to
 |          'scene' OR 'scene1', both keys are identical to Plotly. Link
 |          subsequent Scene objects using 'scene2', 'scene3', etc.
 |  
 |          For more, run `help(plotly.graph_objs.Scene)`
 |  
 |      direction [required=False] (value='clockwise' | 'counterclockwise'):
 |          For polar plots only. Sets the direction corresponding to positive
 |          angles.
 |  
 |      orientation [required=False] (value=number: x in [-360, 360]):
 |          For polar plots only. Rotates the entire polar by the given angle.
 |  
 |      hidesources [required=False] (value=a boolean: True | False):
 |          Toggle whether or not an annotation citing the data source is placed
 |          at the bottom-right corner of the figure.This key has an effect only
 |          on graphs that have been generated from forked graphs from plot.ly.
 |  
 |  Method resolution order:
 |      Layout
 |      PlotlyDict
 |      __builtin__.dict
 |      __builtin__.object
 |  
 |  Methods defined here:
 |  
 |  __init__(self, *args, **kwargs)
 |  
 |  force_clean(self, caller=True)
 |      Attempts to convert to graph_objs and call force_clean() on values.
 |      
 |      Calling force_clean() on a Layout will ensure that the object is
 |      valid and may be sent to plotly. This process will also remove any
 |      entries that end up with a length == 0.
 |      
 |      Careful! This will delete any invalid entries *silently*.
 |      
 |      This method differs from the parent (PlotlyDict) method in that it
 |      must check for an infinite number of possible axis keys, i.e. 'xaxis',
 |      'xaxis1', 'xaxis2', 'xaxis3', etc. Therefore, it cannot make a call
 |      to super...
 |  
 |  to_graph_objs(self, caller=True)
 |      Walk obj, convert dicts and lists to plotly graph objs.
 |      
 |      For each key in the object, if it corresponds to a special key that
 |      should be associated with a graph object, the ordinary dict or list
 |      will be reinitialized as a special PlotlyDict or PlotlyList of the
 |      appropriate `kind`.
 |  
 |  to_string(self, level=0, indent=4, eol='\n', pretty=True, max_chars=80)
 |      Returns a formatted string showing graph_obj constructors.
 |      
 |      Example:
 |      
 |          print(obj.to_string())
 |      
 |      Keyword arguments:
 |      level (default = 0) -- set number of indentations to start with
 |      indent (default = 4) -- set indentation amount
 |      eol (default = '\n') -- set end of line character(s)
 |      pretty (default = True) -- curtail long list output with a '...'
 |      max_chars (default = 80) -- set max characters per line
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from PlotlyDict:
 |  
 |  __setitem__(self, key, value)
 |  
 |  get_data(self)
 |      Returns the JSON for the plot with non-data elements stripped.
 |  
 |  get_ordered(self, caller=True)
 |  
 |  strip_style(self)
 |      Strip style from the current representation.
 |      
 |      All PlotlyDicts and PlotlyLists are guaranteed to survive the
 |      stripping process, though they made be left empty. This is allowable.
 |      
 |      Keys that will be stripped in this process are tagged with
 |      `'type': 'style'` in graph_objs_meta.json.
 |      
 |      This process first attempts to convert nested collections from dicts
 |      or lists to subclasses of PlotlyList/PlotlyDict. This process forces
 |      a validation, which may throw exceptions.
 |      
 |      Then, each of these objects call `strip_style` on themselves and so
 |      on, recursively until the entire structure has been validated and
 |      stripped.
 |  
 |  update(self, dict1=None, **dict2)
 |      Update current dict with dict1 and then dict2.
 |      
 |      This recursively updates the structure of the original dictionary-like
 |      object with the new entries in the second and third objects. This
 |      allows users to update with large, nested structures.
 |      
 |      Note, because the dict2 packs up all the keyword arguments, you can
 |      specify the changes as a list of keyword agruments.
 |      
 |      Examples:
 |      # update with dict
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj.update(update_dict)
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      # update with list of keyword arguments
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      obj.update(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      This 'fully' supports duck-typing in that the call signature is
 |      identical, however this differs slightly from the normal update
 |      method provided by Python's dictionaries.
 |  
 |  validate(self, caller=True)
 |      Recursively check the validity of the keys in a PlotlyDict.
 |      
 |      The valid keys constitute the entries in each object
 |      dictionary in graph_objs_meta.json
 |      
 |      The validation process first requires that all nested collections be
 |      converted to the appropriate subclass of PlotlyDict/PlotlyList. Then,
 |      each of these objects call `validate` and so on, recursively,
 |      until the entire object has been validated.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from PlotlyDict:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from __builtin__.dict:
 |  
 |  __cmp__(...)
 |      x.__cmp__(y) <==> cmp(x,y)
 |  
 |  __contains__(...)
 |      D.__contains__(k) -> True if D has a key k, else False
 |  
 |  __delitem__(...)
 |      x.__delitem__(y) <==> del x[y]
 |  
 |  __eq__(...)
 |      x.__eq__(y) <==> x==y
 |  
 |  __ge__(...)
 |      x.__ge__(y) <==> x>=y
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __getitem__(...)
 |      x.__getitem__(y) <==> x[y]
 |  
 |  __gt__(...)
 |      x.__gt__(y) <==> x>y
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  __le__(...)
 |      x.__le__(y) <==> x<=y
 |  
 |  __len__(...)
 |      x.__len__() <==> len(x)
 |  
 |  __lt__(...)
 |      x.__lt__(y) <==> x<y
 |  
 |  __ne__(...)
 |      x.__ne__(y) <==> x!=y
 |  
 |  __repr__(...)
 |      x.__repr__() <==> repr(x)
 |  
 |  __sizeof__(...)
 |      D.__sizeof__() -> size of D in memory, in bytes
 |  
 |  clear(...)
 |      D.clear() -> None.  Remove all items from D.
 |  
 |  copy(...)
 |      D.copy() -> a shallow copy of D
 |  
 |  fromkeys(...)
 |      dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
 |      v defaults to None.
 |  
 |  get(...)
 |      D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
 |  
 |  has_key(...)
 |      D.has_key(k) -> True if D has a key k, else False
 |  
 |  items(...)
 |      D.items() -> list of D's (key, value) pairs, as 2-tuples
 |  
 |  iteritems(...)
 |      D.iteritems() -> an iterator over the (key, value) items of D
 |  
 |  iterkeys(...)
 |      D.iterkeys() -> an iterator over the keys of D
 |  
 |  itervalues(...)
 |      D.itervalues() -> an iterator over the values of D
 |  
 |  keys(...)
 |      D.keys() -> list of D's keys
 |  
 |  pop(...)
 |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 |      If key is not found, d is returned if given, otherwise KeyError is raised
 |  
 |  popitem(...)
 |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
 |      2-tuple; but raise KeyError if D is empty.
 |  
 |  setdefault(...)
 |      D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
 |  
 |  values(...)
 |      D.values() -> list of D's values
 |  
 |  viewitems(...)
 |      D.viewitems() -> a set-like object providing a view on D's items
 |  
 |  viewkeys(...)
 |      D.viewkeys() -> a set-like object providing a view on D's keys
 |  
 |  viewvalues(...)
 |      D.viewvalues() -> an object providing a view on D's values
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from __builtin__.dict:
 |  
 |  __hash__ = None
 |  
 |  __new__ = <built-in method __new__ of type object>
 |      T.__new__(S, ...) -> a new object with type S, a subtype of T

By default, when hovering over a data point Plotly prints its y-value near the cursor and its x-value on the x-axis. The default behavior corresponds to 'hovermode':'x' in Layout. In contrast, with 'hovermode':'y', Plotly prints x-values near the cursor and y-values on the y-axis.

Alternatively, when 'hovermode' is set to 'closest', information about the data point closest to where the viewer is hovering appears next to the cursor. Plotly prints this info inside the same text box as the text linked to the 'text' key (if defined) of the trace object in question.

So, we make a few updates to the current figure object:

In [13]:
# Update 'xaxis' key, set it to log type and with a power exponent format
fig['layout']['xaxis'].update(
    type='log',
    exponentformat='power',
    showexponent='all'
)

# Update the layout object
fig['layout'].update(
    hovermode='closest',  # (!) hover -> closest data pt
    showlegend=False,     # remove legend (info in hover)
    autosize=False,       # turn off autosize
    width=650,            # plot width
    height=500,           # plot height
)

Next, add hover text presenting the values of all the variables in the dataset at each data point on our figure. To do so, we need to update each trace object as follows:

In [14]:
# Define a hover-text generating function (returns a list of strings)
def make_text(X):
    return 'Country: %s\
    <br>Life Expectancy: %s years\
    <br>GDP per capita: %s $\
    <br>Population: %s million'\
    % (X['country'], X['lifeExp'], X['gdpPercap'], X['pop']/1e6)     
    
# Again, group data frame by continent sub-dataframe (named X),
#   make one trace object per continent and append to data object
i_trace = 0                                        # init. trace counter

for continent, X in df_year.groupby('continent'):
    text = X.apply(make_text, axis=1).tolist()     # get list of hover texts
    fig['data'][i_trace].update(text=text)         # update trace i
    i_trace += 1                                   # inc. trace counter

The apply() dataframe method loops through every row (with axis=1) and calls a given function, in our case make_text().

Before sending the figure object to Plotly, add an annotation citing the data source.

In [15]:
# Update layout with an annotation object in 'annotations' (linked to a list)
fig['layout'].update(annotations=Annotations([
    Annotation(
        text='Data source: GapMinder 2007',  # annotation text
        showarrow=False,                     # remove arrow 
        xref='paper',   # use paper coords
        yref='paper',   #  for both x and y coordinates
        x=0.02,         # x-coord (slightly of plotting area edge)
        y=0.98,         # y-coord (slightly of plotting area edge)
        font=Font(size=14),   # increase font size (default is 12)
        bgcolor='#FFFFFF',    # white background
        borderpad=4           # set border/text space (in pixels)
    )
]))  

# Update title
title = "Fig 3.1b: Hans Rosling's Bubble Chart for the year {}".format(the_year)
fig['layout'].update(title=title)

This yields

In [16]:
# (@) Send to Plotly and show in notebook
py.iplot(fig, filename='s3_life-gdp-log')    
Out[16]:

Not a bad-looking plot.

Spend some time hovering over the bubbles and admire Plotly's interactibility!

If you are feeling adventurous, jump to section 7 to learn how to make an annimated version of Hans Rosling's bubble chart with Plotly's streaming API.

3.2 Small multiple bubble chart

Even though we managed to incorporate a lot of information in our first two bubble charts, only one year's worth of data could be displayed. Our next plot will feature the time evolution of the GDP per capita variable in the GapMinder dataset using a small multiple.

A small multiple is a series of similar graphics or charts plotted as subplots allowing them to be easily compared with one another. In our case, there will be one subplot per country. Each subplot will show GDP per capita versus time where the size of each data point will be a function of the life expectancy of that year.

So first, find the start and end year as well as the number of countries in the dataset:

In [17]:
# Start year, end year and number of year is dataset
df['year'].min(), df['year'].max(), len(df['year'].unique())
Out[17]:
(1952, 2007, 12)
In [18]:
countries = df['country'].unique()            # list of countries
N_country = len(countries)                    # number of countries
N_rowcol = int(np.ceil(np.sqrt(N_country)))   # size of the square subplot grid

N_country, N_rowcol  # print to screen
Out[18]:
(142, 12)

There are 142 countries in the dataset and therefore, choosing to have a square small multiple for aesthetics, we need a 12 by 12 subplot grid to include every country. Consequently, there will be 2 empty subplots.

Next, generate a 12 by 12 subplot grid with shared x and y axes using make_subplots():

In [19]:
# Generate Figure object with 144 axes (12 rows x 12 columns),
fig = tls.make_subplots(
    rows=N_rowcol,    # number of rows
    cols=N_rowcol,    # number of columns
    shared_xaxes=True,
    shared_yaxes=True
)
This is the format of your plot grid:
[ (1,1) x1,y1 ]      [ (1,2) x2,y1 ]      [ (1,3) x3,y1 ]      [ (1,4) x4,y1 ]      [ (1,5) x5,y1 ]      [ (1,6) x6,y1 ]      [ (1,7) x7,y1 ]      [ (1,8) x8,y1 ]      [ (1,9) x9,y1 ]      [ (1,10) x10,y1 ]    [ (1,11) x11,y1 ]    [ (1,12) x12,y1 ]  
[ (2,1) x1,y2 ]      [ (2,2) x2,y2 ]      [ (2,3) x3,y2 ]      [ (2,4) x4,y2 ]      [ (2,5) x5,y2 ]      [ (2,6) x6,y2 ]      [ (2,7) x7,y2 ]      [ (2,8) x8,y2 ]      [ (2,9) x9,y2 ]      [ (2,10) x10,y2 ]    [ (2,11) x11,y2 ]    [ (2,12) x12,y2 ]  
[ (3,1) x1,y3 ]      [ (3,2) x2,y3 ]      [ (3,3) x3,y3 ]      [ (3,4) x4,y3 ]      [ (3,5) x5,y3 ]      [ (3,6) x6,y3 ]      [ (3,7) x7,y3 ]      [ (3,8) x8,y3 ]      [ (3,9) x9,y3 ]      [ (3,10) x10,y3 ]    [ (3,11) x11,y3 ]    [ (3,12) x12,y3 ]  
[ (4,1) x1,y4 ]      [ (4,2) x2,y4 ]      [ (4,3) x3,y4 ]      [ (4,4) x4,y4 ]      [ (4,5) x5,y4 ]      [ (4,6) x6,y4 ]      [ (4,7) x7,y4 ]      [ (4,8) x8,y4 ]      [ (4,9) x9,y4 ]      [ (4,10) x10,y4 ]    [ (4,11) x11,y4 ]    [ (4,12) x12,y4 ]  
[ (5,1) x1,y5 ]      [ (5,2) x2,y5 ]      [ (5,3) x3,y5 ]      [ (5,4) x4,y5 ]      [ (5,5) x5,y5 ]      [ (5,6) x6,y5 ]      [ (5,7) x7,y5 ]      [ (5,8) x8,y5 ]      [ (5,9) x9,y5 ]      [ (5,10) x10,y5 ]    [ (5,11) x11,y5 ]    [ (5,12) x12,y5 ]  
[ (6,1) x1,y6 ]      [ (6,2) x2,y6 ]      [ (6,3) x3,y6 ]      [ (6,4) x4,y6 ]      [ (6,5) x5,y6 ]      [ (6,6) x6,y6 ]      [ (6,7) x7,y6 ]      [ (6,8) x8,y6 ]      [ (6,9) x9,y6 ]      [ (6,10) x10,y6 ]    [ (6,11) x11,y6 ]    [ (6,12) x12,y6 ]  
[ (7,1) x1,y7 ]      [ (7,2) x2,y7 ]      [ (7,3) x3,y7 ]      [ (7,4) x4,y7 ]      [ (7,5) x5,y7 ]      [ (7,6) x6,y7 ]      [ (7,7) x7,y7 ]      [ (7,8) x8,y7 ]      [ (7,9) x9,y7 ]      [ (7,10) x10,y7 ]    [ (7,11) x11,y7 ]    [ (7,12) x12,y7 ]  
[ (8,1) x1,y8 ]      [ (8,2) x2,y8 ]      [ (8,3) x3,y8 ]      [ (8,4) x4,y8 ]      [ (8,5) x5,y8 ]      [ (8,6) x6,y8 ]      [ (8,7) x7,y8 ]      [ (8,8) x8,y8 ]      [ (8,9) x9,y8 ]      [ (8,10) x10,y8 ]    [ (8,11) x11,y8 ]    [ (8,12) x12,y8 ]  
[ (9,1) x1,y9 ]      [ (9,2) x2,y9 ]      [ (9,3) x3,y9 ]      [ (9,4) x4,y9 ]      [ (9,5) x5,y9 ]      [ (9,6) x6,y9 ]      [ (9,7) x7,y9 ]      [ (9,8) x8,y9 ]      [ (9,9) x9,y9 ]      [ (9,10) x10,y9 ]    [ (9,11) x11,y9 ]    [ (9,12) x12,y9 ]  
[ (10,1) x1,y10 ]    [ (10,2) x2,y10 ]    [ (10,3) x3,y10 ]    [ (10,4) x4,y10 ]    [ (10,5) x5,y10 ]    [ (10,6) x6,y10 ]    [ (10,7) x7,y10 ]    [ (10,8) x8,y10 ]    [ (10,9) x9,y10 ]    [ (10,10) x10,y10 ]  [ (10,11) x11,y10 ]  [ (10,12) x12,y10 ]
[ (11,1) x1,y11 ]    [ (11,2) x2,y11 ]    [ (11,3) x3,y11 ]    [ (11,4) x4,y11 ]    [ (11,5) x5,y11 ]    [ (11,6) x6,y11 ]    [ (11,7) x7,y11 ]    [ (11,8) x8,y11 ]    [ (11,9) x9,y11 ]    [ (11,10) x10,y11 ]  [ (11,11) x11,y11 ]  [ (11,12) x12,y11 ]
[ (12,1) x1,y12 ]    [ (12,2) x2,y12 ]    [ (12,3) x3,y12 ]    [ (12,4) x4,y12 ]    [ (12,5) x5,y12 ]    [ (12,6) x6,y12 ]    [ (12,7) x7,y12 ]    [ (12,8) x8,y12 ]    [ (12,9) x9,y12 ]    [ (12,10) x10,y12 ]  [ (12,11) x11,y12 ]  [ (12,12) x12,y12 ]

By default, make_subplots() generates subplots axes row-wise starting from the top-left corner of the plot.

Next, define a trace-generating function. As before in subsection 3.1 the color of each bubble is determined by the continent and hover text is added using make_text():

In [20]:
# (!) Set bubble size monde et size reference
sizemode = 'dimeter'
sizeref = df['lifeExp'].max() / 30

# Define a trace-generating function (returns a Scatter object)
def make_Scatter_gdp(i, j, x , y, color, sizes, name, text):
    return Scatter(
        x=x,   # x coordinates
        y=y,   # y coordinates
        name=name,        # label name (on hover)
        text=text,        # hover text
        mode='markers',   # marker pts only
        marker=Marker( 
            color=color,        # marker color
            size=sizes,         # maker sizes (a list)
            sizeref=sizeref,    # link sizeref
            sizemode=sizemode,  # link sizemode
            opacity=0.6         # partly transparent markers
        ),           
        xaxis= 'x{}'.format(i),  # (!) bind coordinate to given x-axis
        yaxis= 'y{}'.format(j)   # (!) bind coordinate to given y-axis
    )

To try to limit the amount of redundant information on the plot, only subplots on the left-hand side of the subplot grid will have labelled y axes and only subplots on the bottom row of the subplot grid will have labelled x axes.

Additionally, for better comparisons between countries, all subplots will have the same axis range. So, let's get an idea of the range in GDP per capita in the dataset:

In [21]:
df['gdpPercap'].min(), df['gdpPercap'].max()
Out[21]:
(241.16587650000002, 113523.1329)

Then, define style dictionaries for the x and y axis objects and paste them into the figure object:

In [22]:
# For all y axes
yaxis_style = dict(
    type='log',                           # (!) log y-axis
    range=[np.log10(90), np.log10(5e5)],  # (!) set y-axis range w.r.t. log scale
    title='GDP per cap.', # title of the y axes
    ticks='outside',      # no ticks
    showline=True,        # show axis bounding line
    showgrid=False,       # remove grid
    zeroline=False        # no thick line at x=y=0
)

# For all x axes
xaxis_style = dict(
    range=[1950, 2010],  # set x-axis range
    title='year',        # title of the x axes
    ticks='outside',     # no ticks
    showline=True,       # show axis bounding line
    showgrid=False,      # remove grid
    zeroline=False       # no thick line at x=y=0
)

# Paste the style dictionaries into the figure object
for i in range(N_rowcol):
    yaxis_splt = fig['layout']['yaxis{}'.format(i+1)]
    yaxis_splt.update(yaxis_style)
    xaxis_splt = fig['layout']['xaxis{}'.format(i+1)]
    xaxis_splt.update(xaxis_style)

Now, make an annotation-generating function to label the subplots with each country's name:

In [23]:
# Define an annotation-generating function, to label each subplot
def make_splt_anno(i, j, country):
    if len(country) > 14:
        country = country[0:14] + '.'  # truncate country's name if too long
    return Annotation(
        xref='x{}'.format(i),   # position in relation to the x
        yref='y{}'.format(j),   #   and y axes
        x=1955,                 # x position
        xanchor='left',         #   and anchor
        y=np.log10(2.5e5),      # y position (in log coords)
        text=country,           # text is the country name
        showarrow=False,        # no arrow 
        align='center',         # align text in the center
        font=Font(size=14),     # font size
    )

Add a few keys to the layout object:

In [24]:
width = 2000   # fix plot's width 
height = 1800  #  and height in pixels

title = "Fig 3.2: GDP per Capita from 1952 to 2007 \
in USD of the year 2000 [GapMinder]"                  # plot's title

fig['layout'].update(
    title=title,              # set plot's title
    titlefont=Font(size=30),  # increase title font size
    showlegend=False,         # remove legend
    autosize=False,           # turn off autosize
    width=width,              # plot's width
    height=height             # plot's height
)
    
fig['layout']['annotations'] = Annotations([])  # init. 'annotations' key

Now, loop through every country in the dataframe and fill in the data object and update each axis' style:

In [25]:
i = j = 1  # init. col and row counters

# Group dataframe by country in alphabetical order and loop
for country, X in df.groupby('country'):
    
    x = X['year'].values                        # get years
    y = X['gdpPercap'].values                   # get GDP values
    sizes = X['lifeExp'].values                 # get bubble size
    continent = X['continent'].values[0]        # get continent name
    color = colors[continent]                   # get bubble color
    text = X.apply(make_text, axis=1).tolist()  # get hover text

    # Append data object
    fig['data'].append(make_Scatter_gdp(i, j, x, y, 
                                        color, sizes, 
                                        continent, text))
    
    # (!) fig['data'] was intialized by tls.make_subplots()
    
    # Append annotations object, label each subplot
    fig['layout']['annotations'].append(make_splt_anno(i, j, country))
    
    # Increment counter accordingly
    if i == 12:
        j += 1
        i = 1
    else:
        i += 1

We are now ready to send the figure object to Plotly and see the results:

In [26]:
# (@) Send figure object to Plotly, show plot in notebook
py.iplot(fig, filename='s3_small-multiples', width=width, height=height)       

# adjust output cell with 'width' and 'height'
Out[26]:

To see this figure in separate tab, use this link.

Who wants to print this on a T-shirt?


Go to [Section 4 --- Histograms and Box Plots](https://plot.ly/python/histograms-and-box-plots-tutorial)

Go to [Section 2 --- Bar Charts](https://plot.ly/python/bar-charts-tutorial)

Go to [top of page](#Plotly's-Python-API-User-Guide)

Go to User Guide's [homepage](https://plot.ly/python/user-guide)


Got Questions or Feedback?

About Plotly

About the User Guide

Notebook styling ideas

Big thanks to


In [27]:
# CSS styling within IPython notebook
from IPython.display import display, HTML
display(HTML(open('../custom.css').read()))