I am working on a school project. I have created the following callback to update a graph. The graph simply plots :
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.
# 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:
I have attempted to do this modification following the steps illustrated here. In particular, I made the following changes:
from plotly.subplots import make_subplots
Changed fig = go.Figure()
to fig = make_subplots(specs=[[{"secondary_y": True}]])
Added secondary_y=True
as follows:
# 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:
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.
Two things:
The problem of non-aligning fill and graph is because you gave the line its own axis (secondary_y = True). Without specifying, this gets assigned a range automatically. You can solve this by setting the range of both Y-axes manually, or by grabbing the auto-range of the fill and assigning it to the second Y-axis.
The easiest solution to your original question is to add an empty trace with an y-axis which is defined as the primary axis range minus the starting value.
'
full_fig = fig.full_figure_for_development()
org_yrange = full_fig.layout.yaxis.range
sec_yrange = (0, org_yrange[1] - org_yrange[0])
fig.add_trace(go.Scatter(x=[],y=[],yaxis='y2'))
fig.update_layout(
yaxis2=dict(
range=sec_yrange,
side='right',
overlaying='y',
),
)