这是我经常遇到的问题。我有一个带有列和行面的图形。我已经使用
fig.update_yaxes(matches=None)
取消了 y 轴的链接。然而,虽然这对于缩放第 1 行和第 2 行之间的轴很有用,因为它们存在于完全不同的域中,但它破坏了在列方面之间进行比较的能力。您可以在下图中看到这个问题:
所以我的问题是,如何在每行的所有列面上具有相同的 y 轴,而第 1 行和第 2 行具有不同的 y 轴?
为了确保
row-wise matching
,您必须为第一行指定以下内容:
fig.layout.yaxis.matches = 'y'
fig.layout.yaxis2.matches = 'y'
fig.layout.yaxis3.matches = 'y'
这是第二个:
fig.layout.yaxis4.matches = 'y4'
fig.layout.yaxis5.matches = 'y4'
fig.layout.yaxis6.matches = 'y4'
如您所见,所有 y 轴都与每个相应行的第一个 y 轴相关。
对于那些想尝试一下的人,这里有一个基于 facet 图
的示例import plotly.express as px
df = px.data.gapminder()
fig = px.scatter(df, x='gdpPercap', y='lifeExp', color='continent', size='pop',
facet_col='year', facet_col_wrap=4)
fig.layout.yaxis.matches = 'y'
fig.layout.yaxis2.matches = 'y'
fig.layout.yaxis3.matches = 'y'
fig.layout.yaxis4.matches = 'y'
fig.layout.yaxis5.matches = 'y5'
fig.layout.yaxis7.matches = 'y5'
fig.layout.yaxis6.matches = 'y5'
fig.layout.yaxis8.matches = 'y5'
fig.layout.yaxis9.matches = 'y9'
fig.layout.yaxis10.matches = 'y9'
fig.layout.yaxis11.matches = 'y9'
fig.layout.yaxis12.matches = 'y9'
fig.show()
nrows = df.row_var.nunique() # or find a way to get number of rows from fig object..
for i in range(0,nrows):
fig.update_yaxes(showticklabels=True, matches=f'y{i+1}', col=i+1)
https://github.com/plotly/plotly_express/issues/147#issuecomment-537814046
由于我经常遇到这种需求,所以我将不同的解决方案压缩为一个函数并为大家记录下来,使其可以在(代码)框中使用(Python>=3.10):
from typing import List, Tuple, TypeAlias
import plotly.express as px
import plotly.graph_objects as go
from plotly._subplots import SubplotRef
grid_ref_: TypeAlias = List[List[Tuple[SubplotRef]]]
def realign_subplot_axes(
fig: go.Figure,
x_axes: bool | dict = False,
y_axes: bool | dict = False
) -> None:
"""For a given plotly figure, allow all x-axes (column-wise) and/or y-axes (row-wise) to have
their own ranges. The function operates in-place, modifying the figure without returning it.
Args:
fig: The plotly figure to be modified.
x_axes, y_axes:
If True, the respective axes will be realigned.
If a dictionary, they will additionally be updated with the specified settings.
Examples:
realign_subplot_axes(fig, y_axes=True)
realign_subplot_axes(fig, x_axes=dict(showticklabels=True))
"""
if not (x_axes or y_axes):
return
subplot_rows: grid_ref_ = fig._grid_ref # a list of lists, indicative of the grid dimensions
n_cols: int = len(subplot_rows[0]) # needed in either case
if x_axes:
x_settings = x_axes if isinstance(x_axes, dict) else {}
for col in range(1, n_cols+1):
# set the layout.xaxis.matches property of all subplots in the same column to the
# x-axis name of the first subplot in that column. 'x1' is accepted for 'x' (the very first).
fig.update_xaxes(x_settings, col=col, matches=f"x{col}")
if y_axes:
y_settings = y_axes if isinstance(y_axes, dict) else None
n_rows = len(subplot_rows)
for row in range(1, n_rows+1):
# set the layout.yaxis.matches property of all subplots in the same row to the
# y-axis name of the first subplot in that row. Y-axis names are numbered row-wise,
# such that the first y-axis in the second row is 'y3' (if there are 2 columns).
first_y = (row-1) * n_cols + 1
fig.update_yaxes(y_settings, row=row, matches=f"y{first_y}")
df = px.data.tips()
fig = px.scatter(
df,
x="total_bill",
y="tip",
color='time',
facet_col="sex",
facet_row="day"
)
fig.write_image("default.png")
realign_subplot_axes(fig, y_axes=True)
fig.write_image("y_axes.png")
realign_subplot_axes(fig, x_axes=dict(showticklabels=True))
fig.write_image("both_axes.png")
default.png 所有绘图共享相同的范围 y_axes.png 行共享 y 轴范围 both_axes.png 列共享 x 轴范围并显示所有 x 刻度