Plotly: как добавить несколько осей y?

#python #python-3.x #plotly

#python #python-3.x #plotly

Вопрос:

У меня есть данные с 5 разными столбцами, и их значение отличается друг от друга.

 Actual gen  Storage Solar Gen   Total Gen   Frequency
  1464      1838    1804         18266        51 
  2330      2262    518          4900         51
  2195      923     919          8732         49
  2036      1249    1316         3438         48
  2910      534     1212         4271         47
  857       2452    1272         6466         50
  2331      990     2729         14083        51
  2604      767     2730         19037        47
  993       2606    705          17314        51
  2542      213     548          10584        52
  2030      942     304          11578        52
  562       414     2870         840          52
  1111      1323    337          19612        49   
  1863      2498    1992         18941        48
  1575      2262    1576         3322         48
  1223      657     661          10292        47
  1850      1920    2986         10130        48
  2786      1119    933          2680         52
  2333      1245    1909         14116        48
  1606      2934    1547         13767        51
 

Итак, из этих данных я хочу построить график с 3 осями y. Одна для frequency , вторая для Total Gen и третья для Actual gen , Storage и Solar Gen .
Частота должна быть на вторичной оси Y (с правой стороны), а остальные должны быть на левой стороне.

  • Для частоты, как вы можете видеть, значения очень случайны от 47 до 52, поэтому они должны быть с правой стороны, вот так: введите описание изображения здесь
  • Для общего значения Gen они очень высоки по сравнению с другими, поскольку они составляют от 100-20000, поэтому я не могу объединить их с другими, что-то вроде этого: введите описание изображения здесьЗдесь я хочу:
  • Заголовок оси Y 1 = фактическое поколение, хранилище и солнечное поколение
  • Заголовок оси Y 2 = общее поколение
  • Заголовок оси Y 3 = Частота

Мой подход:

 import logging

import pandas as pd
import plotly.graph_objs as go
import plotly.offline as pyo
import xlwings as xw
from plotly.subplots import make_subplots

app = xw.App(visible=False)
try:
    wb = app.books.open('2020 10 08 0000 (Float).xlsx')
    sheet = wb.sheets[0]
    
    actual_gen = sheet.range('A2:A21').value
    frequency = sheet.range('E2:E21').value
    storage = sheet.range('B2:B21').value
    total_gen = sheet.range('D2:D21').value
    solar_gen = sheet.range('C2:C21').value

except Exception as e:
    logging.exception("Something awful happened!")
    print(e)
finally:
    app.quit()
    app.kill()

# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])

# Add traces
fig.add_trace(
    go.Scatter(y=storage, name="BESS(KW)"),
)
fig.add_trace(
    go.Scatter(y=actual_gen, name="Act(KW)"),
)
fig.add_trace(
    go.Scatter(y=solar_gen, name="Solar Gen")
)
fig.add_trace(
    go.Scatter(x=x_values, y=total_gen, name="Total Gen",yaxis = 'y2')
)
fig.add_trace(
    go.Scatter(y=frequency, name="Frequency",yaxis = 'y1'),
)

fig.update_layout( title_text = '8th oct BESS',
    yaxis2=dict(title="BESS(KW)",titlefont=dict(color="red"), tickfont=dict(color="red")),
    yaxis3=dict(title="Actual Gen(KW)",titlefont=dict(color="orange"),tickfont=dict(color="orange"), anchor="free", overlaying="y2", side="left"),
    yaxis4=dict(title="Solar Gen(KW)",titlefont=dict(color="pink"),tickfont=dict(color="pink"), anchor="x2",overlaying="y2", side="left"),
    yaxis5=dict(title="Total Gen(KW)",titlefont=dict(color="cyan"),tickfont=dict(color="cyan"), anchor="free",overlaying="y2", side="left"),
    yaxis6=dict(title="Frequency",titlefont=dict(color="purple"),tickfont=dict(color="purple"), anchor="free",overlaying="y2", side="right"))

fig.show()
 

Может кто-нибудь, пожалуйста, помогите?

Комментарии:

1. Пожалуйста, уточните точную проблему. Я вижу, у вас есть документы Plotly… где вы застряли? Кроме того, возможно Total Gen , можно пересчитать до шкалы log10?

2. Сэр, я действительно застрял на Total_gen plot. Я не могу построить график заголовка 2 по оси y.

3. Конечно. Я внес изменения. Спасибо

4. Большое вам спасибо, сэр. Это действительно полезно.

5. С удовольствием. Рад, что это помогло вам. Всего наилучшего!

Ответ №1:

Вот пример того, как можно создать многоуровневые оси y.

По сути, ключами к этому являются:

  • Создайте ключ в layout dict для каждой оси, затем назначьте трассировку для этой оси.
  • Установите xaxis domain значение более узким, чем [0, 1] (например [0.2, 1] ), таким образом сдвинув левый край графика вправо, освободив место для многоуровневой оси y.

