Plotly: Como mostrar mais de 2 títulos / intervalos de eixos x no mesmo subplot?

Nov 28 2020

Estou usando Plotly e fazendo subplots de dispersão com um eixo y compartilhado e diferentes eixos x. Tentei usar a sintaxe do objeto de figura (fig ['layout'] [índice de dados]) para mostrar vários eixos x empilhados e seus respectivos intervalos. Eu só tive sucesso em mostrar dois eixos x e intervalos por subtrama atribuindo 'superior' e 'inferior' ao atributo lateral do layout da figura. A 2ª coluna à direita na figura abaixo deve mostrar títulos / intervalos para as séries T5, T6 e T7, mas apenas o título e intervalo para T5 e T7 aparecem.

É possível mostrar mais de 2 títulos / intervalos de eixos x no mesmo subplot no Plotly? Para um exemplo implementado, Matplotlib suporta a exibição de vários eixos empilhados

Obrigado a Vestland, o segredo era usar o atributo position do layout da figura e dimensionar o eixo y para ajustar o ajuste adequadamente. Veja a [monstruosidade] abaixo para uma implementação completa de vários eixos com base no código de amostra de Vestland.

Respostas

2 vestland Nov 29 2020 at 22:47

Você vai precisar de uma combinação precisa de make_subplots(rows=1, cols=2), add_traces()e fig.update_layout(xaxis=dict(domain=...):

  1. Configure uma subtrama "regular" usando fig=make_subplots(rows=1, cols=2)e inclua dois traços conforme descrito aqui .

  2. Adicione um terceiro traço com seu próprio eixo x usando fig.add_trace(go.Scatter([...[, xaxis="x3"))

  3. Em seguida, ajuste a subtrama 1 para abrir espaço para xaxis3usar:fig.update_layout(xaxis3=dict(anchor="free", overlaying="x1", position=0.0))

  4. Faça alguns ajustes finais usando fig.update_layout([...], yaxis2=dict(domain=[0.1, 1]))

A razão pela qual você terá que levar domainem consideração é porque o positionatributo em point 3não pode ser negativo e você terá que abrir espaço para os eixos x duplos de alguma forma. Aqui está o resultado:

Enredo

Código completo:

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()

Editar: aperte o espaço entre o título e o intervalo.

Uma abordagem é alterar a posição do próprio título usando fig.update_layout(title=dict()):

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

Lote 2

Código completo para lote 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()
1 max Nov 30 2020 at 08:30

A questão é um pouco complicada, mas viável. Há um exemplo de como criar vários eixos em um único gráfico. Basicamente, você cria outro eixo com twinx()e, em seguida, configura tudo de forma que fique bem. O problema é que matplotlib coloca automaticamente outros eixos no lado oposto ( 'top'no caso do eixo xe 'right'no caso do eixo y). É por isso que precisamos definir todas essas propriedades (onde mostrar o eixo, em qual direção o rótulo e os tiques devem ser colocados) e algumas coisas legais, como a cor do rótulo e os tiques.

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()

BTW, usei matplotlib devido à (minha) conveniência. É a biblioteca de plotagem que prefiro, mas por nenhum motivo específico.