Plotting a scatterplot gif from a dataframe - python

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):

Related

Matplotlib animation, bars are getting white after a while

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())

Create 3D Plot (not surface, scatter), where colour depends on z values

I want to create and save a number of sequential plots so I can then make an mp4 movie out of those plots. I want the color of the plot to depend on z (the value of the third axis):
The code I am using:
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
file_dir1 = r"C:\Users\files\final_files\B_6_sec\_read.csv"
specs23 = pd.read_csv(file_dir1, sep=',')
choose_file = specs23 # Choose file betwenn specs21, specs22,...
quant = 0 # Choose between 0,1,...,according to the following list
column = ['$\rho$', '$V_{x}$', '$V_{y}$', '$V_{z}$','$B_{x}$', '$B_{y}$','$B_{z}$','$Temperature$']
choose_column = choose_file[column[quant]]
resolution = 1024 # Specify resolution of grid
t_steps = int(len(specs23)/resolution) # Specify number of timesteps
fig, ax = plt.subplots(subplot_kw={"projection": "3d"},figsize=(15,10))
# Make data.
X = np.arange(0, resolution, 1)
Y = np.arange(0, int(len(specs23)/resolution),1)
X, Y = np.meshgrid(X, Y)
Z = choose_file[column[quant]].values
new_z = np.zeros((t_steps,resolution)) # Selected quantity as a function of x,t
### Plot figure ###
for i in range(0,int(len(choose_file)/resolution)):
zs = choose_column[i*resolution:resolution*(i+1)].values
new_z[i] = zs
for i in range(len(X)):
ax.plot(X[i], Y[i], new_z[i]) #%// color binded to "z" values
ax.zaxis.set_major_locator(LinearLocator(10))
# A StrMethodFormatter is used automatically
ax.zaxis.set_major_formatter('{x:.02f}')
plt.show()
What I am getting looks like this:
I would like to look it like this:
I have created the second plot using the LineCollection module. The problem is that it prints all the lines at once not allowing me to save each separately to create a movie.
You can find the dataframe I am using to create the figure here:
https://www.dropbox.com/s/idbeuhyxqfy9xvw/_read.csv?dl=0
The poster wants two things
lines with colors depending on z-values
animation of the lines over time
In order to achieve(1) one needs to cut up each line in separate segments and assign a color to each segment; in order to obtain a colorbar, we need to create a scalarmappable object that knows about the outer limits of the colors.
For achieving 2, one needs to either (a) save each frame of the animation and combine it after storing all the frames, or (b) leverage the animation module in matplotlib. I have used the latter in the example below and achieved the following:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt, numpy as np
from mpl_toolkits.mplot3d.art3d import Line3DCollection
fig, ax = plt.subplots(subplot_kw = dict(projection = '3d'))
# generate data
x = np.linspace(-5, 5, 500)
y = np.linspace(-5, 5, 500)
z = np.exp(-(x - 2)**2)
# uggly
segs = np.array([[(x1,y2), (x2, y2), (z1, z2)] for x1, x2, y1, y2, z1, z2 in zip(x[:-1], x[1:], y[:-1], y[1:], z[:-1], z[1:])])
segs = np.moveaxis(segs, 1, 2)
# setup segments
# get bounds
bounds_min = segs.reshape(-1, 3).min(0)
bounds_max = segs.reshape(-1, 3).max(0)
# setup colorbar stuff
# get bounds of colors
norm = plt.cm.colors.Normalize(bounds_min[2], bounds_max[2])
cmap = plt.cm.plasma
# setup scalar mappable for colorbar
sm = plt.cm.ScalarMappable(norm, plt.cm.plasma)
# get average of segment
avg = segs.mean(1)[..., -1]
# get colors
colors = cmap(norm(avg))
# generate colors
lc = Line3DCollection(segs, norm = norm, cmap = cmap, colors = colors)
ax.add_collection(lc)
def update(idx):
segs[..., -1] = np.roll(segs[..., -1], idx)
lc.set_offsets(segs)
return lc
ax.set_xlim(bounds_min[0], bounds_max[0])
ax.set_ylim(bounds_min[1], bounds_max[1])
ax.set_zlim(bounds_min[2], bounds_max[2])
fig.colorbar(sm)
from matplotlib import animation
frames = np.linspace(0, 30, 10, 0).astype(int)
ani = animation.FuncAnimation(fig, update, frames = frames)
ani.save("./test_roll.gif", savefig_kwargs = dict(transparent = False))
fig.show()

Plotting a animated stocks' prices using Matplotlib

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()

python plot several figures

I have 50 csv files. I use "for loop" to get the dataframe. Now I want plot these 50 figures seperately. 6 subplots in 1 plot. How can I get this? Thanks a lot.
path = 'E:/XXXX/'
files = os.listdir(path)
files_csv = list(filter(lambda x: x[-4:]=='.csv' , files))
for file1 in files_csv:
tmp1=pd.read_csv(path + file1)
my data is like below:
df = pd.DataFrame({'date': [20121231,20130102, 20130105, 20130106, 20130107, 20130108],'price': [25, 163, 235, 36, 40, 82]})
You can create a figure for each frame and use matplotlib.pyplot.subplot function to plot your 6 different plots. Help yourself with the example bellow. Hope this helps.
from math import pi
import numpy as np
import matplotlib.pyplot as plt
x1 = np.linspace(-2*pi, 2*pi, 50)
y1 = np.cos(x1)
x2 = np.linspace(-pi, pi, 50)
y2 = np.cos(x2)
plt.figure()
plt.grid(True)
plt.title('your title ' )
plt.subplot(121)
plt.plot(x1, y1, 'r', label= 'y1 = cos(x1)')
plt.legend(loc=1)
plt.subplot(122)
plt.plot(x2, y2, 'b', label = 'y2 = cos(x2)')
plt.legend(loc=1)
plt.show()
import matplotlib.pyplot as plt
import numpy as np
x1 = np.linspace(-1, 1, 50)
howmanyrowsyouwant = 1 # how many times 6 subplots you want
for x in range(howmanyrowsyouwant):
_, ax = plt.subplots(nrows=1, ncols=6, figsize=(24,4))
ax[0].set_title('title of first')
ax[0].plot(x1) # plot for first subplot
ax[1].set_title('title of second')
ax[1].plot(x1) # plot for second subplot
ax[2].set_title('title of third')
ax[2].plot(x1) # plot for third subplot
ax[3].set_title('title of fourth')
ax[3].plot(x1) # plot for fourth subplot
ax[4].set_title('title of fifth')
ax[4].plot(x1) # plot for fifth subplot
ax[5].set_title('title of sixth')
ax[5].plot(x1) # plot for sixth subplot
This produces six subplots in a row, as many times as you specify.

How to reorder nodes (change depth) in a scatterplot for an animation

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)

Categories