Search code examples
pythonmatplotlibseabornsubplot

Is there a way to create a secondary y axis within a looped subplot figure using seaborn?


I have written the code successfully to loop through data and create subplots however, I want two of the data sets to be displayed on the y axis. Those are y and y1 or ax2. This code creates a secondary y axis however, it does not label the secondary y axis nor is their any data associated with it.

nrow = 4
ncol = 3
fig, axes = plt.subplots(nrow, ncol, figsize=(17,17))
plt.subplots_adjust(bottom=.1, right=1.5,top=1.75)
fig.delaxes(axes[3,2])

count = 0
for r in range(nrow):
  for c in range(ncol):

    x = dataobserved[count].index
    x1 = datamodeled[count].index
    y = dataobserved[count]['Average Q cfs']
    y1 = datamodeled[count]['QHistorical']
    y2 = dataobserved[count]['Total Generation Mwh']
    y3 = datamodeled[count]['Historical']

    sns.set_style("white")
    ax2 = ax.twinx()

    ax2 = sns.lineplot(x = x, y = y, color = "black", linestyle = '--', ax = axes[r,c])    
    ax2 = sns.lineplot(x = x1, y = y1, color = "blue", linestyle = '--', ax = axes[r,c]) 
    ax = sns.lineplot(x = x, y = y2, color = "black", ax = axes[r,c]) 
    ax = sns.lineplot(x = x1, y = y3, color = "blue", ax = axes[r,c]) 


    subplot_title = (str(ttl[count]))
    axes[r,c].set_title(subplot_title, size = 20)
    axes[r,c].legend(labels = ['Observed Outflow','Historical Baseline Outflow','Observed Generation','Historical Baseline Generation'], loc = "upper left")

    count+=1
  for ax in axes.flat:
    ax.set_xlabel('Water Year Week', fontsize = 15)
    ax.set_ylabel( 'Weekly Total Generation (Mwh)', fontsize = 15)
    ax2.set_ylabel('Weekly Average Outflow (cfs)')
    ax.set_xticks(range(1,52,10))

example of two subplots produced from the above code

I have reviewed Share secondary y axis in looped seaborn plots but its not quite the same situation as the secondary y axis is not shared.


Solution

  • Since there was no tater presented, I used sample data that I created appropriately and handled the subplots in a loop process. You have used the loop process twice, but by flattening the axes, you only need to do it once.

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    df = pd.DataFrame({'date':pd.date_range('2021-01-01','2021-12-31',freq='1d'),
                       'y': np.random.randint(100,1000,365),
                       'y1': np.random.randint(200,2000,365),
                       'y2':np.random.rand(365),
                       'y3':np.random.rand(365)})
    
    df['date'] = pd.to_datetime(df['date'])
    df['Week'] = df['date'].dt.isocalendar().week
    df['month'] = df['date'].dt.month
    
    fig, axes = plt.subplots(4,3, figsize=(17,17))
    plt.subplots_adjust(bottom=.1, right=1.5,top=1.75)
    
    sns.set_style("white")
    
    for i,ax in enumerate(axes.flat, start=1):
        dfs = df[df['month'] == i]
        ax2 = ax.twinx()
        sns.lineplot(data=dfs, x = 'Week', y = 'y', color = "black", linestyle = '--', estimator=None, ax=ax)    
        sns.lineplot(data=dfs, x = 'Week', y = 'y1', color = "blue", linestyle = '--', estimator=None, ax=ax) 
        sns.lineplot(data=dfs, x = 'Week', y = 'y2', color = "red", estimator=None, ax=ax2)
        sns.lineplot(data=dfs, x = 'Week', y = 'y3', color = "blue", estimator=None, ax=ax2)
        ax.set_title(i, size = 20)
        ax.legend(labels = ['y1','y2'], loc = "upper left")
        ax.set_xlabel('Water Year Week', fontsize = 15)
        ax.set_ylabel( 'Weekly Total Generation (Mwh)', fontsize = 15)
        ax2.set_ylabel('Weekly Average Outflow (cfs)')
        #ax.set_xticks(range(1,52,10))
    

    enter image description here