Black Lives Matter. Please consider donating to Black Girls Code today.

The Figure Data Structure in Python

The structure of a figure - data, traces and layout explained.


If you're using Dash Enterprise's Data Science Workspaces, you can copy/paste any of these cells into a Workspace Jupyter notebook.
Alternatively, download this entire tutorial as a Jupyter notebook and import it into your Workspace.
Find out if your company is using Dash Enterprise.


New to Plotly?

Plotly is a free and open-source graphing library for Python. We recommend you read our Getting Started guide for the latest installation or upgrade instructions, then move on to our Plotly Fundamentals tutorials or dive straight in to some Basic Charts tutorials.

Overview

The plotly Python package exists to create, manipulate and render graphical figures (i.e. charts, plots, maps and diagrams) represented by data structures also referred to as figures. The rendering process uses the Plotly.js JavaScript library under the hood although Python developers using this module very rarely need to interact with the Javascript library directly, if ever. Figures can be represented in Python either as dicts or as instances of the plotly.graph_objects.Figure class, and are serialized as text in JavaScript Object Notation (JSON) before being passed to Plotly.js.

Note: the recommended entry-point into the plotly package is the high-level plotly.express module, also known as Plotly Express, which consists of Python functions which return fully-populated plotly.graph_objects.Figure objects. This page exists to document the architecture of the data structure that these objects represent, for users who wish to understand more about how to customize them, or assemble them from other plotly.graph_objects components.

Viewing the underlying data structure for any plotly.graph_objects.Figure object, including those returned by Plotly Express, can be done via print(fig) or, in JupyterLab, with the special fig.show("json") renderer. Figures also support fig.to_dict() and fig.to_json() methods. print()ing the figure will result in the often-verbose layout.template key being represented as ellipses '...' for brevity.

In [1]:
import plotly.express as px

