Search code examples
pythonpandasseabornfacet-grid

Adding secondary y-axis with Facetgrid


I have a cohorted dataset (from 1 day to 365 days) that now I am representing like this in Seaborn. The blue line is the previous result, the orange is the current result and the bars are the delta in % between them:

enter image description here

However, I need to add the bars on the same plot that the lineplot with a secondary axis.

My expected output would be this for each plot of the Facetgrid:

enter image description here

A sample of the dataframe that I am using on wide format that I splited in two to separate the date from de delta and the pre y post results. I need it this way because of the cohorted data:

enter image description here

And this is the code I use to plot the first graph:

fig, ax1 = plt.subplots() # initializes figure and plots

ax2 = ax1.twinx() # applies twinx to ax2, which is the second y axis. 

g = sns.FacetGrid(df_ads_long_st, col="m", hue="status", height=5, aspect=0.8)
g.map(sns.lineplot, "dx", "value_a", alpha=.7, ax = ax1)

g = sns.FacetGrid(df_ads_long_de, col="m", hue="status", height=5, aspect=0.8)
g.map(sns.barplot, "dx", "value_a", alpha=.7, ax = ax2)

# these lines add the annotations for the plot. 
ax1.set_xlabel('DX')
ax1.set_ylabel('ARPU', color='b')
ax2.set_ylabel('Delta', color='r')

plt.show(); # shows the plot. 

Is there any other day to do this?

Thanks!


Solution

  • I had a similar requirement. The trick is to create your own function. In this function you create a twin axis.

    import matplotlib.pyplot as plt
    import pandas as pd
    import seaborn as sns
    
    
    def facetgrid_two_axes(data: pd.DataFrame, x_name: str, y1_name: str, y2_name: str, ylabel1: str, ylabel2: str):
        ax1 = plt.gca()
        sns.lineplot(data[x_name], data[y1_name], alpha=.7, ax=ax1)
        ax1.set_ylabel(ylabel1)
    
        ax2 = ax1.twinx()
        sns.barplot(data[x_name], data[y2_name], alpha=.7, ax=ax2)
        ax2.set_ylabel(ylabel2)
    
    
    if __name__ == '__main__':
        df_ads_long_st = pd.DataFrame(...) // Your data
    
        g = sns.FacetGrid(df_ads_long_st, col="m", hue="status", height=5, aspect=0.8)
        g.map_dataframe(facetgrid_two_axes, x_name="dx", y1_name="value_a", y2_name="value_a", alpha=.7,
                        ylabel1='Counts', ylabel2='Detection rates')
    
        g.set_xlabels('x data')
    
        plt.show()
    

    A shortcoming I had, was that the ylabels are shown on every plot instead of only at the borders of the figure.