I am trying to make a movie showing the evolution of an estimator. The idea is to draw the history of the estimator after each update. I want to do this as a movie as some updates come in out of sequence and this has been the easiest way to display this.
The animation_frame
option for scatter or line in ploty.express
has worked well but when I try and color points, or use anything that makes a legend, I am running into issues.
Here is a code snippet showing the first problem. It is a simple scatter of some y=x
points. Each frame has one new point compared to the last and points above (3, 3)
are red. Doing this in what I think is the simplest way leads to red points not being displayed in the legend or on the graph.
import numpy as np
import pandas as pd
import plotly.express as px
# Make a y=x line that grows from origin (0, 0), to (9, 9)
x = [val for stop in range(10) for val in np.arange(0, stop, 1)]
frame_id = [stop for stop in range(10) for _ in np.arange(0, stop, 1)]
y = x
color = ['blue' if val < 3 else 'red' for val in x]
simple_df = pd.DataFrame().assign(
x=x,
y=y,
color=color,
frame_id=frame_id,
).sort_values(by='frame_id')
px.scatter(
simple_df, x='x', y='y', color='color', animation_frame='frame_id'
).update_layout(
title='Animation of y=x line growing from (0, 0) to (9, 9)',
).update_yaxes(range=[-1, 10]).update_xaxes(range=[-1, 10]).show()
I have sort of fixed this by adding a starting frame with all points displayed. I have made this less ugly in real code by setting the size of the points to zero, but am displaying them here for clarity. The issue now is only colors that are added on a given frame are handled correctly. So on frames 1-3 blue points only are updated. This leads to the red points hanging around until the first frame where a red point is added. This leads to further issues when going forwards and backwards using the slider but I think this movie rolling forward displays the core of the issue.
zero_frame = pd.concat([simple_df.assign(frame_id=0), simple_df])
px.scatter(
zero_frame, x='x', y='y', color='color', animation_frame='frame_id'
).update_layout(
title='Animation of y=x line growing from (0, 0) to (9, 9)',
).update_yaxes(range=[-1, 10]).update_xaxes(range=[-1, 10]).show()
Does anyone have any fixes for this? To re-iterate, in the toy example I would like a line of blue dots to grow from (1, 1)
and have dots after (3, 3)
be red, with one dot added per frame. I think if this worked I could get it to behave on real data.
I would do it like below with adding a fake red dot outside the y range:
import numpy as np
import pandas as pd
import plotly.express as px
# define your frames
frame = np.arange(10)
#define x values for each frame
x = [np.arange(f+1) for f in frame]
# add the y values
y = x
# define the colors
color = [ ['blue' if val <3 else 'red' for val in xx] for xx in x]
#put that in a dataframe
df = pd.DataFrame().assign(frame = frame,x=x,y=y,color=color)
#explode the lists of list
df = df.explode(['x','y','color'])
# add a fake point on frame 0 with red color and y value below the y range min
df.loc[-1] = [0,3,-2,'red']
# plot it
px.scatter(df,x='x',y='y',color='color',animation_frame='frame').update_yaxes(range=[-1,10]).update_xaxes(range=[-1,10])
you will just see a somehow weird behavior for the first red point, sliding from the bottom but not sure if/how it can be done in a better way.
Another approach could be to define it with correct x and y but with a size at 0. Then the glitch would be that the dot will "grow" in size on the correct spot.