Ссылка на официальные документы Plotly по этому вопросу.

Чтобы упростить чтение данных для этой демонстрации, я взял на себя смелость сохранить ваш набор данных в виде файла CSV, а не Excel, а затем использовал pandas.read_csv() функцию для загрузки набора данных в a pandas.DataFrame , который затем передается в функции построения графиков в виде столбцов данных.

Пример:

Прочитайте набор данных:

 df = pd.read_csv('energy.csv')
 

Пример кода построения графика:

 layout = {'title': '8th Oct BESS'}
traces = []

traces.append({'y': df['storage'], 'name': 'Storage'})
traces.append({'y': df['actual_gen'], 'name': 'Actual Gen'})
traces.append({'y': df['solar_gen'], 'name': 'Solar Gen'})
traces.append({'y': df['total_gen'], 'name': 'Total Gen', 'yaxis': 'y2'})
traces.append({'y': df['frequency'], 'name': 'Frequency', 'yaxis': 'y3'})

layout['xaxis'] = {'domain': [0.12, 0.95]}
layout['yaxis1'] = {'title': 'Actual Gen, Storage, Solar Gen', 'titlefont': {'color': 'orange'}, 'tickfont': {'color': 'orange'}}
layout['yaxis2'] = {'title': 'Total Gen', 'side': 'left', 'overlaying': 'y', 'anchor': 'free', 'titlefont': {'color': 'red'}, 'tickfont': {'color': 'red'}}
layout['yaxis3'] = {'title': 'Frequency', 'side': 'right', 'overlaying': 'y', 'anchor': 'x', 'titlefont': {'color': 'purple'}, 'tickfont': {'color': 'purple'}}
    
pio.show({'data': traces, 'layout': layout})
 

График:

Учитывая природу этих трасс, они сильно накладываются друг на друга, что может затруднить интерпретацию графика.

Доступно несколько вариантов:

  • Измените range параметр для каждой оси y, чтобы ось занимала только часть графика. Например, если набор данных находится в диапазоне от 0 до 5, установите соответствующий yaxis range параметр [-15, 5] равным , который переместит эту трассировку в верхнюю часть графика.
  • Рассмотрите возможность использования подзаголовков, где подобные трассы могут быть сгруппированы … или каждая трасса может иметь свой собственный график. Вот документы Plotly по подзаголовкам.

введите описание изображения здесь

Комментарии (TL; DR):

Приведенный здесь пример кода использует API Plotly более низкого уровня, а не удобную оболочку, такую как graph_objects или express . Причина в том, что я (лично) считаю, что пользователям полезно показывать, что происходит «под капотом», а не маскировать базовую логику кода удобной оболочкой.

Таким образом, когда пользователю нужно изменить более мелкие детали графика, он будет лучше понимать list s и dict s, которые Plotly создает для базового графического движка (orca).

Ответ №2:

Это моя функция для построения любого фрейма данных с индексом как x по оси x. Должен поддерживать любой размер фреймов данных

 def plotly_multi(data):
    if data.shape[1]>2:
        fig = go.Figure()
        fig.add_trace(
            go.Scatter(x=data.index, y=data.iloc[:, 0], name=data.columns[0]))
    
        fig.update_layout(
            xaxis=dict(domain=[0.1, 0.9]),
            yaxis=dict(title=data.columns[0]),
            yaxis2=dict(title=data.columns[1], anchor="x", overlaying="y", side="right"))
    
        for i, col in enumerate(data.columns[1:], 1):
            fig.add_trace(
                go.Scatter(x=data.index,y=data[col],name=col,yaxis=f"y{i 1}"))
    
        for i, col in enumerate(data.columns[2:], 2):
            axis = f"yaxis{i 1}"
    
            if i%2 == 0:
                side = "left"
                position = (i-1)*0.05
            else:
                side = "right"
                position = 1 - (i-2)*0.05
    
            axis_value = dict(
                title=col,
                anchor="free",
                overlaying="y",
                side=side,
                position=position)
            exec(f"fig.update_layout({axis} = axis_value)")
    if data.shape[1]==2:
        fig = make_subplots(specs=[[{"secondary_y": True}]])
        # Add traces
        fig.add_trace(
            go.Scatter(x=data.index, y=data.iloc[:, 0], name=data.columns[0]),
            secondary_y=False,)
        fig.add_trace(
            go.Scatter(x=data.index, y=data.iloc[:, 1], name=data.columns[1]),
            secondary_y=True,)
        # Set x-axis title
        fig.update_xaxes(title_text="Date")
        # Set y-axes titles
        fig.update_yaxes(title_text=data.columns[0], secondary_y=False)
        fig.update_yaxes(title_text=data.columns[0], secondary_y=True)
    if data.shape[1] == 1:
        fig = px.line(data.reset_index(), x = data.index.name, y = data.columns)
    
    fig.update_layout(
    title_text="Data",
    width=800,)
    
    fig.show()