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