Plotly: Como mostrar mais de 2 títulos / intervalos de eixos x no mesmo subplot?
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
Você vai precisar de uma combinação precisa de make_subplots(rows=1, cols=2)
, add_traces()
e fig.update_layout(xaxis=dict(domain=...)
:
Configure uma subtrama "regular" usando
fig=make_subplots(rows=1, cols=2)
e inclua dois traços conforme descrito aqui .Adicione um terceiro traço com seu próprio eixo x usando
fig.add_trace(go.Scatter([...[, xaxis="x3"))
Em seguida, ajuste a subtrama 1 para abrir espaço para
xaxis3
usar:fig.update_layout(xaxis3=dict(anchor="free", overlaying="x1", position=0.0))
Faça alguns ajustes finais usando
fig.update_layout([...], yaxis2=dict(domain=[0.1, 1]))
A razão pela qual você terá que levar domain
em consideração é porque o position
atributo em point 3
nã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()
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.