如何在 Plotly 中循环创建子图,每个子图上都有几条曲线?都有、几条、个子、图上

2023-09-06 14:31:40 作者:淡淡de茶香味

我已经在下面编写了嵌套循环以成功生成 21 个图表(每个国家/地区一个图表,例如

I already wrote below nested loops to generate 21 charts with success (one chart for each country, for example german gas austrian gas)

Plotly绘图原理

dfs is a dict with 21 countries names as keys and their respective gas storage dfs as values

for country in list(dfs_storage.keys()):
    df_country=dfs_storage[country]
    month = list(set(df_country['month']))
    fig = go.Figure()
    for year in set(df_country['year']):
        workingGasVolume_peryear=df_country.loc[df_country['year']==year,'workingGasVolume']
        gasInStorage_peryear=df_country.loc[df_country['year']==year,'gasInStorage']
        # Create and style traces
        fig.add_trace(go.Scatter(x=month, y=workingGasVolume_peryear, name=f'workingGasVolume{year}',
                                 line=dict(width=4,dash='dash')))
        fig.add_trace(go.Scatter(x=month, y=gasInStorage_peryear, name=f'gasInStorage{year}',
                                 line = dict(width=4)))

    # Edit the layout
    fig.update_layout(title=f'{country} workingGasVolume gasInStorage',
                       xaxis_title='Month',
                       yaxis_title='Gas Volume')

    offline.plot({'data':fig},filename=f'{country} gas storage.html',auto_open=False)

Now I am asked to put these 21 charts in one HTML file without changing each chart, they can appear vertically one after another for example

I tried the "subplots" with Plotly with below code and modified a few times but never have the desired chart, I got one single useless chart where I can't see any values.. Can anyone help me? Thanks

countries=[]
for country in list(dfs_storage.keys()):
    countries.append(country)
fig = make_subplots(
    rows=len(list(dfs_storage.keys())),cols=1,
    subplot_titles=(countries))

for country in countries:
    df_country=dfs_storage[country]
    month = list(set(df_country['month']))
    for year in set(df_country['year']):
        workingGasVolume_peryear=df_country.loc[df_country['year']==year,'workingGasVolume']
        gasInStorage_peryear=df_country.loc[df_country['year']==year,'gasInStorage']
        # Create and style traces
        fig.add_trace(go.Scatter(x=month, y=workingGasVolume_peryear, name=f'workingGasVolume{year}',
                                 line=dict(width=4,dash='dash')))
        fig.add_trace(go.Scatter(x=month, y=gasInStorage_peryear, name=f'gasInStorage{year}',
                                 line = dict(width=4)))

    # Edit the layout
# fig.update_layout(title='workingGasVolume gasInStorage',
#                    xaxis_title='Month',
#                    yaxis_title='Gas Volume')

offline.plot({'data':fig},filename='gas storage.html',auto_open=False) 

Edit 7th June: as per jayveesea's advice, I added the row and col argument under add_trace, the code is below but still has Traceback:

countries=[]
for country in list(dfs_storage.keys()):
    countries.append(country)
fig = make_subplots(
    rows=len(list(dfs_storage.keys())),cols=1,
    subplot_titles=(countries))

for i in range(len(countries)):
    country=countries[i]
    df_country=dfs_storage[country]
    month = list(set(df_country['month']))
    for year in set(df_country['year']):
        workingGasVolume_peryear=df_country.loc[df_country['year']==year,'workingGasVolume']
        gasInStorage_peryear=df_country.loc[df_country['year']==year,'gasInStorage']
        # Create and style traces
        fig.add_trace(go.Scatter(x=month, y=workingGasVolume_peryear, name=f'workingGasVolume{year}',row=i,col=1,
                                 line=dict(width=4,dash='dash')))
        fig.add_trace(go.Scatter(x=month, y=gasInStorage_peryear, name=f'gasInStorage{year}',row=i,col=1,
                                 line = dict(width=4)))

    # Edit the layout
# fig.update_layout(title='workingGasVolume gasInStorage',
#                    xaxis_title='Month',
#                    yaxis_title='Gas Volume')

offline.plot({'data':fig},filename='gas storage.html',auto_open=False)

print('the Plotly charts are saved in the same folder as the Python code')

Edit 8th June: This is the code I am running now, copied from @jayveesea's answer and only modified the name of the df

countries=[]
for country in list(dfs_storage.keys()):
    countries.append(country)
# STEP 1
fig = make_subplots(
    rows=len(countries), cols=1,
    subplot_titles=(countries))

for i, country in enumerate(countries): #enumerate here to get access to i
    years = df_country.year[df_country.country==country].unique()
    for yrs in years:
        focus = (df_country.country==country) & (df_country.year==yrs)
        month = df_country.month[focus]
        workingGasVolume_peryear = df_country.workingGasVolume[focus]
        gasInStorage_peryear = df_country.gasInStorage[focus]

        # STEP 2, notice position of arguments!
        fig.add_trace(go.Scatter(x=month, 
                                 y=workingGasVolume_peryear, 
                                 name=f'workingGasVolume{yrs}',
                                 line=dict(width=4,dash='dash')),
                      row=i+1, #index for the subplot, i+1 because plotly starts with 1
                      col=1)
        fig.add_trace(go.Scatter(x=month, 
                                 y=gasInStorage_peryear, 
                                 name=f'gasInStorage{yrs}',
                                 line = dict(width=4)),
                      row=i+1,
                      col=1)      
fig.show()

Yet I still have Traceback message

Traceback (most recent call last):

  File "<ipython-input-27-513826172e49>", line 43, in <module>
    line=dict(width=4,dash='dash')),

