简体   繁体   中英

Plotly: How to add multiple y-axes?

I have data with 5 different columns and their value varies from each other.

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

So in from this data I want to plot a graph with 3 y-axis. One for the frequency , second for the Total Gen and third is for Actual gen , Storage and Solar Gen . Frequency should be on the secondary Y-axis(Right side) and the Rest of them should be on the left side.

  • For frequency as you can see the values are very random between 47 to 52 that's why it should be on the right side, like this: 在此处输入图像描述

  • For Total Gen value are very high as compared to others as they are from 100-20000 so that's I can't merge them with others, something like this: 在此处输入图像描述 Here I want:

  • Y-axis title 1 = Actual gen, Storage, and Solar gen

  • Y-axis title 2 = Total gen

  • Y-axis title 3 = Frequency

My approach:

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

Can someone please help?

Here is an example of how multi-level y-axes can be created.

Essentially, the keys to this are:

  • Create a key in the layout dict, for each axis, then assign a trace to the that axis.
  • Set the xaxis domain to be narrower than [0, 1] (for example [0.2, 1] ), thus pushing the left edge of the graph to the right, making room for the multi-level y-axis.

A link to the official Plotly docs on the subject.

To make reading the data easier for this demonstration, I have taken the liberty of storing your dataset as a CSV file, rather than Excel - then used the pandas.read_csv() function to load the dataset into a pandas.DataFrame , which is then passed into the plotting functions as data columns.

Example:

Read the dataset:

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

Sample plotting code:

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

Graph:

Given the nature of these traces, they overlay each other heavily, which could make graph interpretation difficult.

A couple of options are available:

  • Change the range parameter for each y-axis so the axis only occupies a portion of the graph. For example, if a dataset ranges from 0-5, set the corresponding yaxis range parameter to [-15, 5] , which will push that trace near the top of the graph.

  • Consider using subplots, where like-traces can be grouped... or each trace can have it's own graph. Here are Plotly's docs on subplots.

在此处输入图像描述

Comments (TL;DR):

The example code shown here uses the lower-level Plotly API, rather than a convenience wrapper such as graph_objects or express . The reason is that I (personally) feel it's helpful to users to show what is occurring 'under the hood', rather than masking the underlying code logic with a convenience wrapper.

This way, when the user needs to modify a finer detail of the graph, they will have a better understanding of the list s and dict s which Plotly is constructing for the underlying graphing engine (orca).

This is my function to plot any dataframe with index as x in the x axis. Should support any size of dataframes

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM