# Add both primary and secondary axis for same line

**URL:** <https://community.plotly.com/t/add-both-primary-and-secondary-axis-for-same-line/82375>\
**Category:** 📊 Plotly Python\
**Tags:** question\
**Created:** [February 15, 2024, 3:55am UTC](https://community.plotly.com/t/add-both-primary-and-secondary-axis-for-same-line/82375 "2024-02-15T03:55:30Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![nohedge](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/nohedge/32/26943_2.png) [@nohedge](https://community.plotly.com/u/nohedge)\
**Post date:** [February 15, 2024, 3:55am UTC](https://community.plotly.com/t/add-both-primary-and-secondary-axis-for-same-line/82375/1 "2024-02-15T03:55:30Z")

</div>

I am working on a school project. I have created the following callback to update a graph. The graph simply plots 1) a line showing the values of the variable Wealth, 2) a horizontal line at the starting value of Wealth, 3) fills the area between the first and the second line. I would like to modify the code to add ticks on the vertical axis on the left, showing the difference between the value of Wealth and the starting its value. Therefore, I don’t want to add a 3rd line, I just want to add the ticks that essentially show the profit. I will show my attempt below this code snippet.

```auto
# Callbacks to update graphs based on the original code
@app.callback(
    Output('wealth-over-time', 'figure'),
    Input('wealth-time-range-slider', 'value'))
def update_wealth_over_time(selected_range):
    filtered_df = df[df['Day'].between(selected_range[0], selected_range[1])]
    # Create a new DataFrame for the fill area
    fill_area_df = pd.DataFrame({
        'Day': filtered_df['Day'],
        'Starting Wealth': starting_wealth,
        'Wealth ($)': filtered_df['Wealth ($)']
    })

    fig = go.Figure()
    
    # Add fill area between starting wealth and Wealth ($)
    fig.add_trace(go.Scatter(
        x=pd.concat([fill_area_df['Day'], fill_area_df['Day'][::-1]]), # X-values concatenated with reverse for seamless fill
        y=pd.concat([fill_area_df['Starting Wealth'], fill_area_df['Wealth ($)'][::-1]]), # Y-values for starting wealth and Wealth ($) with reverse for fill
        fill='toself',
        fillcolor='rgba(135, 206, 250, 0.5)', # Light blue fill with transparency
        line=dict(color='rgba(255,255,255,0)'), # Invisible line around the fill area
        showlegend=False,
        name='Fill Area',
    ))
    
    # Add Wealth ($) line on top of the fill area
    fig.add_trace(go.Scatter(
        x=filtered_df['Day'], 
        y=filtered_df['Wealth ($)'],
        mode='lines+markers',
        showlegend=False,
        name='Wealth ($)',
        line=dict(color='DeepSkyBlue'),
    ))

    # Add dashed horizontal line for starting wealth
    fig.add_shape(
        type='line',
        x0=filtered_df['Day'].min(),
        x1=filtered_df['Day'].max(),
        y0=starting_wealth,
        y1=starting_wealth,
        line=dict(color='Gray', dash='dash', width=2),
    )
    
    fig.update_layout(
        title={'text': "Wealth", 'font': {'color': 'black'}},
        plot_bgcolor='white',
        xaxis=dict(
            title='Day',
            color='black',
            showgrid=True,
            gridcolor='lightgrey',
            gridwidth=1,
            showline=True,
            linewidth=2,
            linecolor='black',
            mirror=True
        ),
        yaxis=dict(
            title='Wealth ($)',
            color='black',
            showgrid=True,
            gridcolor='lightgrey',
            gridwidth=1,
            showline=True,
            linewidth=2,
            linecolor='black',
            mirror=True
        ),
        xaxis_range=[filtered_df['Day'].min(), filtered_df['Day'].max()]
    )
    fig.add_shape(
        go.layout.Shape(
            type="line",
            x0=min(filtered_df['Day']),
            y0=starting_wealth,
            x1=max(filtered_df['Day']),
            y1=starting_wealth,
            line=dict(
                color="black",
                width=2,
                dash="dash",
            ),
        )
    )
    return fig

```

This generates the following image:

 ![Screenshot 2024-02-14 at 19.55.14](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/e/d/ede20f108ca3abfaf1cdbcdabb2aa7a7e72e24bf.png)

I have attempted to do this modification following the steps illustrated [here](https://plotly.com/python/multiple-axes/). In particular, I made the following changes:

1. `from plotly.subplots import make_subplots`

2. `Changed fig = go.Figure()` to `fig = make_subplots(specs=[[{"secondary_y": True}]])`

3. Added `secondary_y=True` as follows:

```auto
    # Add Wealth ($) line on top of the fill area
    fig.add_trace(go.Scatter(
        x=filtered_df['Day'], 
        y=filtered_df['Wealth ($)'],
        mode='lines+markers',
        showlegend=False,
        name='Wealth ($)',
        line=dict(color='DeepSkyBlue'),
    ), secondary_y=True,)

```

However, when I run the code, **1) the blue area is no longer aligned properly**:

 ![Screenshot 2024-02-14 at 19.49.20](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/6/1/6171dd1677ea64187c3751bb848461aa30e0467c.png)

