Plotly Annotations



In [1]:
import plotly  
plotly.__version__
Out[1]:
'1.1.4'
In [2]:
import plotly.plotly as py
import plotly.tools as tls
from plotly.graph_objs import *


Let's start by retrieving the figure object associated with your original Plotly figure:

In [3]:
empet22 = py.get_figure("https://plot.ly/~empet/22/")
In [4]:
print empet22.to_string()
Figure(
    data=Data([
        Bar(
            x=[39.0, 29.0, 33.0, 35.0, 39.0, 28.0, 33.0, 35.0, 31.0, 31.0, ...],
            y=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 1...],
            orientation='h',
            text=['1936, Jesse DOUGLAS', '1936, Lars Valerian AHLFORS', '19...],
            marker=Marker(
                color='rgb(127,205,187)'
            )
        )
    ]),
    layout=Layout(
        title='The age of Fields Medalists at the time of medal award',
        font=Font(
            family='Raleway, sans-serif'
        ),
        showlegend=False,
        autosize=False,
        width=500,
        height=800,
        xaxis=XAxis(
            title='Age',
            range=[20, 41],
            gridwidth=2
        ),
        yaxis=YAxis(
            title='The index of medalists sorted by award year'
        ),
        bargap=0.15
    )
)

Next, call help on the Annotation graph object:

In [5]:
help(Annotation)  

# note that the next version of Plotly's Python API will have more extension 
# help() documentation for several graph object including Annotation
Help on class Annotation in module plotly.graph_objs.graph_objs:

