Plotly basics¶
Plotly is a library for creating interactive plots. To install Plotly from inside of a Jupyter notebook execute the following code:
[2]:
%pip install plotly
Traces and figures¶
Plotly module plotly.graph_objects
defines classes that can be used to construct plots of various types. Below we use it to produce a scatter plot, which displays points with given \(x\) and \(y\) coordinates. This example illustrates the basic process of producing a plot with Plotly. First, we create an object with plot data (a trace in the Plotly nomenclature). Then we embed the trace in a figure object and finally, we display the resulting figure.
[2]:
import plotly.graph_objects as go
import numpy as np
t = np.linspace(0, 10, 30)
# create a trace
sin_plot = go.Scatter(
x=t, # x-coordinates of points
y=np.sin(t), # y-coordinates
mode='markers', # use markers to plot points
marker={'symbol' : 'diamond', # dictionary of marker properties
'color' : 'Red',
'size' : 10})
# create a figure with the trace
fig = go.Figure(data=sin_plot)
# display the figure
fig.show()
In a scatter plot the mode
argument specifies how points in a plot are to be displayed. If we set mode = 'markers'
then each point will be indicated by a marker. If, instead, we set mode = 'lines'
, then points will be connected with straight line segments:
[3]:
t = np.linspace(0, 10, 30)
sin_plot = go.Scatter(
x=t,
y=np.sin(t),
mode='lines', # connect points with line segments
line={'color': 'Blue', # dictionary of line properties
'width': 4})
fig = go.Figure(data=sin_plot)
fig.show()
If is possible to combine mode
values. By setting mode='lines+markers'
we can produce a plot showing both lines and markers:
[4]:
t = np.linspace(0, 10, 30)
sin_plot = go.Scatter(
x=t,
y=np.sin(t),
mode='lines+markers', # plot lines and markers
line={'color': 'Green', # dictionary of line properties
'width': 4},
marker={'symbol': 'diamond', # dictionary of marker properties
'color': 'Red',
'size': 10})
fig = go.Figure(data = sin_plot)
fig.show()
A figure can contain several traces. Below we define two traces and display them in one figure. Notice that Plotly automatically creates a legend identifying each trace.
[5]:
t = np.linspace(0, 10, 50)
# trace of sine function
cos_plot = go.Scatter(x=t,
y=np.cos(t),
mode='lines',
name='sin(t)') # name of the trace for the legend
# trace of cosine function
sin_plot = go.Scatter(x=t,
y=np.sin(t),
mode='lines',
name='cos(t)') # name of the trace for the legend
# a figure with a list of traces to be displayed
fig = go.Figure(data=[sin_plot, cos_plot])
fig.show()
Figure layout¶
So far we created figure objects using only one argument, data
, which specifies which traces are included in the figure. The second commonly used argument is layout
. It can be used to set the general properties of the figure: its title, width, height, the location of the legend etc.:
[7]:
fig = go.Figure(data=[sin_plot, cos_plot],
layout={'title': {'text': "Sine and cosine", # figure title properties
'font': {'size': 24,
'color': 'Green'}},
'legend' : {'title': {'text': "Legend"}, # legend properties
'y': 0},
'width': 750, # figure width
'height': 200}) # figure height
fig.show()
Nested dictionaries, such as these used above to specify layout properties, are commonly used in Plotly code. Internally Plotly represents each figure as such a dictionary. The fig.to_dict()
method returns a dictionary describing the figure. Here we apply it to the figure displaced above:
[8]:
fig.to_dict()
[8]:
{'data': [{'mode': 'lines',
'name': 'cos(t)',
'x': array([ 0. , 0.20408163, 0.40816327, 0.6122449 , 0.81632653,
1.02040816, 1.2244898 , 1.42857143, 1.63265306, 1.83673469,
2.04081633, 2.24489796, 2.44897959, 2.65306122, 2.85714286,
3.06122449, 3.26530612, 3.46938776, 3.67346939, 3.87755102,
4.08163265, 4.28571429, 4.48979592, 4.69387755, 4.89795918,
5.10204082, 5.30612245, 5.51020408, 5.71428571, 5.91836735,
6.12244898, 6.32653061, 6.53061224, 6.73469388, 6.93877551,
7.14285714, 7.34693878, 7.55102041, 7.75510204, 7.95918367,
8.16326531, 8.36734694, 8.57142857, 8.7755102 , 8.97959184,
9.18367347, 9.3877551 , 9.59183673, 9.79591837, 10. ]),
'y': array([ 0. , 0.20266794, 0.39692415, 0.57470604, 0.72863478,
0.85232157, 0.94063279, 0.98990308, 0.99808748, 0.96484631,
0.89155923, 0.78126802, 0.63855032, 0.46932961, 0.2806294 ,
0.08028167, -0.12339814, -0.32195632, -0.50715171, -0.67129779,
-0.80758169, -0.91034694, -0.97532829, -0.99982867, -0.9828312 ,
-0.92504137, -0.82885774, -0.6982724 , -0.53870529, -0.35677924,
-0.16004509, 0.04333173, 0.24491007, 0.43632343, 0.6096272 ,
0.75762842, 0.8741843 , 0.9544572 , 0.99511539, 0.99447137,
0.95255185, 0.8710967 , 0.75348673, 0.60460332, 0.43062587,
0.23877532, 0.0370144 , -0.16628279, -0.36267843, -0.54402111]),
'type': 'scatter'},
{'mode': 'lines',
'name': 'sin(t)',
'x': array([ 0. , 0.20408163, 0.40816327, 0.6122449 , 0.81632653,
1.02040816, 1.2244898 , 1.42857143, 1.63265306, 1.83673469,
2.04081633, 2.24489796, 2.44897959, 2.65306122, 2.85714286,
3.06122449, 3.26530612, 3.46938776, 3.67346939, 3.87755102,
4.08163265, 4.28571429, 4.48979592, 4.69387755, 4.89795918,
5.10204082, 5.30612245, 5.51020408, 5.71428571, 5.91836735,
6.12244898, 6.32653061, 6.53061224, 6.73469388, 6.93877551,
7.14285714, 7.34693878, 7.55102041, 7.75510204, 7.95918367,
8.16326531, 8.36734694, 8.57142857, 8.7755102 , 8.97959184,
9.18367347, 9.3877551 , 9.59183673, 9.79591837, 10. ]),
'y': array([ 1. , 0.97924752, 0.91785141, 0.81835992, 0.68490244,
0.52301811, 0.33942593, 0.1417459 , -0.0618173 , -0.26281476,
-0.45290412, -0.6241957 , -0.76958007, -0.88302305, -0.9598162 ,
-0.99677222, -0.99235724, -0.94675453, -0.8618568 , -0.74118774,
-0.58975572, -0.41384591, -0.22075945, -0.01851037, 0.18450698,
0.37986637, 0.55945943, 0.71583215, 0.84249428, 0.93418872,
0.98710971, 0.99906074, 0.9695458 , 0.8997899 , 0.79268826,
0.65268613, 0.48559429, 0.29834787, 0.09871854, -0.10500809,
-0.30437638, -0.49111153, -0.65746312, -0.79652673, -0.90253053,
-0.97107484, -0.99931473, -0.98607811, -0.93191435, -0.83907153]),
'type': 'scatter'}],
'layout': {'height': 200,
'legend': {'title': {'text': 'Legend'}, 'y': 0},
'title': {'font': {'color': 'Green', 'size': 24}, 'text': 'Sine and cosine'},
'width': 750,
'template': {'data': {'barpolar': [{'marker': {'line': {'color': '#E5ECF6',
'width': 0.5}},
'type': 'barpolar'}],
'bar': [{'error_x': {'color': '#2a3f5f'},
'error_y': {'color': '#2a3f5f'},
'marker': {'line': {'color': '#E5ECF6', 'width': 0.5}},
'type': 'bar'}],
'carpet': [{'aaxis': {'endlinecolor': '#2a3f5f',
'gridcolor': 'white',
'linecolor': 'white',
'minorgridcolor': 'white',
'startlinecolor': '#2a3f5f'},
'baxis': {'endlinecolor': '#2a3f5f',
'gridcolor': 'white',
'linecolor': 'white',
'minorgridcolor': 'white',
'startlinecolor': '#2a3f5f'},
'type': 'carpet'}],
'choropleth': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'type': 'choropleth'}],
'contourcarpet': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'type': 'contourcarpet'}],
'contour': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'colorscale': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']],
'type': 'contour'}],
'heatmapgl': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'colorscale': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']],
'type': 'heatmapgl'}],
'heatmap': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'colorscale': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']],
'type': 'heatmap'}],
'histogram2dcontour': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'colorscale': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']],
'type': 'histogram2dcontour'}],
'histogram2d': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'colorscale': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']],
'type': 'histogram2d'}],
'histogram': [{'marker': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'type': 'histogram'}],
'mesh3d': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'type': 'mesh3d'}],
'parcoords': [{'line': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'type': 'parcoords'}],
'pie': [{'automargin': True, 'type': 'pie'}],
'scatter3d': [{'line': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'marker': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'type': 'scatter3d'}],
'scattercarpet': [{'marker': {'colorbar': {'outlinewidth': 0,
'ticks': ''}},
'type': 'scattercarpet'}],
'scattergeo': [{'marker': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'type': 'scattergeo'}],
'scattergl': [{'marker': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'type': 'scattergl'}],
'scattermapbox': [{'marker': {'colorbar': {'outlinewidth': 0,
'ticks': ''}},
'type': 'scattermapbox'}],
'scatterpolargl': [{'marker': {'colorbar': {'outlinewidth': 0,
'ticks': ''}},
'type': 'scatterpolargl'}],
'scatterpolar': [{'marker': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'type': 'scatterpolar'}],
'scatter': [{'marker': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'type': 'scatter'}],
'scatterternary': [{'marker': {'colorbar': {'outlinewidth': 0,
'ticks': ''}},
'type': 'scatterternary'}],
'surface': [{'colorbar': {'outlinewidth': 0, 'ticks': ''},
'colorscale': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']],
'type': 'surface'}],
'table': [{'cells': {'fill': {'color': '#EBF0F8'},
'line': {'color': 'white'}},
'header': {'fill': {'color': '#C8D4E3'}, 'line': {'color': 'white'}},
'type': 'table'}]},
'layout': {'annotationdefaults': {'arrowcolor': '#2a3f5f',
'arrowhead': 0,
'arrowwidth': 1},
'autotypenumbers': 'strict',
'coloraxis': {'colorbar': {'outlinewidth': 0, 'ticks': ''}},
'colorscale': {'diverging': [[0, '#8e0152'],
[0.1, '#c51b7d'],
[0.2, '#de77ae'],
[0.3, '#f1b6da'],
[0.4, '#fde0ef'],
[0.5, '#f7f7f7'],
[0.6, '#e6f5d0'],
[0.7, '#b8e186'],
[0.8, '#7fbc41'],
[0.9, '#4d9221'],
[1, '#276419']],
'sequential': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']],
'sequentialminus': [[0.0, '#0d0887'],
[0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'],
[0.3333333333333333, '#9c179e'],
[0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'],
[0.6666666666666666, '#ed7953'],
[0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'],
[1.0, '#f0f921']]},
'colorway': ['#636efa',
'#EF553B',
'#00cc96',
'#ab63fa',
'#FFA15A',
'#19d3f3',
'#FF6692',
'#B6E880',
'#FF97FF',
'#FECB52'],
'font': {'color': '#2a3f5f'},
'geo': {'bgcolor': 'white',
'lakecolor': 'white',
'landcolor': '#E5ECF6',
'showlakes': True,
'showland': True,
'subunitcolor': 'white'},
'hoverlabel': {'align': 'left'},
'hovermode': 'closest',
'mapbox': {'style': 'light'},
'paper_bgcolor': 'white',
'plot_bgcolor': '#E5ECF6',
'polar': {'angularaxis': {'gridcolor': 'white',
'linecolor': 'white',
'ticks': ''},
'bgcolor': '#E5ECF6',
'radialaxis': {'gridcolor': 'white', 'linecolor': 'white', 'ticks': ''}},
'scene': {'xaxis': {'backgroundcolor': '#E5ECF6',
'gridcolor': 'white',
'gridwidth': 2,
'linecolor': 'white',
'showbackground': True,
'ticks': '',
'zerolinecolor': 'white'},
'yaxis': {'backgroundcolor': '#E5ECF6',
'gridcolor': 'white',
'gridwidth': 2,
'linecolor': 'white',
'showbackground': True,
'ticks': '',
'zerolinecolor': 'white'},
'zaxis': {'backgroundcolor': '#E5ECF6',
'gridcolor': 'white',
'gridwidth': 2,
'linecolor': 'white',
'showbackground': True,
'ticks': '',
'zerolinecolor': 'white'}},
'shapedefaults': {'line': {'color': '#2a3f5f'}},
'ternary': {'aaxis': {'gridcolor': 'white',
'linecolor': 'white',
'ticks': ''},
'baxis': {'gridcolor': 'white', 'linecolor': 'white', 'ticks': ''},
'bgcolor': '#E5ECF6',
'caxis': {'gridcolor': 'white', 'linecolor': 'white', 'ticks': ''}},
'title': {'x': 0.05},
'xaxis': {'automargin': True,
'gridcolor': 'white',
'linecolor': 'white',
'ticks': '',
'title': {'standoff': 15},
'zerolinecolor': 'white',
'zerolinewidth': 2},
'yaxis': {'automargin': True,
'gridcolor': 'white',
'linecolor': 'white',
'ticks': '',
'title': {'standoff': 15},
'zerolinecolor': 'white',
'zerolinewidth': 2},
'margin': {'b': 60, 'l': 30, 'r': 30, 't': 60}}}}}
Underscore Notation¶
Specifying properties of traces and figures using nested dictionaries can be cumbersome. We can often avoid it by using Plotly’s underscore notation. This notation lets us denote by dict_k
the value of the dictionary dict
assigned to the key k
. For example, using this notation the assignment
layout = {'width' : 750, 'height' : 200}
is equivalent to:
layout_width = 750
layout_height = 200
The underscore notation can be used recursively with nested dictionaries. In this way the code
layout = {'title': {'text' : "Sine and cosine",
'font' : {'size' : 24, 'color' : 'Green'}}
can be rewritten as follows:
layout_title_text = "Sine and cosine"
layout_title_font_size = 24
layout_title_font_color = 'Green'
The code below creates the same plot as the one in the last example, but uses the underscore notation in place of nested dictionaries:
[8]:
fig = go.Figure(data=[sin_plot, cos_plot],
layout_title_text= "Sine and cosine",
layout_title_font_size=24,
layout_title_font_color='Green',
layout_legend_title_text="Legend",
layout_legend_y=0,
layout_width=750,
layout_height=200)
fig.show()
Updating layout¶
The fig.update_layout()
method lets us modify figure layout after the figure has been created. We can use it with either dictionaries or the underscore notation. Since all arguments of this method apply to the layout, the layout_
part of the underscore notation is omitted:
[9]:
# create a figure
fig = go.Figure(data=[sin_plot, cos_plot],
layout_title_text="Sine and cosine",
layout_title_font_size=24,
layout_title_font_color='Green',
layout_width=750,
layout_height=200)
# modify some title properties
fig.update_layout(title_text="New title", title_font_color="Red")
fig.show()
Updating traces¶
The method fig.update_traces()
can be used to update traces embedded in an existing figure.
[10]:
t = np.linspace(0, 10, 30)
cos_plot = go.Scatter(x=t, y=np.cos(t),
mode='lines',
line_dash='dash', # dashed line
line_color="Blue")
sin_plot = go.Scatter(x=t, y=np.sin(t),
mode='lines',
line_color="Red")
# create a figure
fig = go.Figure(data=[sin_plot, cos_plot],
layout_width=750,
layout_height=200)
# modify line width and color
fig.update_traces(line_color="Green", line_width=4)
fig.show()
By default, fig.update_traces()
modifies to all figure traces. To get more control, we can use it with a selector
argument. When this argument is assigned to a dictionary, only traces which have properties corresponding to the dictionary keys with values equal to the respective dictionary values will be updated:
[11]:
# create a figure
fig = go.Figure(data=[sin_plot, cos_plot],
layout_width=750,
layout_height=200)
fig.update_traces(line_color="Green",
line_width=4,
selector={'line_dash': 'dash'}) # modify dashed lines only
fig.show()
Subplots¶
The fig.add_trace()
method can be used to add a trace to an existing figure:
[12]:
# create a figure
fig = go.Figure(
layout_width=350,
layout_height=350,
layout_yaxis_scaleanchor="x", # set scaleanchor="x" and scaleratio=1
layout_yaxis_scaleratio=1, # to plot x and y axes using the same scale
layout_showlegend=False, # do not show legend
layout_title_text="Circles")
# create traces
t = np.linspace(0, 2 * np.pi, 300)
circle1 = go.Scatter(x=np.sin(t), y=np.cos(t), mode='lines', line_color='Red')
circle2 = go.Scatter(x=0.7*np.sin(t), y=0.7*np.cos(t), mode='lines', line_color='Green')
circle3 = go.Scatter(x=0.4*np.sin(t), y=0.4*np.cos(t), mode='lines', line_color='Blue')
# add traces to the figure
fig.add_trace(circle1)
fig.add_trace(circle2)
fig.add_trace(circle3)
fig.show()
fig.add_trace()
is especially useful when we want to create a figure consisting of subplots:
[13]:
from plotly.subplots import make_subplots
# create a figure with subplots
fig = make_subplots(
rows=2, # number of rows with subplots
cols=2, # number of columns
subplot_titles=(
'sin(t)', # list of subplot titles
"sin(2t)",
"sin(3t)",
"sin(4t)"))
# make_subplots does not accept the layout argument, but we can
# use fig.update_layout() to modify the layout of the figure
fig.update_layout(width=700, height=400,
showlegend=False) # do not show legend
# create traces
t = np.linspace(-3, 3, 100)
plot1 = go.Scatter(x=t, y=np.sin(t), mode="lines")
plot2 = go.Scatter(x=t, y=np.sin(2*t), mode="lines")
plot3 = go.Scatter(x=t, y=np.sin(3*t), mode="lines")
plot4 = go.Scatter(x=t, y=np.sin(4*t), mode="lines")
# add traces to subplots
# subplot rows and columns are numbered starting with 1, not 0
fig.add_trace(plot1, row=1, col=1)
fig.add_trace(plot2, row=1, col=2)
fig.add_trace(plot3, row=2, col=1)
fig.add_trace(plot4, row=2, col=2)
fig.show()
Templates¶
Plotly comes with several theme templates which modify the overall look of a figure. A theme can be specified using the template
key in the figure layout.
[14]:
t = np.linspace(0, 10, 40)
cos_plot = go.Scatter(x=t, y=np.cos(t), mode='lines', name='sin(t)')
sin_plot = go.Scatter(x=t, y=np.sin(t), mode='lines', name='cos(t)')
fig = go.Figure(
data=[sin_plot, cos_plot],
layout_width=750,
layout_height=200,
layout_margin_l=10, # left margin of the figure
layout_margin_r=10, # right margin
layout_margin_t=60, # top margin
layout_margin_b=30, # bottom margin
layout_template="ggplot2", # select a template
layout_title_text="Theme:\"ggplot2\"")
fig.show()
[15]:
fig.update_layout(template="seaborn", title_text="Theme:\"seaborn\"")
fig.show()
[16]:
fig.update_layout(template="plotly_white", title_text="Theme:\"plotly_white\"")
fig.show()
[17]:
fig.update_layout(template="plotly_dark", title_text="Theme:\"plotly_dark\"")
fig.show()