Plotly:如何在同一个子图中显示超过 2 x-axes titles/ranges?

Plotly: How to show more than 2 x-axes titles/ranges on the same subplot?

我正在使用 Plotly 并使用共享的 y-axis 和不同的 x-axes 制作散点图子图。我试图使用图形 object (fig['layout'][数据索引]) 语法来显示多个堆叠的 x-axes 及其各自的范围。通过将 'top' 和 'bottom' 分配给图形布局的 side 属性,我只成功地在每个子图中显示了两个 xaxes 和范围。下图中右起第二列应该显示系列 T5、T6 和 T7 的 titles/ranges,但只显示 T5 和 T7 的标题和范围。

是否可以在 Plotly 中的同一个子图中显示超过 2 个 x-axes title/ranges? For an implemented example, Matplotlib supports showing multiple stacked axes

感谢 Vestland,关键是使用图形布局的位置属性并缩放 y-axis 以正确适应调整。基于 Vestland 示例代码的多轴的完整实现,请参见下面的 [monstrosity]。

您需要 make_subplots(rows=1, cols=2)add_traces()fig.update_layout(xaxis=dict(domain=...) 的精确组合:

  1. 使用 fig=make_subplots(rows=1, cols=2) 设置一个“常规”子图,并按照 here.

    所述包含两条轨迹
  2. 使用 fig.add_trace(go.Scatter([...[, xaxis="x3"))

    添加具有自己的 x 轴的第三条轨迹
  3. 然后,调整子图 1 为 xaxis3 腾出空间,使用:fig.update_layout(xaxis3=dict(anchor="free", overlaying="x1", position=0.0))

  4. 使用 fig.update_layout([...], yaxis2=dict(domain=[0.1, 1]))

    进行一些最终调整

之所以要考虑domain,是因为point 3中的position属性不能为负,必须腾出空间对于双 x-axes 不知何故。结果如下:

情节

完整代码:

from plotly.subplots import make_subplots
import plotly.graph_objects as go

# initial subplot with two traces
fig = make_subplots(rows=1, cols=2)

fig.add_trace(
    go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),
    row=1, col=1
)

fig.add_trace(
    go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),
    row=1, col=2
)

fig.update_layout(height=600, width=800,
                  title_text="Subplots with shared x-axes")

# extra data where xaxis3 is shared with subplot 1
fig.add_trace(go.Scatter(
    x=[11, 12, 13],
    y=[6, 5, 4],
    name="xaxis3 data",
    xaxis="x3"
))

# some adjustmentns for xaxis3
fig.update_layout(xaxis3=dict(
        title="xaxis3 title",
        titlefont=dict(
            color="#9467bd"
        ),
        tickfont=dict(
            color="#9467bd"
        ),
        anchor="free",
        overlaying="x1",
        side="right",
        position=0.0
    ))

# extra data where xaxis4 is shared with subplot 2
fig.add_trace(go.Scatter(
    x=[50, 60, 70],
    y=[60, 60, 60],
    name="xaxis4 data",
    xaxis="x4",
    yaxis = 'y2'
))

# some adjustments for xaxis4
fig.update_layout(xaxis4=dict(
        title="xaxis4 title",
        titlefont=dict(
            color="#9467bd"
        ),
        tickfont=dict(
            color="#9467bd"
        ),
        anchor="free",
        overlaying="x2",
        side="right",
        position=0.0
    ))

# make room to display double x-axes
fig.update_layout(yaxis1=dict(domain=[0.1, 1]),
                  yaxis2=dict(domain=[0.1, 1]),
                 )

# not critical, but just to put a little air in there
fig.update_layout(xaxis1=dict(domain=[0.0, 0.4]),
                  xaxis2=dict(domain=[0.6, 1]),
                 )

fig.show()

编辑:收紧标题和范围之间的 space。

一种方法是使用 fig.update_layout(title=dict()):

更改标题本身的位置
fig.update_layout(
    title={
        'text': "Plot Title",
        'y':0.88,
        'x':0.42,
        'xanchor': 'left',
        'yanchor': 'top'})

情节 2

情节 2 的完整代码

from plotly.subplots import make_subplots
import plotly.graph_objects as go

# initial subplot with two traces
fig = make_subplots(rows=1, cols=2)

fig.add_trace(
    go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),
    row=1, col=1
)