class Annotation(PlotlyDict)
 |  A dictionary-like object for representing an annotation in plotly.
 |  
 |      Annotations appear as notes on the final figure. You can set all the
 |      features of the annotation text, background color, and location.
 |      Additionally, these notes can be anchored to actual data or the page for
 |      help with location after pan-and-zoom actions.
 |  
 |      This object is validated upon instantiation, therefore, you may see
 |      exceptions getting thrown. These are intended to help users find the
 |      origin of errors faster. The errors will usually contain information that
 |      can be used to remedy the problem.
 |  
 |      Example:
 |  
 |          note = Annotation(text='what i want this to say is:<br>THIS!',
 |                            x=0,
 |                            y=0,
 |                            xref='paper',
 |                            yref='paper,
 |                            yanchor='bottom',
 |                            xanchor='left')
 |  
 |      
 |  Quick method reference:
 |  
 |      Annotation.update(changes)
 |      Annotation.strip_style()
 |      Annotation.get_data()
 |      Annotation.to_graph_objs()
 |      Annotation.validate()
 |      Annotation.to_string()
 |      Annotation.force_clean()
 |  
 |  Valid keys:
 |  
 |      x [required=False] (value=number: x >= 0):
 |          Sets the 'x' position of this annotations.
 |  
 |      y [required=False] (value=number: x >= 0):
 |          Sets the 'y' position of this annotations.
 |  
 |      xref [required=False] (value='paper' | 'x1' | 'x2' | etc):
 |          Sets the x coordinate for this object refers to. If you reference an
 |          axis, e.g., 'x2', the object will move with pan-and-zoom to stay
 |          fixed to this point. If you reference the 'paper', it remains fixed
 |          regardless of pan-and-zoom. In other words, if set to 'paper', the
 |          'x' location refers to the distance from the left side of the
 |          plotting area in normalized coordinates where 0=='left' and
 |          1=='right'. If set to refer to an xaxis' object, e.g., 'x1', 'x2',
 |          'x3', etc., the 'x' location will refer to the location in terms of
 |          this axis.
 |  
 |      yref [required=False] (value='paper' | 'y1' | 'y2' | etc):
 |          Sets the y coordinate for this object refers to. If you reference an
 |          axis, e.g., 'y2', the object will move with pan-and-zoom to stay
 |          fixed to this point. If you reference the 'paper', it remains fixed
 |          regardless of pan-and-zoom. In other words, if set to 'paper', the
 |          'y' location refers to the distance from the left side of the
 |          plotting area in normalized coordinates where 0=='bottom' and
 |          1=='top'. If set to refer to an yaxis' object, e.g., 'y1', 'y2',
 |          'y3', etc., the 'y' location will refer to the location in terms of
 |          this axis.
 |  
 |      text [required=False] (value=string):
 |          The text associated with this annotation. Plotly uses a subset of
 |          HTML escape characters to do things like newline (<br>), bold
 |          (<b></b>), italics (<i></i>), etc.
 |  
 |          Examples:
 |              ['regular text', 'an annotation<br>spanning two lines', '<b>bold
 |              text</b>']
 |  
 |      font [required=False] (value=Font object | dictionary-like):
 |          A dictionary-like object describing the font settings within this
 |          annotation.
 |  
 |          For more, run `help(plotly.graph_objs.Font)`
 |  
 |      align [required=False] (value='left' | 'center' | 'right'):
 |          Sets the alignment of the text in the annotation.
 |  
 |      showarrow [required=False] (value=boolean: True | False):
 |          Toggle whether or not the arrow associated with this annotation with
 |          be shown.
 |  
 |      arrowhead [required=False] (value=0 | 1 | 2 | 3 | 4 | 5 | 6 | 7):
 |          Sets the arrowhead style. Has an effect only if 'showarrow' is set
 |          to True.
 |  
 |      arrowsize [required=False] (value=number: x >= 0):
 |          Scales the arrowhead's size. Has an effect only if 'showarrow' is
 |          set to True.
 |  
 |      arrowwidth [required=False] (value=number: x > 0):
 |          Sets the arrowhead's width (in pixels). Has an effect only if
 |          'showarrow' is set to True.
 |  
 |      arrowcolor [required=False] (value=string describing color):
 |          Sets the color of the arrowhead. Has an effect only if 'showarrow'
 |          is set to True.
 |  
 |          Examples:
 |              ["'green'", "'rgb(0, 255, 0)'", "'rgba(0, 255, 0, 0.3)'",
 |              "'hsl(120,100%,50%)'", "'hsla(120,100%,50%,0.3)'"]
 |  
 |      ax [required=False] (value=number):
 |          Position of the annotation text relative to the arrowhead about the
 |          x-axis. Has an effect only if 'showarrow' is set to True.
 |  
 |      ay [required=False] (value=number):
 |          Position of the annotation text relative to the arrowhead about the
 |          y-axis. Has an effect only if 'showarrow' is set to True.
 |  
 |      bordercolor [required=False] (value=string describing color):
 |          The color of the enclosing boarder of this annotation.
 |  
 |          Examples:
 |              ["'green'", "'rgb(0, 255, 0)'", "'rgba(0, 255, 0, 0.3)'",
 |              "'hsl(120,100%,50%)'", "'hsla(120,100%,50%,0.3)'"]
 |  
 |      borderwidth [required=False] (value=number: x >= 0):
 |          Sets the width of the boarder enclosing this annotation
 |  
 |      borderpad [required=False] (value=number: x in [0, 10]):
 |          The amount of space (padding) between the text and the enclosing
 |          boarder.
 |  
 |      bgcolor [required=False] (value=string describing color):
 |          Sets the background (bg) color for this annotation.
 |  
 |          Examples:
 |              ["'green'", "'rgb(0, 255, 0)'", "'rgba(0, 255, 0, 0.3)'",
 |              "'hsl(120,100%,50%)'", "'hsla(120,100%,50%,0.3)'"]
 |  
 |      opacity [required=False] (value=number: x in [0, 1]):
 |          Sets the opacity, or transparency, of the entire object, also known
 |          as the alpha channel of colors. If the object's color is given in
 |          terms of 'rgba' color model, 'opacity' is redundant.
 |  
 |      xanchor [required=False] (value='left' | 'center' | 'right'):
 |          This defines the horizontal location on the object referenced by the
 |          'x' (position) key. For example, if 'x'==1, 'xref'='paper', and
 |          'xanchor'='right', the rightmost portion of this object will line up
 |          with the rightmost edge of the plotting area.
 |  
 |      yanchor [required=False] (value='bottom' | 'middle' | 'top'):
 |          This defines the horizontal location on the object referenced by the
 |          'y' (position) key. For example, if 'y'==1, 'yref'='paper', and
 |          'yanchor'='top', the topmost portion of this object will line up
 |          with the topmost edge of the plotting area.
 |  
 |      xatype [required=False] (value=):
 |          more info coming soon
 |  
 |      yatype [required=False] (value=):
 |          more info coming soon
 |  
 |      tag [required=False] (value=):
 |          more info coming soon
 |  
 |      ref [required=False] (value=):
 |          more info coming soon
 |  
 |  Method resolution order:
 |      Annotation
 |      PlotlyDict
 |      __builtin__.dict
 |      __builtin__.object
 |  
 |  Methods inherited from PlotlyDict:
 |  
 |  __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 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 the INFO dictionary listed in graph_objs_meta.py.
 |      
 |      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 = '
 |      ') -- 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 INFO stored in graph_objs_meta.py.
 |      
 |      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)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from PlotlyDict:
 |  
 |  __metaclass__ = <class 'plotly.graph_objs.graph_objs.DictMeta'>
 |      A meta class for PlotlyDict class creation.
 |      
 |      The sole purpose of this meta class is to properly create the __doc__
 |      attribute so that running help(Obj), where Obj is a subclass of PlotlyDict,
 |      will return information about key-value pairs for that object.
 |  
 |  ----------------------------------------------------------------------
 |  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)
 |  
 |  __setitem__(...)
 |      x.__setitem__(i, y) <==> x[i]=y
 |  
 |  __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

