Сюжет: как отобразить более двух заголовков / диапазонов по осям x на одном подзаголовке?

Nov 28 2020

Я использую Plotly и делаю подзаголовки точечной диаграммы с общей осью y и разными осями x. Я попытался использовать синтаксис объекта figure (fig ['layout'] [data index]) для отображения нескольких сложенных осей x и их соответствующих диапазонов. Мне удалось показать только две оси x и диапазоны для каждого подзаголовка, назначив атрибуты «верх» и «низ» боковому атрибуту макета рисунка. Во втором столбце справа на рисунке ниже должны отображаться заголовки / диапазоны для серий T5, T6 и T7, но отображаются только заголовок и диапазон для T5 и T7.

Можно ли отобразить более двух заголовков / диапазонов осей x на одном подзаголовке в Plotly? Для реализованного примера Matplotlib поддерживает отображение нескольких составных осей

Спасибо Vestland, ключевым моментом было использование атрибута position макета фигуры и масштабирование оси Y для правильного соответствия корректировке. См. [Monstrosity] ниже для полной реализации нескольких осей на основе примера кода Vestland.

Ответы

2 vestland Nov 29 2020 at 22:47

Вам необходимо точное сочетание make_subplots(rows=1, cols=2), add_traces()и fig.update_layout(xaxis=dict(domain=...):

  1. Создайте «обычный» подзаголовок, используя fig=make_subplots(rows=1, cols=2)две трассировки, как описано здесь .

  2. Добавьте третью трассу с собственной осью x, используя fig.add_trace(go.Scatter([...[, xaxis="x3"))

  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во внимание, заключается в том, что positionатрибут в point 3не может быть отрицательным, и вам нужно как-то освободить место для двойных осей x. Вот результат:

участок

Полный код:

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

Изменить: сократить расстояние между заголовком и диапазоном.

Один из подходов - изменить положение самого заголовка, используя 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()
1 max Nov 30 2020 at 08:30

Вопрос немного сложный, но выполнимый. Вот пример того, как создать несколько осей на одном графике. По сути, вы создаете еще одну ось, 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 из-за (моего) удобства. Я предпочитаю эту библиотеку для построения графиков, но без особой причины.