fig = px.line(x=["a","b","c"], y=[1,3,2], title="sample figure")
print(fig)
fig.show()
Figure({
    'data': [{'hovertemplate': 'x=%{x}<br>y=%{y}<extra></extra>',
              'legendgroup': '',
              'line': {'color': '#636efa', 'dash': 'solid'},
              'mode': 'lines',
              'name': '',
              'orientation': 'v',
              'showlegend': False,
              'type': 'scatter',
              'x': array(['a', 'b', 'c'], dtype=object),
              'xaxis': 'x',
              'y': array([1, 3, 2]),
              'yaxis': 'y'}],
    'layout': {'legend': {'tracegroupgap': 0},
               'template': '...',
               'title': {'text': 'sample figure'},
               'xaxis': {'anchor': 'y', 'domain': [0.0, 1.0], 'title': {'text': 'x'}},
               'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'y'}}}
})

Figures as Trees of Attributes

Plotly.js supports inputs adhering to a well-defined schema, whose overall architecture is explained in this page and which is exhaustively documented in the Figure Reference (which is itself generated from a machine-readable JSON representation of the schema). Figures are represented as trees with named nodes called "attributes". The root node of the tree has three top-level attributes: data, layout and frames (see below).

Attributes are referred to in text and in the Figure Reference by their full "path" i.e. the dot-delimited concatenation of their parents. For example "layout.width" refers to the attribute whose key is "width" inside a dict which is the value associated with a key "layout" at the root of the figure. If one of the parents is a list rather than a dict, a set of brackets is inserted in the path when referring to the attribute in the abstract, e.g. "layout.annotations[].text". Finally, as explained below, the top-level "data" attribute defines a list of typed objects called "traces" with the schema dependent upon the type, and these attributes' paths are listed in the Figure Reference as "data[type=scatter].name".

The plotly.graph_objects module contains an automatically-generated hierarchy of Python classes which represent non-leaf attributes in the figure schema and provide a Pythonic API for them. When manipulating a plotly.graph_objects.Figure object, attributes can be set either directly using Python object attributes e.g. fig.layout.title.font.family="Open Sans" or using update methods and "magic underscores" e.g. fig.update_layout(title_font_family="Open Sans")

When building a figure, it is not necessary to populate every attribute of every object. At render-time, the JavaScript layer will compute default values for each required unspecified attribute, depending upon the ones that are specified, as documented in the Figure Reference. An example of this would be layout.xaxis.range, which may be specified explicitly, but if not will be computed based on the range of x values for every trace linked to that axis. The JavaScript layer will ignore unknown attributes or malformed values, although the plotly.graph_objects module provides Python-side validation for attribute values. Note also that if the layout.template key is present (as it is by default) then default values will be drawn first from the contents of the template and only if missing from there will the JavaScript layer infer further defaults. The built-in template can be disabled by setting layout.template="none".

The Top-Level data Attribute

The first of the three top-level attributes of a figure is data, whose value must be a list of dicts referred to as "traces".

  • Each trace has one of more than 40 possible types (see below for a list organized by subplot type, including e.g. scatter, bar, pie, surface, choropleth etc), and represents a set of related graphical marks in a figure. Each trace must have a type attribute which defines the other allowable attributes.
  • Each trace is drawn on a single subplot whose type must be compatible with the trace's type, or is its own subplot (see below).
  • Traces may have a single legend entry, with the exception of pie and funnelarea traces (see below).
  • Certain trace types support continuous color, with an associated colorbar, which can be controlled by attributes either within the trace, or within the layout when using the coloraxis attribute.

The Top-Level layout Attribute

The second of the three top-level attributes of a figure is layout, whose value is referred to in text as "the layout" and must be a dict, containing attributes that control positioning and configuration of non-data-related parts of the figure such as:

  • Dimensions and margins, which define the bounds of "paper coordinates" (see below)
  • Figure-wide defaults: templates, fonts, colors, hover-label and modebar defaults
  • Title and legend (positionable in container and/or paper coordinates)
  • Color axes and associated color bars (positionable in paper coordinates)
  • Subplots of various types on which can be drawn multiple traces and which are positioned in paper coordinates:
    • xaxis, yaxis, xaxis2, yaxis3 etc: X and Y cartesian axes, the intersections of which are cartesian subplots
    • scene, scene2, scene3 etc: 3d scene subplots
    • ternary, ternary2, ternary3, polar, polar2, polar3, geo, geo2, geo3, mapbox, mapbox2, mabox3 etc: ternary, polar, geo or mapbox subplots
  • Non-data marks which can be positioned in paper coordinates, or in data coordinates linked to 2d cartesian subplots:
  • Controls which can be positioned in paper coordinates and which can trigger Plotly.js functions when interacted with by a user:

The Top-Level frames Attribute

The third of the three top-level attributes of a figure is frames, whose value must be a list of dicts that define sequential frames in an animated plot. Each frame contains its own data attribute as well as other parameters. Animations are usually triggered and controlled via controls defined in layout.sliders and/or layout.updatemenus

The config Object

At render-time, it is also possible to control certain figure behaviors which are not considered part of the figure proper i.e. the behavior of the "modebar" and how the figure relates to mouse actions like scrolling etc. The object that contains these options is called the config, and has its own documentation page. It is exposed in Python as the config keyword argument of the .show() method on plotly.graph_objects.Figure objects.

Positioning With Paper, Container Coordinates, or Axis Domain Coordinates

Various figure components configured within the layout of the figure support positioning attributes named x or y, whose values may be specified in "paper coordinates" (sometimes referred to as "plot fractions" or "normalized coordinates"). Examples include layout.xaxis.domain or layout.legend.x or layout.annotation[].x.

Positioning in paper coordinates is not done in absolute pixel terms, but rather in terms relative to a coordinate system defined with an origin (0,0) at (layout.margin.l, layout.margin.b) and a point (1,1) at (layout.width-layout.margin.r, layout.height-layout.margin.t) (note: layout.margin values are pixel values, as are layout.width and layout.height). Paper coordinate values less than 0 or greater than 1 are permitted, and refer to areas within the plot margins.

To position an object in "paper" coordinates, the corresponding axis reference is set to "paper". For instance a shape's xref attribute would be set to "paper" so that the x value of the shape refers to its position in paper coordinates.

Note that the contents of the layout.margin attribute are by default computed based on the position and dimensions of certain items like the title or legend, and may be made dependent on the position and dimensions of tick labels as well when setting the layout.xaxis.automargin attribute to True. This has the effect of automatically increasing the margin values and therefore shrinking the physical area defined between the (0,0) and (1,1) points. Positioning certain items at paper coordinates less than 0 or greater than 1 will also trigger this behavior. The layout.width and layout.height, however, are taken as givens, so a figure will never grow or shrink based on its contents.

The figure title may be positioned using "container coordinates" which have (0,0) and (1,1) anchored at the bottom-left and top-right of the figure, respectively, and therefore are independent of the values of layout.margin.

Furthermore, shapes, annotations, and images can be placed relative to an axis's domain so that, for instance, an x value of 0.5 would place the object halfway along the x-axis, regardless of the domain as specified in the layout.xaxis.domain attribute. This behavior can be specified by adding ' domain' to the axis reference in the axis referencing attribute of the object. For example, setting yref = 'y2 domain' for a shape will refer to the length and position of the axis named y2.

2D Cartesian Trace Types and Subplots

The most commonly-used kind of subplot is a two-dimensional Cartesian subplot. Traces compatible with these subplots support xaxis and yaxis attributes whose values must refer to corresponding objects in the layout portion of the figure. For example, if xaxis="x", and yaxis="y" (which is the default) then this trace is drawn on the subplot at the intersection of the axes configured under layout.xaxis and layout.yaxis, but if xaxis="x2" and yaxis="y3" then the trace is drawn at the intersection of the axes configured under layout.xaxis2 and layout.yaxis3. Note that attributes such as layout.xaxis and layout.xaxis2 etc do not have to be explicitly defined, in which case default values will be inferred. Multiple traces of different types can be drawn on the same subplot.

X- and Y-axes support the type attribute, which enables them to represent continuous values (type="linear", type="log"), temporal values (type="date") or categorical values (type="category", type="multicategory). Axes can also be overlaid on top of one another to create dual-axis or multiple-axis charts. 2-d cartesian subplots lend themselves very well to creating "small multiples" figures, also known as facet or trellis plots.

The following trace types are compatible with 2d-cartesian subplots via the xaxis and yaxis attributes:

3D, Polar and Ternary Trace Types and Subplots

Beyond 2D cartesian subplots, figures can include three-dimensional cartesian subplots, polar subplots and ternary subplots. The following trace types support attributes named scene, polar or ternary, whose values must refer to corresponding objects in the layout portion of the figure i.e. ternary="ternary2" etc. Note that attributes such as layout.scene and layout.ternary2 etc do not have to be explicitly defined, in which case default values will be inferred. Multiple traces of a compatible type can be placed on the same subplot.

The following trace types are compatible with 3D subplots via the scene attribute, which contains special camera controls:

The following trace types are compatible with polar subplots via the polar attribute:

The following trace types are compatible with ternary subplots via the ternary attribute:

Map Trace Types and Subplots

Figures can include two different types of map subplots: geo subplots for outline maps and mapbox subplots for tile maps. The following trace types support attributes named geo or mapbox, whose values must refer to corresponding objects in the layout i.e. geo="geo2" etc. Note that attributes such as layout.geo2 and layout.mapbox etc do not have to be explicitly defined, in which case default values will be inferred. Multiple traces of a compatible type can be placed on the same subplot.

The following trace types are compatible with geo subplots via the geo attribute:

The following trace types are compatible with mapbox subplots via the mapbox attribute:

Traces Which Are Their Own Subplots

Certain trace types cannot share subplots, and hence have no attribute to map to a corresponding subplot in the layout. Instead, these traces are their own subplot and support a domain attribute for position, which enables the trace to be positioned in paper coordinates (see below). With the exception of pie and funnelarea, such traces also do not support legends (see below)

The following trace types are their own subplots and support a domain attribute:

Carpet Trace Types and Subplots

Certain trace types use traces of type carpet as a subplot. These support a carpet attribute whose value must match the value of the carpet attribute of the carpet trace they are to be drawn on. Multiple compatible traces can be placed on the same carpet trace.

The following trace types are compatible with carpet trace subplots via the carpet attribute:

Trace Types, Legends and Color Bars

Traces of most types can be optionally associated with a single legend item in the legend. Whether or not a given trace appears in the legend is controlled via the showlegend attribute. Traces which are their own subplots (see above) do not support this, with the exception of traces of type pie and funnelarea for which every distinct color represented in the trace gets a separate legend item. Users may show or hide traces by clicking or double-clicking on their associated legend item. Traces that support legend items also support the legendgroup attribute, and all traces with the same legend group are treated the same way during click/double-click interactions.

The fact that legend items are linked to traces means that when using discrete color, a figure must have one trace per color in order to get a meaningful legend. Plotly Express has robust support for discrete color to make this easy.

Traces which support continuous color can also be associated with color axes in the layout via the coloraxis attribute. Multiple traces can be linked to the same color axis. Color axes have a legend-like component called color bars. Alternatively, color axes can be configured within the trace itself.

In [ ]:
 

What About Dash?

Dash is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.

Learn about how to install Dash at https://dash.plot.ly/installation.

Everywhere in this page that you see fig.show(), you can display the same figure in a Dash application by passing it to the figure argument of the Graph component from the built-in dash_core_components package like this:

import plotly.graph_objects as go # or plotly.express as px
fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)
# fig.add_trace( ... )
# fig.update_layout( ... )

import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()
app.layout = html.Div([
    dcc.Graph(figure=fig)
])

app.run_server(debug=True, use_reloader=False)  # Turn off reloader if inside Jupyter