As the desired annotation includes two rather long <a> HTML tags, I recommand linking your annotation text to a variable. That way you don't have to worry about Python and/or your web browser erroneously formating tabs and spaces. So,

In [6]:
anno_text = "Source: medalist birth dates on \
<a href='http://www.mathunion.org/index.php?id=prizewinners'>[1]</a> and \
<a href='http://en.wikipedia.org/'>[2]</a>"

# (!) use " " to bound the text and ' ' to bound the hyperlinks
#     make sure that the string continues on the first column after the line breaks (\)

Ok. Now fill in an Annotation object:

In [7]:
annotation = Annotation(
    showarrow=False,  # no arrow, making 'x' and 'y' are the coord of the text
    text=anno_text,   # link the annotation text
    xref='paper',     # (!) use 'paper' coordinates for both 'x'
    yref='paper',     #     and 'y'
    x=0,  # (!) corresponds to left edge of plotting area
    y=1,  # (1) " to top edge of plotting area
    xanchor='left',    # (!) 'x' corresponds to the text's left edge
    yanchor='bottom',  # (!) 'y' corresponds to the text's bottom edge
    font=Font(
        size=14        # increase the font size
    )
)

In this case, I recommand using paper coordinates, as the annoation does not refer to a particular data point.

Setinng xref='x1' and yref='y1' would make 'x' and 'y' correspond to the coordinates of the axes. For example, something like:

annotation = Annotation(
    showarrow=False,  
    text=anno_text,   
    xref='x1',     
    yref='y1',    
    x=30,  
    y=40,  
)

would place the annotation at (x=30,y=40).

Moreover, note that axis-referenced annotations move when one zooms in or pans, whereas paper-referenced annotations are static.

Next, package the annoation into Annotations, which is simply takes in a list of Annotation object(s), and update the figure object:

In [8]:
annotations = Annotations([annotation])

# Update figure object
empet22['layout'].update(annotations=annotations)
In [9]:
print empet22.to_string()   # print updated figure object
Figure(
    data=Data([
        Bar(
            x=[39.0, 29.0, 33.0, 35.0, 39.0, 28.0, 33.0, 35.0, 31.0, 31.0, ...],
            y=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 1...],
            orientation='h',
            text=['1936, Jesse DOUGLAS', '1936, Lars Valerian AHLFORS', '19...],
            marker=Marker(
                color='rgb(127,205,187)'
            )
        )
    ]),
    layout=Layout(
        title='The age of Fields Medalists at the time of medal award',
        font=Font(
            family='Raleway, sans-serif'
        ),
        showlegend=False,
        autosize=False,
        width=500,
        height=800,
        xaxis=XAxis(
            title='Age',
            range=[20, 41],
            gridwidth=2
        ),
        yaxis=YAxis(
            title='The index of medalists sorted by award year'
        ),
        annotations=Annotations([
            Annotation(
                x=0,
                y=1,
                xref='paper',
                yref='paper',
                text="Source: medalist birth dates on <a href='http://www.m...",
                font=Font(
                    size=14
                ),
                showarrow=False,
                xanchor='left',
                yanchor='bottom'
            )
        ]),
        bargap=0.15
    )
)

And now, send the updated figure object to Plotly!

In [10]:
py.iplot(empet22, filename="empet22-with-annotation", height=810)

# 'height' adjust the height of notebook iframe




Got Questions or Feedback?

About Plotly

Notebook styling ideas

Big thanks to


In [11]:
from IPython.display import display, HTML
import urllib2
url = 'https://raw.githubusercontent.com/plotly/python-user-guide/master/custom.css'
display(HTML(urllib2.urlopen(url).read()))