TypeError: 'dict' object is not callable

解决方案

To use subplots in plotly you need to:

use make_subplots to initialize the layout specifying the row and column then use row and col as arguments to fig.add_trace. NOTE: subplots row and columns start at 1 (not zero)

In your case, step2 is where you are getting stuck. Initially this part was missing (first post), but now in your update it's added in as an argument to go.Scatter. Carefully look over the examples here as the differences are just commas and parentheses and their placement.

To clarify, this:

fig.add_trace(go.Scatter(x=month, 
                         y=workingGasVolume_peryear, 
                         name=f'workingGasVolume{year}',
                         row=i,
                         col=1,
                         line=dict(width=4,dash='dash')))

should be:

fig.add_trace(go.Scatter(x=month, 
                         y=workingGasVolume_peryear, 
                         name=f'workingGasVolume{year}',
                         line=dict(width=4,dash='dash')),
              row=i+1,
              col=1)

I'm having difficulty with your code and data, which could be on my end as I do not use dictionaries like this, but here is a working example with your data in a csv and the use of pandas. Also, I changed one of the years to a different country so that there would be another plot.

import pandas as pd
import plotly.graph_objects as go  
from plotly.subplots import make_subplots

df = pd.read_csv('someData.csv')
countries = df.country.unique()

# STEP 1
fig = make_subplots(
    rows=len(countries), cols=1,
    subplot_titles=(countries))

for i, country in enumerate(countries): #enumerate here to get access to i
    years = df.year[df.country==country].unique()
    for yrs in years:
        focus = (df.country==country) & (df.year==yrs)
        month = df.month[focus]
        workingGasVolume_peryear = df.workingGasVolume[focus]
        gasInStorage_peryear = df.gasInStorage[focus]

        # STEP 2, notice position of arguments!
        fig.add_trace(go.Scatter(x=month, 
                                 y=workingGasVolume_peryear, 
                                 name=f'workingGasVolume{yrs}',
                                 line=dict(width=4,dash='dash')
                                ),
                      row=i+1, #index for the subplot, i+1 because plotly starts with 1
                      col=1)
        fig.add_trace(go.Scatter(x=month, 
                                 y=gasInStorage_peryear, 
                                 name=f'gasInStorage{yrs}',
                                 line = dict(width=4)),
                      row=i+1,
                      col=1)      
fig.show()