Is there a simple way to hide the repeated axis titles in a faceted chart using plotly express? I tried setting
visible=True
In the code below, but that also hid the y axis tick labels (the values). Ideally, I would like to set hiding the repeated axes titles a default for faceted plots in general (or even better, just defaulting to showing a single x and y axis title for the entire faceted figure.
Here is the test code:
import pandas as pd
import numpy as np
import plotly.express as px
import string
# create a dataframe
cols = list(string.ascii_letters)
n = 50
df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})
# create data with vastly different ranges
for col in cols:
start = np.random.choice([1, 10, 100, 1000, 100000])
s = np.random.normal(loc=0, scale=0.01*start, size=n)
df[col] = start + s.cumsum()
# melt data columns from wide to long
dfm = df.melt("Date")
fig = px.line(
data_frame=dfm,
x = 'Date',
y = 'value',
facet_col = 'variable',
facet_col_wrap=6,
facet_col_spacing=0.05,
facet_row_spacing=0.035,
height = 1000,
width = 1000,
title = 'Value vs. Date'
)
fig.update_yaxes(matches=None, showticklabels=True, visible=True)
fig.update_annotations(font=dict(size=16))
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))
Final Code (accepted answer). Note plotly >= 4.9
import pandas as pd
import numpy as np
import plotly.express as px
import string
import plotly.graph_objects as go
# create a dataframe
cols = list(string.ascii_letters)
n = 50
df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})
# create data with vastly different ranges
for col in cols:
start = np.random.choice([1, 10, 100, 1000, 100000])
s = np.random.normal(loc=0, scale=0.01*start, size=n)
df[col] = start + s.cumsum()
# melt data columns from wide to long
dfm = df.melt("Date")
fig = px.line(
data_frame=dfm,
x = 'Date',
y = 'value',
facet_col = 'variable',
facet_col_wrap=6,
facet_col_spacing=0.05,
facet_row_spacing=0.035,
height = 1000,
width = 1000,
title = 'Value vs. Date'
)
fig.update_yaxes(matches=None, showticklabels=True, visible=True)
fig.update_annotations(font=dict(size=16))
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))
# hide subplot y-axis titles and x-axis titles
for axis in fig.layout:
if type(fig.layout[axis]) == go.layout.YAxis:
fig.layout[axis].title.text = ''
if type(fig.layout[axis]) == go.layout.XAxis:
fig.layout[axis].title.text = ''
# keep all other annotations and add single y-axis and x-axis title:
fig.update_layout(
# keep the original annotations and add a list of new annotations:
annotations = list(fig.layout.annotations) +
[go.layout.Annotation(
x=-0.07,
y=0.5,
font=dict(
size=16, color = 'blue'
),
showarrow=False,
text="single y-axis title",
textangle=-90,
xref="paper",
yref="paper"
)
] +
[go.layout.Annotation(
x=0.5,
y=-0.08,
font=dict(
size=16, color = 'blue'
),
showarrow=False,
text="Dates",
textangle=-0,
xref="paper",
yref="paper"
)
]
)
fig.show()
As a side note on this, I found an even more direct way to eliminate the axis labels from within the plotly express call using the labels argument and providing it with a dict of labels with values of '' for the ones I wanted to eliminate.
This does not result in a single label at the overall figure level though, but if the figure title is descriptive enough of "Y vs. X", then maybe the lack of axis labels can be "excused"? ( or added in as @vestland demonstrated )
Note, you can "almost" eliminate the annoying repeated facet titles that have "=value" in every single sublot. i.e. if you add one more entry to the labels dict:
'variable': ''
then instead of getting "variable=variable level", you just get the the facet variable level, preceded by the "=" as in the chart below.
Full Code
import pandas as pd
import numpy as np
import plotly.express as px
import string
# create a dataframe
cols = list(string.ascii_letters)
n = 50
df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})
# create data with vastly different ranges
for col in cols:
start = np.random.choice([1, 10, 100, 1000, 100000])
s = np.random.normal(loc=0, scale=0.01*start, size=n)
df[col] = start + s.cumsum()
# melt data columns from wide to long
dfm = df.melt("Date")
# make the plot
fig = px.line(
data_frame=dfm,
x = 'Date',
y = 'value',
facet_col = 'variable',
facet_col_wrap=6,
facet_col_spacing=0.05,
facet_row_spacing=0.035,
height = 1000,
width = 1000,
title = 'Value vs. Date',
labels = {
'Date': '',
'value': '',
'variable': ''
}
)
# ensure that each chart has its own y rage and tick labels
fig.update_yaxes(matches=None, showticklabels=True, visible=True)
fig.show()
fig.layout[axis].tickfont = dict(color = 'rgba(0,0,0,0)')
go.layout.Annotation(xref="paper", yref="paper")
One very important take-away here is that you can edit any element produced with a px
function using plotly.graph_object
references, like go.layout.XAxis
.
If you're otherwise happy with the way you've set up your fig
, you can just include
for anno in fig['layout']['annotations']:
anno['text']=''
fig.show()
You can set the yaxis tickfont to transparent using the following in a loop
fig.layout[axis].tickfont = dict(color = 'rgba(0,0,0,0)')
That exact line is included in the snippet below that also removes y-axis title for every subplot.
The removal of axis labels and inclusion of a single label requires a bit more work, but here's a very flexible setup that does exactly what you need and more if you'd like to edit your new labels in any way:
# hide subplot y-axis titles and x-axis titles
for axis in fig.layout:
if type(fig.layout[axis]) == go.layout.YAxis:
fig.layout[axis].title.text = ''
if type(fig.layout[axis]) == go.layout.XAxis:
fig.layout[axis].title.text = ''
# keep all other annotations and add single y-axis and x-axis title:
fig.update_layout(
# keep the original annotations and add a list of new annotations:
annotations = list(fig.layout.annotations) +
[go.layout.Annotation(
x=-0.07,
y=0.5,
font=dict(
size=16, color = 'blue'
),
showarrow=False,
text="single y-axis title",
textangle=-90,
xref="paper",
yref="paper"
)
] +
[go.layout.Annotation(
x=0.5,
y=-0.08,
font=dict(
size=16, color = 'blue'
),
showarrow=False,
text="Dates",
textangle=-0,
xref="paper",
yref="paper"
)
]
)
fig.show()
import pandas as pd
import numpy as np
import plotly.express as px
import string
import plotly.graph_objects as go
# create a dataframe
cols = list(string.ascii_letters)
cols[0]='zzz'
n = 50
df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})
# create data with vastly different ranges
for col in cols:
start = np.random.choice([1, 10, 100, 1000, 100000])
s = np.random.normal(loc=0, scale=0.01*start, size=n)
df[col] = start + s.cumsum()
# melt data columns from wide to long
dfm = df.melt("Date")
fig = px.line(
data_frame=dfm,
x = 'Date',
y = 'value',
facet_col = 'variable',
facet_col_wrap=6,
#facet_col_spacing=0.05,
#facet_row_spacing=0.035,
height = 1000,
width = 1000,
title = 'Value vs. Date'
)
fig.update_yaxes(matches=None, showticklabels=True, visible=True)
fig.update_annotations(font=dict(size=16))
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))
# subplot titles
for anno in fig['layout']['annotations']:
anno['text']=''
# hide subplot y-axis titles and x-axis titles
for axis in fig.layout:
if type(fig.layout[axis]) == go.layout.YAxis:
fig.layout[axis].title.text = ''
if type(fig.layout[axis]) == go.layout.XAxis:
fig.layout[axis].title.text = ''
# keep all other annotations and add single y-axis and x-axis title:
fig.update_layout(
# keep the original annotations and add a list of new annotations:
annotations = list(fig.layout.annotations) +
[go.layout.Annotation(
x=-0.07,
y=0.5,
font=dict(
size=16, color = 'blue'
),
showarrow=False,
text="single y-axis title",
textangle=-90,
xref="paper",
yref="paper"
)
] +
[go.layout.Annotation(
x=0.5,
y=-0.08,
font=dict(
size=16, color = 'blue'
),
showarrow=False,
text="Dates",
textangle=-0,
xref="paper",
yref="paper"
)
]
)
fig.show()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With