fig.add_trace(
    go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),
    row=1, col=2
)

fig.update_layout(height=600, width=800,
                  title_text="Subplots with shared x-axes")

# extra data where xaxis3 is shared with subplot 1
fig.add_trace(go.Scatter(
    x=[11, 12, 13],
    y=[6, 5, 4],
    name="xaxis3 data",
    xaxis="x3"
))

# some adjustmentns for xaxis3
fig.update_layout(xaxis3=dict(
        title="xaxis3 title",
        titlefont=dict(
            color="#9467bd"
        ),
        tickfont=dict(
            color="#9467bd"
        ),
        anchor="free",
        overlaying="x1",
        side="right",
        position=0.0
    ))

# extra data where xaxis4 is shared with subplot 2
fig.add_trace(go.Scatter(
    x=[50, 60, 70],
    y=[60, 60, 60],
    name="xaxis4 data",
    xaxis="x4",
    yaxis = 'y2'
))

# some adjustments for xaxis4
fig.update_layout(xaxis4=dict(
        title="xaxis4 title",
        titlefont=dict(
            color="#9467bd"
        ),
        tickfont=dict(
            color="#9467bd"
        ),
        anchor="free",
        overlaying="x2",
        side="right",
        position=0.0
    ))

# make room to display double x-axes
fig.update_layout(yaxis1=dict(domain=[0.1, 1]),
                  yaxis2=dict(domain=[0.1, 1]),
                 )

# not critical, but just to put a little air in there
fig.update_layout(xaxis1=dict(domain=[0.0, 0.4]),
                  xaxis2=dict(domain=[0.6, 1]),
                 )

fig.update_layout(
    title={
        'text': "Plot Title",
        'y':0.88,
        'x':0.42,
        'xanchor': 'left',
        'yanchor': 'top'})

fig.show()

这个问题有点棘手,但可行。 There 是如何在单个图中创建多个轴的示例。 基本上,您使用 twinx() 创建另一个轴,然后以这样的方式设置所有内容,使其最终很好地结束。问题是 matplotlib 自动将其他轴放在另一侧(所以 'top' 在 x 轴的情况下, 'right' 在 y- 的情况下轴)。这就是为什么我们需要设置所有这些属性(在哪里显示轴,标签和刻度应该放置在哪个方向)和一些不错的东西,比如标签和刻度的颜色。

import matplotlib.pyplot as plt

fig, ax1 = plt.subplots()
fig.subplots_adjust(right=0.75)

axs =[]
axs.append( ax1 )
for i in range(1,3):
    # creates another axes that shares the same y-axis 
    axs.append( ax1.twiny() ) 

offest = 42
for i,ax in enumerate(axs):
    # sets the ticks to be shown at the bottom
    ax.xaxis.tick_bottom()
    ax.tick_params(axis='x', direction='out',labelbottom=True)
    # places the nex axis (ticks and description) below the other axes
    ax.spines["bottom"].set_position(("outward", offest*i)) # additional offset


line1, = axs[0].plot([0, 1, 2], [0, 1, 2], "b-", label="Line 1")
line2, = axs[1].plot([0, 2, 4], [0, 3, 2], "r-", label="Line 2")
line3, = axs[2].plot([0, 10, 60], [50, 30, 15], "g-", label="Line 3")
lines = [line1,line2,line3]

lim = [(0,2), (0,4),(2,65)]
XLabel = ["Time","Distance","Height"]

for i,ax in enumerate(axs):
    # set limits
    ax.set_xlim( lim[i] )
    # set label
    ax.set_xlabel( XLabel[i] )
    # set label position
    ax.xaxis.set_label_position("bottom")
    # set label color
    color = lines[i].get_color()
    ax.xaxis.label.set_color( color )
    # set tick color
    ax.tick_params(axis='x', colors=color)
# set legend only in one axis (but with all lines)
ax1.legend(lines, [l.get_label() for l in lines])

plt.show()

顺便说一句,由于(我的)方便,我使用了 matplotlib。这是我更喜欢的绘图库,但没有具体原因。