Plotly: ¿Cómo mostrar más de 2 títulos / rangos de ejes x en la misma subparcela?

Nov 28 2020

Estoy usando Plotly y hago subtramas de gráficos de dispersión con un eje y compartido y diferentes ejes x. He intentado utilizar la sintaxis del objeto figure (fig ['layout'] [data index]) para mostrar varios ejes x apilados y sus respectivos rangos. Solo he tenido éxito en mostrar dos ejes x y rangos por subparcela asignando 'superior' e 'inferior' al atributo lateral del diseño de la figura. La segunda columna de la derecha en la figura siguiente debe mostrar títulos / rangos para las series T5, T6 y T7, pero solo aparecen el título y el rango para T5 y T7.

¿Es posible mostrar más de 2 títulos / rangos de ejes x en la misma subparcela en Plotly? Para un ejemplo implementado, Matplotlib admite mostrar múltiples ejes apilados

Gracias a Vestland, la clave fue usar el atributo de posición del diseño de la figura y escalar el eje y para ajustar el ajuste correctamente. Vea la [monstruosidad] a continuación para una implementación completa de múltiples ejes basada en el código de muestra de Vestland.

Respuestas

2 vestland Nov 29 2020 at 22:47

Usted necesitará una combinación precisa de make_subplots(rows=1, cols=2), add_traces()y fig.update_layout(xaxis=dict(domain=...):

  1. Configure una subtrama "regular" utilizando fig=make_subplots(rows=1, cols=2)e incluya dos trazas como se describe aquí .

  2. Agregue un tercer trazo con su propio eje x usando fig.add_trace(go.Scatter([...[, xaxis="x3"))

  3. Luego, ajuste la subparcela 1 para dejar espacio para xaxis3usar:fig.update_layout(xaxis3=dict(anchor="free", overlaying="x1", position=0.0))

  4. Haga algunos ajustes finales usando fig.update_layout([...], yaxis2=dict(domain=[0.1, 1]))

La razón por la que tendrás que tener domainen cuenta es porque el positionatributo en point 3no puede ser negativo y tendrás que dejar espacio para los ejes x dobles de alguna manera. Aquí está el resultado:

Trama

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: ajusta el espacio entre el título y el rango.

Un enfoque es cambiar la posición del 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'})

Parcela 2

Código completo para la parcela 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

La pregunta es un poco complicada pero factible. Hay un ejemplo de cómo crear varios ejes en un solo gráfico. Básicamente, crea otro eje con twinx()y luego configura todo de tal manera que termina bien. El problema es que matplotlib coloca automáticamente otros ejes en el lado opuesto (así 'top'en el caso del eje xy 'right'en el caso del eje y). Es por eso que necesitamos establecer todas estas propiedades (dónde mostrar el eje, en qué dirección se deben colocar la etiqueta y los ticks) y algunas cosas interesantes, como el color de la etiqueta y los ticks.

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

Por cierto, usé matplotlib debido a (mi) conveniencia. Es la biblioteca de trazado que prefiero, pero sin ninguna razón específica.