pythonpandasplotly

Plotly bar conditional dual axis python


import plotly.graph_objects as go
fig=go.Figure()
cols=['NO2 mug/m^3','NO mug/m^3']

for idx,col in enumerate(cols):
    for df in [ag,snd]:
        fig.add_trace(go.Bar(x=df['Date'],y=df[col],
                             name=f'{str(df.Municipality.unique()[0])} {col}'))
        #fig.add_trace(go.Bar(x=ag['Date'],y=ag['NO mug/m^3']))
        fig.update_layout(barmode='group',xaxis=dict(title='Date'))
       # print(df)
fig

In this snippet i take two columns (which are common in both dataframes) and use a bar chart which give me the following result enter image description here

in this photo we get 4 distinct bars that do not overlay. what i want is to keep this format but to also add a second y axis. But because of the dynamic nature that this format will be used i want it to be versatile. When lets say we have one column ( NO mug/m^3) to not have the second axis.

i tried something like this

fig = go.Figure()
cols = ['NO2 mug/m^3', 'NO mug/m^3']  # Selected gases
dfs = [ag, snd]  # List of dataframes

for df_idx, df in enumerate(dfs):  # Iterate over DataFrames (locations)
    for col_idx, col in enumerate(cols):  # Iterate over gases
        fig.add_trace(go.Bar(
            x=df['Date'],
            y=df[col],
            name=f'{df.Municipality.unique()[0]} - {col}',
            offsetgroup=str(df_idx),  # Group bars per location
            marker=dict(opacity=0.8),
            yaxis="y" if col_idx == 0 else "y2"  # Assign second gas to secondary y-axis
        ))

# Layout adjustments
layout_args = {
    "barmode": "group",  # Ensures bars are placed side-by-side
    "xaxis": {"title": "Date"},
    "legend_title": "Location - Gas"
}

if len(cols) == 1:
    layout_args["yaxis"] = {"title": cols[0]}  # Single Y-axis case
else:
    layout_args["yaxis"] = {"title": cols[0]}
    layout_args["yaxis2"] = {
        "title": cols[1],
        "overlaying": "y",  # Overlay on primary y-axis
        "side": "right",
        "showgrid": False
    }

fig.update_layout(**layout_args)
fig.show()

From what i tried i got something like the followingenter image description here

which is not desireable. is there any way to keep the format of my first image(4 distinct non overlaying bars per x point) while using some condition in order to achive my second axis?

thank for your patience


Solution

  • This is because you are using df_idx to offset the bars. df_idx will only be 0 or 1.

    You need to create another variable for offsetting the positions.

    Example:

    fig = go.Figure()
    cols = ['NO2 mug/m^3', 'NO mug/m^3']  # Selected gases
    dfs = [ag, snd]  # List of dataframes
    
    offsetgroup = 0 # new variable for offsetting position
    
    for col_idx, col in enumerate(cols):  # Iterate over gases
      for df_idx, df in enumerate(dfs):  # Iterate over DataFrames (locations)
        
            fig.add_trace(go.Bar(
                x=df['Date'],
                y=df[col],
                name=f'{df.Municipality.unique()[0]} - {col}',
                offsetgroup=str(offsetgroup),  # Group bars per location
                marker=dict(opacity=0.8),
                yaxis="y" if col_idx == 0 else "y2"  # Assign second gas to secondary y-axis
            ))
            offsetgroup += 1
    
    
    # Layout adjustments
    layout_args = {
        "barmode": "group",  # Ensures bars are placed side-by-side
        "xaxis": {"title": "Date"},
        "legend_title": "Location - Gas"
    }
    
    if len(cols) == 1:
        layout_args["yaxis"] = {"title": cols[0]}  # Single Y-axis case
    else:
        layout_args["yaxis"] = {"title": cols[0]}
        layout_args["yaxis2"] = {
            "title": cols[1],
            "overlaying": "y",  # Overlay on primary y-axis
            "side": "right",
            "showgrid": False
        }
    
    fig.update_layout(**layout_args)
    fig.show()