What am I doing wrong? Can anyone help me? Or give me specific keywords for google search (I'm sure I'm not the first)? Have been dealing with this problem for over 8h now, cant find something on the internet.
Full Notebook Link (problem at the end): Kaggle Notebook
My code:
dict_data = data.copy()
dict_data.drop(["Date"], axis=1, inplace=True)
series_data = dict_data.to_dict()
bar_label = []
for key in dict_data:
bar_label.append(key)
bar_color = generate_color_series(28)
fig = plt.figure(figsize=(7, 5))
axes = fig.add_subplot(1, 1, 1)
axes.set_xlim(0, 35)
axes.set_xlabel("Popularity in %")
def animate(i):
i_value = []
for key in dict_data:
i_value.append(dict_data[key][i])
i_value = tuple(i_value)
plt.barh(bar_label, i_value, color=bar_color)
ani = FuncAnimation(fig, animate, interval=30)
%time ani.save('myAnimation1.gif', writer='imagemagick', fps=15)
plt.close()
Output:
[Picture]
The reason is that the new graph is being drawn with the previous drawing still intact, as described in the comments. So, the easiest way to deal with this is to put the action to clear the current graph in the loop process. Clearing the graph removes the x-axis limit and changes the height of the bar graph, so the x-axis limit is added again.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
# set global variable for color palette (plots) and grid style
PALETTE = "magma_r" # my favourite palettes: flare, CMRmap_r, magma_r
sns.set(style="darkgrid")
# function that generates n color values out of defined PALETTE
def generate_color_series(n):
segments = cm.get_cmap(PALETTE, n)
return segments(range(n))
data = pd.read_csv('./data/Most Popular Programming Languages from 2004 to 2022.csv', sep=',')
data["Date"] = pd.to_datetime(data["Date"])
dict_data = data.copy()
dict_data.drop(["Date"], axis=1, inplace=True)
series_data = dict_data.to_dict()
bar_label = []
for key in dict_data:
bar_label.append(key)
bar_color = generate_color_series(28)
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(1, 1, 1)
ax.set_xlim(0, 35)
ax.set_xlabel("Popularity in %")
def animate(i):
i_value = []
for key in dict_data:
i_value.append(dict_data[key][i])
i_value = tuple(i_value)
ax.cla()
ax.set_xlim(0, 35)
ax.barh(bar_label, i_value, color=bar_color)
ani = FuncAnimation(fig, animate, interval=30)
from IPython.display import HTML
plt.close()
HTML(ani.to_html5_video())
Related
I have a Dataframe with 6 rows of data and 4 columns. Is there any way to generate a gif scatterplot (y which are the 4 columns in different color versus x which are the index rows) plot in which in every frame of the gif, first data point of the Column 1 and its first respective row data is plotted in different color versus the shared x axis which are the indexes, at the same time, column 2, 3 and 4 first data points are plotted, and this goes progressively until the last 6th point is plotted for all of the columns? If a gif is not possible at all, is there any other way to generate at least movie so that I can include in my ppt slide? I appreciate any feedback you might have! The error I am getting is generating an empty plot and saying: TypeError: cannot unpack non-iterable AxesSubplot object. But I am not sure if this is preventing the result from the plotting.
This is a sample of my data and code effort:
import pandas as pd
import numpy as np
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
from itertools import count
from IPython import display
row_data = np.arange(0, 6)
column_X = np.random.rand(6,)
column_Y = np.random.rand(6,)
column_Z = np.random.rand(6,)
column_K = np.random.rand(6,)
my_df = pd.DataFrame()
my_df['column_X'] = column_X
my_df['column_Y'] = column_Y
my_df['column_Z'] = column_Z
my_df['column_K'] = column_K
my_df.index = row_data
my_df['index'] = row_data
def animate(j):
fig, ax = plt.subplot(sharex= True)
ax[1]=my_df['column_X', color = 'blue']
ax[2]=my_df['column_Y', color = 'red']
ax[3]=my_df['column_Z', color = 'brown']
ax[4]=my_df['column_K', color = 'green']
y=my_df['index']
x.append()
y.append()
plt.xlabel(color = 'blue')
plt.ylabel(color = 'red')
ax.set_ylabel("progressive sales through time")
ax.set_xlabel("progressive time")
plt.plot(x,y)
animation_1 = animation.FuncAnimation(plt.gcf(),animate,interval=1000)
plt.show()
# Inside Jupyter:
video_1 = animation_1.to_html5_video()
html_code_1 = display.HTML(video_1)
display.display(html_code_1)
plt.tight_layout()
plt.show()
Good question! matplotlib animations can be tricky. I struggled a bit with this one, mainly because you want different colors for the different columns. You need 4 different Line2D objects to do this.
# VSCode notebook magic
%matplotlib widget
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
my_df = pd.DataFrame()
my_df["column_X"] = np.random.rand(6)
my_df["column_Y"] = np.random.rand(6)
my_df["column_Z"] = np.random.rand(6)
my_df["column_K"] = np.random.rand(6)
fig, ax = plt.subplots()
# four y-data lists, x-data is shared
xdata, y1, y2, y3, y4 = [], [], [], [], []
# four Line3D objects with different colors
graph1, = ax.plot([], [], 'ro-')
graph2, = ax.plot([], [], 'go-')
graph3, = ax.plot([], [], 'bo-')
graph4, = ax.plot([], [], 'ko-')
# set up the plot
plt.xlim(-1, 6)
plt.xlabel('Time')
plt.ylim(0, 1)
plt.ylabel('Price')
# animation function
def animate(i):
xdata.append(i)
y1.append(my_df.iloc[i,0])
y2.append(my_df.iloc[i,1])
y3.append(my_df.iloc[i,2])
y4.append(my_df.iloc[i,3])
graph1.set_data(xdata, y1)
graph2.set_data(xdata, y2)
graph3.set_data(xdata, y3)
graph4.set_data(xdata, y4)
return (graph1,graph2,graph3,graph4,)
anim = animation.FuncAnimation(fig, animate, frames=6, interval=500, blit=True)
anim.save('test.mp4')
#plt.show()
Here's the resulting .gif (converted from .mp4 using Adobe Express):
Good Morning
I have done a bar graph, and I want than a line change of position in the graph bar with a mouse event. Im a novice and I couldn't get it to work, I put the code underneath.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
np.random.seed(12345)
df = pd.DataFrame([np.random.normal(32000,200000,3650),
np.random.normal(43000,100000,3650),
np.random.normal(43500,140000,3650),
np.random.normal(48000,70000,3650)],
index=[1992,1993,1994,1995])
df['mean']=df.mean(axis=1)
df['std']=df.std(axis=1)
fig, ax = plt.subplots()
years = df.index.values.tolist()
averages = df['mean'].values.tolist()
stds =df['std'].values.tolist()
x_pos = np.arange(len(years))
min_value = int(df.values.min())
max_value =int(df.values.max())
yaxis = np.arange(min_value,max_value, 100)
plt.bar(x_pos,averages, color='red')
ax.set_xticks(x_pos)
ax.set_xticklabels(years)
ax.set_ylabel('Values')
ax.set_title('Average values per year')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
line = ax.axhline(y=10000,color='black')
traza=[]
def onclick(event):
if not event.inaxes:
return
traza.append('onclick '+event.ydata())
line.set_data([0,1], event.ydata())
plt.connect('button_press_event', onclick)
plt.show()
I can't even get the onclick procedure done. Could you help me?
Thank you
Several things are going wrong:
event.ydata is not a function, so you can't call it as event.ydata(). Just use it directly.
When some graphical information changes, the image on the screen isn't updated immediately (as there can be many changes and redrawing continuously could be very slow). After all changes are done, calling fig.canvas.draw() will update the screen.
'onclick ' + event.ydata doesn't work. 'onclick ' is a string and ydata is a number. To concatenate a string and a number, first convert the number to a string: 'onclick ' + str(event.ydata)
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
def onclick(event):
if not event.inaxes:
return
line.set_data([0, 1], event.ydata)
fig.canvas.draw()
np.random.seed(12345)
df = pd.DataFrame([np.random.normal(32000, 200000, 3650),
np.random.normal(43000, 100000, 3650),
np.random.normal(43500, 140000, 3650),
np.random.normal(48000, 70000, 3650)],
index=[1992, 1993, 1994, 1995])
df['mean'] = df.mean(axis=1)
df['std'] = df.std(axis=1)
fig, ax = plt.subplots()
years = df.index.values.tolist()
averages = df['mean'].values.tolist()
stds = df['std'].values.tolist()
x_pos = np.arange(len(years))
min_value = int(df.values.min())
max_value = int(df.values.max())
yaxis = np.arange(min_value, max_value, 100)
plt.bar(x_pos, averages, color='red')
ax.set_xticks(x_pos)
ax.set_xticklabels(years)
ax.set_ylabel('Values')
ax.set_title('Average values per year')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
line = ax.axhline(y=10000, color='black', linestyle=':')
plt.connect('button_press_event', onclick)
plt.show()
I want my animation only to show the most previous point, and I believe that I have to adjust something around this line: plt.gca().cla()
Can anyone tell me what I am doing wrong? In my animation, all the points stay visible, while I only want to show the most previous points. Any suggestions?
This is my code:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
title = 'Occ'
x = np.array(df.x)
y = np.array(df.y)
Writer = animation.writers['ffmpeg']
writer = Writer(fps = 4, bitrate = 1800)
fig = plt.figure(figsize = (12, 8))
def animate(i):
plt.gca().cla()
data = df.iloc[:int(i + 1)] # select data range
p = sns.scatterplot(x = 'x', y = 'y', hue = 'id', data = data, s = 200, alpha = 0.5)
p.tick_params(labelsize = 17)
plt.setp(p.lines, linewidth = 7)
plt.xlim(0, 500)
plt.ylim(0, 500)
plt.xlabel('X', fontsize = 20)
plt.ylabel('Y', fontsize = 20)
plt.title('Occ', fontsize = 20)
ani = matplotlib.animation.FuncAnimation(fig, animate, frames = len(df), repeat = True, blit=False)
ani.save('Occ.mp4', writer = writer)
The line
data = df.iloc[:int(i + 1)] # select data range
select all the rows from 0 to i+1, therefore you are showing a growing number of points at each frame. If you want to show only the current point, you should do:
data = df.iloc[i] # select data range
I am trying to animate a time-series plot with Matplotlib but the figure always comes out empty. I attached my code below. Any help would be appreciated
import yfinance as yf
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
# loading the data
indices = ["^GSPC","TLT", ]
data = yf.download(indices,start='2020-01-01')
data = data['Adj Close']
inv_growth = (data.pct_change().dropna() + 1).cumprod()
# plotting the data
fig, ax = plt.subplots()
ax.set_xlim(inv_growth.index[0], inv_growth.index[-1])
ax.set_ylim(940, 1100)
line, = ax.plot(inv_growth.index[0], 1000)
x_data = []
y_data = []
def animation_frame(date):
x_data.append(date)
y_data.append(inv_growth.loc[date])
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
animation = FuncAnimation(fig,
func=animation_frame,
frames=list(inv_growth.index),
interval = 100)
plt.show()
Your problem is that you are trying to plot two values at the same time. If you want two lines, you have to create two lines and update their respective data.
Here is a slightly simplified version of your code (also, your y-scale seemed to be a factor 1000 off).
import yfinance as yf
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
# loading the data
indices = ["^GSPC","TLT", ]
data = yf.download(indices,start='2020-01-01')
data = data['Adj Close']
inv_growth = (data.pct_change().dropna() + 1).cumprod()
# plotting the data
fig, ax = plt.subplots()
ax.set_xlim(inv_growth.index[0], inv_growth.index[-1])
ax.set_ylim(0.940, 1.100)
line1, = ax.plot([], [])
line2, = ax.plot([], [])
def animation_frame(i):
temp = inv_growth.iloc[:i]
line1.set_data(temp.index, temp[0])
line2.set_data(temp.index, temp[1])
return line1,line2,
animation = FuncAnimation(fig,
func=animation_frame,
frames=range(inv_growth.index.size),
interval = 100)
plt.show()
I have a bunch of points in a scatterplot which overlap. I am using FuncAnimation to create an animation. In successive frames I would like to change which appear in front of the others.
As a simple MCVE, consider the code below in which each frame makes a different set of points be red. However, these red points are often largely obscured by other points. I would like to make these red points come to the front.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random
def update(time, plotted_points, N):
#in here I would like to update the order that the points are plotted in. I would like the red points to come to the front.
color_change_indices = set(random.choices(range(N), k=1000))
colors = ['red' if n in color_change_indices else 'gray' for n in range(N)]
plotted_points.set_color(colors)
return plotted_points
def animate(N):
x = [random.random() for val in range(N)]
y = [random.random() for val in range(N)]
fig = plt.figure()
ax = fig.add_subplot(111)
plotted_points = ax.scatter(x, y, color='gray')
fargs = (plotted_points, N)
ani = FuncAnimation(fig, update, frames = range(100), fargs = fargs)
plt.show()
animate(10000)
I can change their color. I can move their coordinates. However, so far I cannot modify their relative depth.
So far the best idea I have is that perhaps I should delete the plotted points and then replot them. But I don't have a deep understanding of matplotlib, and my attempts to delete them have failed so far.
You can have a second scatter plot, red coloured and with a higher zorder, and update its points:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random
plt.ion()
def update(time, red_plotted_points, points, N):
indexes = set([random.choice(range(N)) for i in range(int(N/2))]) # for instance!
new_points = [points[i] for i in indexes]
red_plotted_points.set_offsets(new_points)
def animate(N):
x = [random.random() for val in range(N)]
y = [random.random() for val in range(N)]
points = [[x[i], y[i]]for i in range(N)]
fig = plt.figure()
ax = fig.add_subplot(111)
plotted_points = ax.scatter(x, y, color='gray', zorder=1)
red_plotted_points = ax.scatter([], [], color='red', zorder=2) # starts empty
fargs = (red_plotted_points, points, N)
ani = FuncAnimation(fig, update, frames=range(100), fargs=fargs,
interval=200, repeat=True)
ani._start()
fig.show()
return fig, ani
if __name__ == '__main__':
fig, ani = animate(100)
(python 2.7.14, matplotlib 2.1.1)
Edit: Updated to also run on Python 3.6.3, matpotlib 2.1.0
I'm not sure why, but it seems that if a reference is not kept to the FuncAnimation it does not work on Python 3.6. Thanks to Joel (comments) for noticing.
In order to bring red points in front, you need to sort the data where red points come last. In a following code, instead of sorting the points by their color, it shuffles the data and draws last 1000 points with red, at each time update is called.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random
import numpy as np # np.random.shuffle
def update(time, plotted_points):
# Extract data and colors
data = plotted_points.get_offsets()
colors = plotted_points.get_facecolor()
# shuffle the data [N, 2] along the first axis
np.random.shuffle(data)
# Set shuffled data and reset colors
plotted_points.set_offsets(data)
plotted_points.set_color(colors)
return plotted_points
def animate(N):
x = [random.random() for val in range(N)]
y = [random.random() for val in range(N)]
fig = plt.figure()
ax = fig.add_subplot(111)
# Need to define colors at first.
colors = ['red' if idx < 1000 else 'gray' for idx in range(N)]
colors.reverse()
plotted_points = ax.scatter(x, y, color=colors)
fargs = (plotted_points,)
ani = FuncAnimation(fig, update, frames = range(100), fargs = fargs)
plt.show()
animate(10000)