Moreover, **2) I do not know how to show the correct ticks** (the difference of the ticks on the left and the starting value of Wealth).

I would really appreciate some help with this.

---

<div class="post-metadata">

**Author:** ![AIMPED](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/aimped/32/21758_2.png) [@AIMPED](https://community.plotly.com/u/AIMPED)\
**Post date:** [February 15, 2024, 10:45am UTC](https://community.plotly.com/t/add-both-primary-and-secondary-axis-for-same-line/82375/2 "2024-02-15T10:45:10Z")

</div>

Hi @nohedge.

First of all thank you for the detailed description of your issue. This makes helping you a lot easier!

I came up with a slightly different approach, maybe it helps:

```python
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import numpy as np
np.random.seed(42)

# create some data
starting_wealth = 14

x = np.arange(100)
y = np.random.randint(starting_wealth, 20, size=(100))
y_rel = y - starting_wealth
min_dif = y_rel.min()
max_dif = y_rel.max()

# base figure
fig = make_subplots(specs=[[{"secondary_y": True}]])

# starting_wealth line
fig.add_trace(
    go.Scatter(
        x=np.arange(100),
        y=np.ones(shape=100) * starting_wealth,
        name='starting wealth',
        line={'color': 'rgba(0, 0, 0, 1.0)', 'dash': 'dash'}
    )
)

    
# wealth line, filled to starting_wealth
fig.add_trace(
        go.Scatter(
        x=x,
        y=y,
        fill='tonexty',
        fillcolor='rgba(135, 206, 250, 0.5)',  
        showlegend=False,
        name='Fill Area',
        mode='lines+markers',
        line={'color': 'rgba(135, 206, 250, 1.0)'},
    )
)

# relative values
fig.add_trace(
    go.Scatter(
        x=x,
        y=y_rel,
        showlegend=False,
        mode='lines+markers',
        line={'color': 'rgba(0, 0, 0, 0.0)'},
        # ^^ set color to invisble. visible=True is necessary for the scondary axis
        visible=True,
        hoverinfo='skip'
    ), 
    secondary_y=True
)

# set ticks for first y-axis
fig.update_yaxes(
    dict(
        tickmode = 'linear',
        tick0 = 0.0,
        dtick = 1.0
    ),
    selector=0
)

# set ticks for second y-axis
fig.update_yaxes(
    dict(
        tickmode = 'array',
        tickvals = np.arange(min_dif, max_dif+1)
    ),
    selector=1
)

```

 ![newplot(1)](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/c/e/cee3cd76e36b2f5d3f96c80e65f6fd7e3ad237d3.png)

---

<div class="post-metadata">

**Author:** ![nohedge](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/nohedge/32/26943_2.png) [@nohedge](https://community.plotly.com/u/nohedge)\
**Post date:** [February 15, 2024, 8:25pm UTC](https://community.plotly.com/t/add-both-primary-and-secondary-axis-for-same-line/82375/3 "2024-02-15T20:25:09Z")

</div>

> [@AIMPED](#):
>
> ```auto
> from plotly.subplots import make_subplots
> import plotly.graph_objects as go
> import numpy as np
> np.random.seed(42)
> 
> # create some data
> starting_wealth = 14
> 
> x = np.arange(100)
> y = np.random.randint(starting_wealth, 20, size=(100))
> y_rel = y - starting_wealth
> min_dif = y_rel.min()
> max_dif = y_rel.max()
> 
> # base figure
> fig = make_subplots(specs=[[{"secondary_y": True}]])
> 
> # starting_wealth line
> fig.add_trace(
> go.Scatter(
> x=np.arange(100),
> y=np.ones(shape=100) * starting_wealth,
> name='starting wealth',
> line={'color': 'rgba(0, 0, 0, 1.0)', 'dash': 'dash'}
> )
> )
> 
>     
> # wealth line, filled to starting_wealth
> fig.add_trace(
> go.Scatter(
> x=x,
> y=y,
> fill='tonexty',
> fillcolor='rgba(135, 206, 250, 0.5)',  
> showlegend=False,
> name='Fill Area',
> mode='lines+markers',
> line={'color': 'rgba(135, 206, 250, 1.0)'},
> )
> )
> 
> # relative values
> fig.add_trace(
> go.Scatter(
> x=x,
> y=y_rel,
> showlegend=False,
> mode='lines+markers',
> line={'color': 'rgba(0, 0, 0, 0.0)'},
> # ^^ set color to invisble. visible=True is necessary for the scondary axis
> visible=True,
> hoverinfo='skip'
> ), 
> secondary_y=True
> )
> 
> # set ticks for first y-axis
> fig.update_yaxes(
> dict(
> tickmode = 'linear',
> tick0 = 0.0,
> dtick = 1.0
> ),
> selector=0
> )
> 
> # set ticks for second y-axis
> fig.update_yaxes(
> dict(
> tickmode = 'array',
> tickvals = np.arange(min_dif, max_dif+1)
> ),
> selector=1
> )
> 
> ```

Thank you very much. The code worked as expected. There is only one minor issue left if you could help me. I updated the call back following your steps:

```auto
@app.callback(
    Output('wealth-over-time', 'figure'),
    Input('wealth-time-range-slider', 'value'))
def update_wealth_over_time(selected_range):
    filtered_df = df[df['Day'].between(selected_range[0], selected_range[1])]
    # Create a new DataFrame for the fill area
    fill_area_df = pd.DataFrame({
        'Day': filtered_df['Day'],
        'Starting Wealth': starting_wealth,
        'Wealth ($)': filtered_df['Wealth ($)']
    })

    fig = make_subplots(specs=[[{"secondary_y": True}]])
    
    fig.add_trace(
    go.Scatter(
        x=filtered_df['Day'],
        y=np.ones(shape=filtered_df.shape[0]) * starting_wealth,
        name='starting wealth',
        showlegend=False,
        line={'color': 'rgba(0, 0, 0, 1.0)', 'dash': 'dash'}
    ))

    fig.add_trace(
        go.Scatter(
        x=filtered_df['Day'],
        y=filtered_df['Wealth ($)'],
        fill='tonexty',
        fillcolor='rgba(135, 206, 250, 0.5)',  
        showlegend=False,
        name='Fill Area',
        mode='lines+markers',
        line={'color': 'rgba(135, 206, 250, 1.0)'},
    ))

    fig.add_trace(
    go.Scatter(
        x=filtered_df['Day'],
        y=filtered_df['Cumul P&L ($)'],
        showlegend=False,
        mode='lines+markers',
        line={'color': 'rgba(0, 0, 0, 0.0)'},
        # ^^ set color to invisble. visible=True is necessary for the scondary axis
        visible=True,
        hoverinfo='skip'
    ), 
    secondary_y=True
)
    
    fig.update_layout(
        margin=dict(r=0),
        title={'text': "Wealth", 'font': {'color': 'black'}},
        plot_bgcolor='white',
        xaxis=dict(
            title='Day',
            color='black',
            showgrid=True,
            gridcolor='lightgrey',
            gridwidth=1,
            showline=True,
            linewidth=2,
            linecolor='black',
            mirror=True
        ),
        xaxis_range=[filtered_df['Day'].min(), filtered_df['Day'].max()]
    )

    fig.update_yaxes(
        dict(
            title='Wealth ($)',
            color='black',
            showgrid=True,
            gridcolor='lightgrey',
            gridwidth=1,
            showline=True,
            linewidth=2,
            linecolor='black',
            tickmode = 'linear',
            tick0 = 0.0,
            dtick = 1.0
        ),
        selector=0
    )

    fig.update_yaxes(
        dict(
            title='Cumul P&L ($)',
            color='black',
            showline=True,
            linewidth=2,
            linecolor='black',
            tickmode = 'linear',
            tick0 = 0.0,
            dtick = 1.0
        ),
        selector=1
    )

    return fig

```

Now however I obtain an odd behavior. When I focus on a specific part of the plot by dragging the slider that I have created, I notice that the dashed horizontal line starts to have some black markers, which are not initially visible. What is causing this behavior?

 ![Screenshot 2024-02-15 at 12.22.43](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/1/d/1d2349c4d7b6d0b103fe0fcaed8180fb159992fd.png)

 ![Screenshot 2024-02-15 at 12.23.00](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/e/2/e2a740737489d29396255a343ccc1b782bda6814.png)

I have tried to remove the markets all together by setting `mode='lines'` instead of `mode='lines+markers'` but this is having no effect. If needed I could upload the entire code with the data. Thank you again for your wonderful answer.

---

<div class="post-metadata">

**Author:** ![AIMPED](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/aimped/32/21758_2.png) [@AIMPED](https://community.plotly.com/u/AIMPED)\
**Post date:** [February 15, 2024, 10:57pm UTC](https://community.plotly.com/t/add-both-primary-and-secondary-axis-for-same-line/82375/4 "2024-02-15T22:57:17Z")

</div>

That’s strange. How do you create the slider?

Not sure where these markers come from. Try changing to this

> [@nohedge](#):
>
> ```auto
> fig.add_trace(
> go.Scatter(
> x=filtered_df['Day'],
> y=np.ones(shape=filtered_df.shape[0]) * starting_wealth,
> name='starting wealth',
> showlegend=False,
> line={'color': 'rgba(0, 0, 0, 1.0)', 'dash': 'dash'}
> mode='lines+markers'
> marker_color= 'rgba(0, 0, 0, 0.0)'
> ))
> 
> ```
