jupyter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
As a general rule, there are two ways to add text labels to figures:
- Certain trace types, notably in the
scatter
family (e.g.scatter
,scatter3d
,scattergeo
etc), support atext
attribute, and can be displayed with or without markers. - Standalone text annotations can be added to figures using
fig.add_annotation()
, with or without arrows, and they can be positioned absolutely within the figure, or they can be positioned relative to the axes of 2d or 3d cartesian subplots i.e. in data coordinates.
The differences between these two approaches are that:
- Traces can optionally support hover labels and can appear in legends.
- Text annotations can be positioned absolutely or relative to data coordinates in 2d/3d cartesian subplots only.
- Traces cannot be positioned absolutely but can be positioned relative to data coordinates in any subplot type.
- Traces also be used to draw shapes, although there is a shape equivalent to text annotations.
Here is an example that creates a scatter plot with text labels using Plotly Express.
import plotly.express as px
df = px.data.gapminder().query("year==2007 and continent=='Americas'")
fig = px.scatter(df, x="gdpPercap", y="lifeExp", text="country", log_x=True, size_max=60)
fig.update_traces(textposition='top center')
fig.update_layout(
height=800,
title_text='GDP and Life Expectancy (Americas, 2007)'
)
fig.show()
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(
x=[0, 1, 2],
y=[1, 1, 1],
mode="lines+markers+text",
name="Lines, Markers and Text",
text=["Text A", "Text B", "Text C"],
textposition="top center"
))
fig.add_trace(go.Scatter(
x=[0, 1, 2],
y=[2, 2, 2],
mode="markers+text",
name="Markers and Text",
text=["Text D", "Text E", "Text F"],
textposition="bottom center"
))
fig.add_trace(go.Scatter(
x=[0, 1, 2],
y=[3, 3, 3],
mode="lines+text",
name="Lines and Text",
text=["Text G", "Text H", "Text I"],
textposition="bottom center"
))
fig.show()
Dash is the best way to build analytical apps in Python using Plotly figures. To run the app below, run pip install dash
, click "Download" to get the code and run python app.py
.
Get started with the official Dash docs and learn how to effortlessly style & deploy apps like this with Dash Enterprise.
from IPython.display import IFrame
snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'
IFrame(snippet_url + 'text-and-annotations', width='100%', height=1200)
Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture. Join now.
For the pie, bar-like, sunburst and treemap traces, it is possible to force all the text labels to have the same size thanks to the uniformtext
layout parameter. The minsize
attribute sets the font size, and the mode
attribute sets what happens for labels which cannot fit with the desired fontsize: either hide
them or show
them with overflow.
Here is a bar chart with the default behavior which will scale down text to fit.
import plotly.express as px
df = px.data.gapminder(year=2007)
fig = px.bar(df, x='continent', y='pop', color="lifeExp", text='country',
title="Default behavior: some text is tiny")
fig.update_traces(textposition='inside')
fig.show()
Here is the same figure with uniform text applied: the text for all bars is the same size, with a minimum size of 8. Any text at the minimum size which does not fit in the bar is hidden.
import plotly.express as px
df = px.data.gapminder(year=2007)
fig = px.bar(df, x='continent', y='pop', color="lifeExp", text='country',
title="Uniform Text: min size is 8, hidden if can't fit")
fig.update_traces(textposition='inside')
fig.update_layout(uniformtext_minsize=8, uniformtext_mode='hide')
fig.show()
import plotly.express as px
df = px.data.gapminder().query("continent == 'Asia' and year == 2007")
fig = px.pie(df, values='pop', names='country')
fig.update_traces(textposition='inside')
fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')
fig.show()
The textfont_size
parameter of the the pie, bar-like, sunburst and treemap traces can be used to set the maximum font size used in the chart. Note that the textfont
parameter sets the insidetextfont
and outsidetextfont
parameter, which can also be set independently.
import plotly.express as px
df = px.data.gapminder().query("continent == 'Asia' and year == 2007")
fig = px.pie(df, values='pop', names='country')
fig.update_traces(textposition='inside', textfont_size=14)
fig.show()
Annotations can be added to a figure using fig.add_annotation()
.
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(
x=[0, 1, 2, 3, 4, 5, 6, 7, 8],
y=[0, 1, 3, 2, 4, 3, 4, 6, 5]
))
fig.add_trace(go.Scatter(
x=[0, 1, 2, 3, 4, 5, 6, 7, 8],
y=[0, 4, 5, 1, 2, 2, 3, 4, 2]
))
fig.add_annotation(x=2, y=5,
text="Text annotation with arrow",
showarrow=True,
arrowhead=1)
fig.add_annotation(x=4, y=4,
text="Text annotation without arrow",
showarrow=False,
yshift=10)
fig.update_layout(showlegend=False)
fig.show()
If the x
or y
positions of an annotation reference a log axis, you need to provide that position as a log10
value when adding the annotation. In this example, the yaxis
is a log axis so we pass the log10
value of 1000
to the annotation's y
position.
import plotly.graph_objects as go
import math
dates = [
"2024-01-01",
"2024-01-02",
"2024-01-03",
"2024-01-04",
"2024-01-05",
"2024-01-06",
]
y_values = [1, 30, 70, 100, 1000, 10000000]
fig = go.Figure(
data=[go.Scatter(x=dates, y=y_values, mode="lines+markers")],
layout=go.Layout(
yaxis=dict(
type="log",
)
),
)
fig.add_annotation(
x="2024-01-05",
y=math.log10(1000),
text="Log axis annotation",
showarrow=True,
xanchor="right",
)
fig.show()
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter3d(
x=["2017-01-01", "2017-02-10", "2017-03-20"],
y=["A", "B", "C"],
z=[1, 1000, 100000],
name="z",
))
fig.update_layout(
scene=dict(
xaxis=dict(type="date"),
yaxis=dict(type="category"),
zaxis=dict(type="log"),
annotations=[
dict(
showarrow=False,
x="2017-01-01",
y="A",
z=0,
text="Point 1",
xanchor="left",
xshift=10,
opacity=0.7),
dict(
x="2017-02-10",
y="B",
z=4,
text="Point 2",
textangle=0,
ax=0,
ay=-75,
font=dict(
color="black",
size=12
),
arrowcolor="black",
arrowsize=3,
arrowwidth=1,
arrowhead=1),
dict(
x="2017-03-20",
y="C",
z=5,
ax=50,
ay=0,
text="Point 3",
arrowhead=1,
xanchor="left",
yanchor="bottom"
)]
),
)
fig.show()
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(
x=[0, 1, 2],
y=[1, 1, 1],
mode="lines+markers+text",
name="Lines, Markers and Text",
text=["Text A", "Text B", "Text C"],
textposition="top right",
textfont=dict(
family="sans serif",
size=18,
color="crimson"
)
))
fig.add_trace(go.Scatter(
x=[0, 1, 2],
y=[2, 2, 2],
mode="lines+markers+text",
name="Lines and Text",
text=["Text G", "Text H", "Text I"],
textposition="bottom center",
textfont=dict(
family="sans serif",
size=18,
color="LightSeaGreen"
)
))
fig.update_layout(showlegend=False)
fig.show()
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(
x=[0, 1, 2, 3, 4, 5, 6, 7, 8],
y=[0, 1, 3, 2, 4, 3, 4, 6, 5]
))
fig.add_trace(go.Scatter(
x=[0, 1, 2, 3, 4, 5, 6, 7, 8],
y=[0, 4, 5, 1, 2, 2, 3, 4, 2]
))
fig.add_annotation(
x=2,
y=5,
xref="x",
yref="y",
text="max=5",
showarrow=True,
font=dict(
family="Courier New, monospace",
size=16,
color="#ffffff"
),
align="center",
arrowhead=2,
arrowsize=1,
arrowwidth=2,
arrowcolor="#636363",
ax=20,
ay=-30,
bordercolor="#c7c7c7",
borderwidth=2,
borderpad=4,
bgcolor="#ff7f0e",
opacity=0.8
)
fig.update_layout(showlegend=False)
fig.show()
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scattergeo(
lat=[45.5, 43.4, 49.13, 51.1, 53.34, 45.24, 44.64, 48.25, 49.89, 50.45],
lon=[-73.57, -79.24, -123.06, -114.1, -113.28, -75.43, -63.57, -123.21, -97.13,
-104.6],
marker={
"color": ["MidnightBlue", "IndianRed", "MediumPurple", "Orange", "Crimson",
"LightSeaGreen", "RoyalBlue", "LightSalmon", "DarkOrange", "MediumSlateBlue"],
"line": {
"width": 1
},
"size": 10
},
mode="markers+text",
name="",
text=["Montreal", "Toronto", "Vancouver", "Calgary", "Edmonton", "Ottawa",
"Halifax",
"Victoria", "Winnepeg", "Regina"],
textfont={
"color": ["MidnightBlue", "IndianRed", "MediumPurple", "Gold", "Crimson",
"LightSeaGreen",
"RoyalBlue", "LightSalmon", "DarkOrange", "MediumSlateBlue"],
"family": ["Arial, sans-serif", "Balto, sans-serif", "Courier New, monospace",
"Droid Sans, sans-serif", "Droid Serif, serif",
"Droid Sans Mono, sans-serif",
"Gravitas One, cursive", "Old Standard TT, serif",
"Open Sans, sans-serif",
"PT Sans Narrow, sans-serif", "Raleway, sans-serif",
"Times New Roman, Times, serif"],
"size": [22, 21, 20, 19, 18, 17, 16, 15, 14, 13]
},
textposition=["top center", "middle left", "top center", "bottom center",
"top right",
"middle left", "bottom right", "bottom left", "top right",
"top right"]
))
fig.update_layout(
title_text="Canadian cities",
geo=dict(
lataxis=dict(range=[40, 70]),
lonaxis=dict(range=[-130, -55]),
scope="north america"
)
)
fig.show()
By default, text annotations have xref
and yref
set to "x"
and "y"
, respectively, meaning that their x/y coordinates are with respect to the axes of the plot. This means that panning the plot will cause the annotations to move. Setting xref
and/or yref
to "paper"
will cause the x
and y
attributes to be interpreted in paper coordinates.
Try panning or zooming in the following figure:
import plotly.express as px
fig = px.scatter(x=[1, 2, 3], y=[1, 2, 3], title="Try panning or zooming!")
fig.add_annotation(text="Absolutely-positioned annotation",
xref="paper", yref="paper",
x=0.3, y=0.3, showarrow=False)
fig.show()
To place annotations relative to the length or height of an axis, the string
' domain'
can be added after the axis reference in the xref
or yref
fields.
For example:
import plotly.express as px
import plotly.graph_objects as go
df = px.data.wind()
fig = px.scatter(df, y="frequency")
# Set a custom domain to see how the ' domain' string changes the behaviour
fig.update_layout(xaxis=dict(domain=[0, 0.5]), yaxis=dict(domain=[0.25, 0.75]))
fig.add_annotation(
xref="x domain",
yref="y domain",
# The arrow head will be 25% along the x axis, starting from the left
x=0.25,
# The arrow head will be 40% along the y axis, starting from the bottom
y=0.4,
text="An annotation referencing the axes",
arrowhead=2,
)
fig.show()
The text coordinates / dimensions of the arrow can be specified absolutely, as long as they use exactly the same coordinate system as the arrowhead. For example:
import plotly.express as px
import plotly.graph_objects as go
df = px.data.wind()
fig = px.scatter(df, y="frequency")
fig.update_layout(xaxis=dict(domain=[0, 0.5]), yaxis=dict(domain=[0.25, 0.75]))
fig.add_annotation(
xref="x domain",
yref="y",
x=0.75,
y=1,
text="An annotation whose text and arrowhead reference the axes and the data",
# If axref is exactly the same as xref, then the text's position is
# absolute and specified in the same coordinates as xref.
axref="x domain",
# The same is the case for yref and ayref, but here the coordinates are data
# coordinates
ayref="y",
ax=0.5,
ay=2,
arrowhead=2,
)
fig.show()
To show an arbitrary text in your chart you can use texttemplate, which is a template string used for rendering the information, and will override textinfo.
This template string can include variables
in %{variable} format, numbers
in d3-format's syntax, and date
in d3-time-format's syntax.
texttemplate
customizes the text that appears on your plot vs. hovertemplate that customizes the tooltip text.
import plotly.graph_objects as go
fig = go.Figure(go.Pie(
values = [40000000, 20000000, 30000000, 10000000],
labels = ["Wages", "Operating expenses", "Cost of sales", "Insurance"],
texttemplate = "%{label}: %{value:$,s} <br>(%{percent})",
textposition = "inside"))
fig.show()
The following example uses textfont to customize the added text.
import plotly.graph_objects as go
fig = go.Figure(go.Scatterternary(
a = [3, 2, 5],
b = [2, 5, 2],
c = [5, 2, 2],
mode = "markers+text",
text = ["A", "B", "C"],
texttemplate = "%{text}<br>(%{a:.2f}, %{b:.2f}, %{c:.2f})",
textposition = "bottom center",
textfont = {'family': "Times", 'size': [18, 21, 20], 'color': ["IndianRed", "MediumPurple", "DarkOrange"]}
))
fig.show()
The following example shows how to show date by setting axis.type in funnel charts. As you can see textinfo and texttemplate have the same functionality when you want to determine 'just' the trace information on the graph.
from plotly import graph_objects as go
fig = go.Figure()
fig.add_trace(go.Funnel(
name = 'Montreal',
orientation = "h",
y = ["2018-01-01", "2018-07-01", "2019-01-01", "2020-01-01"],
x = [100, 60, 40, 20],
textposition = "inside",
texttemplate = "%{y| %a. %_d %b %Y}"))
fig.add_trace(go.Funnel(
name = 'Vancouver',
orientation = "h",
y = ["2018-01-01", "2018-07-01", "2019-01-01", "2020-01-01"],
x = [90, 70, 50, 10],
textposition = "inside",
textinfo = "label"))
fig.update_layout(yaxis = {'type': 'date'})
fig.show()
See https://plotly.com/python/reference/layout/annotations/ for more information and chart attribute options!