Plotting a animated stocks' prices using Matplotlib - python

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

Related

Plotting a scatterplot gif from a dataframe

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

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

Animated lissajous curve not visible python

I am very new to animating in python so please bear with me.
So I am trying to make this Lissajous curve animate like the one on this website
I do have code of the lissajous curve stationary if needed. I thought by changing the pi/2 (in the code it's f) to be smaller and bigger would replicate it but the graph doesn't appear. Thank you in advance.
Attempt:
# Import our modules
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
a= 1
A = 1
B = 1
b = 3
c = 1
D = 1
fig = plt.figure()
f=3.14/2
while f > -3.14/2:
f-=1
xdata, ydata = [], []
ax = plt.gca()
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
t = 0.1*i
x = A*np.sin(a*t+f) + c
y = B*np.sin(b*t) + D
xdata.append(x)
ydata.append(y)
line.set_data(xdata, ydata)
# ax.set_facecolor('xkcd:black')
return line,
anim = FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)
anim.save('abclogo.gif', writer='pillow')
You have to include the axes limits
ax = plt.gca()
ax.set_xlim(0,2)
ax.set_ylim(0,2)
line, = ax.plot([], [], lw=2)
Alternately, and slightly more efficient would be to not use append inside the animate function by doing the following:
# Import our modules
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
a= 1
A = 1
B = 1
b = 3
c = 1
D = 1
f=3.14/2
while f > -3.14/2:
f-=1
seq=np.arange(0,200,1)
x = A*np.sin(a*0.1*seq+f) + c
y = B*np.sin(b*0.1*seq) + D
fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')
def animate(i):
line.set_data(x[:i], y[:i])
return line,
anim = FuncAnimation(fig, animate, frames=len(x),interval=25, blit=True)
anim.save('abclogo.gif', writer='imagemagick')
plt.show()
Edit 2:
FuncAnimation doesn't offer a lot of control. For e.g. you won't be able to access axes elements and modify them. You can achieve better control by making use of for loop as shown here:
import numpy as np
import matplotlib.pyplot as plt
import math
###initializing the parameters##################
M=1
N=2
########setup the plot################
fig, ax = plt.subplots()
t = np.arange(0, 1000, 1)
x = np.sin(M*0.1*t)
y = np.sin(N*0.1*t+math.pi/2.0)
ax.set_xlim(-1.25,1.25)
ax.set_ylim(-1.25,1.25)
##################
for i in t:
phi=np.arange(0,10*math.pi,math.pi/50.)
#phase shifting to give the impression of rotation
y = np.sin(N*0.1*t+phi[i])
line, = ax.plot(x[:i],y[:i],c='black')
plt.pause(0.01)
#remove the track
line.remove()
del line
The animation is shown here

How can I animate Pandas dataframe using matplotlib

I have a dataframe that I want to animate (line chart) using matplotlib. My x and y values:
here x = df.index and y = df['Likes']
x y
0 200000
1 50000
2 1000000
.so on.. ....
Code I tried:
from matplotlib import pyplot as plt
from matplotlib import animation
import pandas as pd
df = pd.read_csv("C:\\Users\\usr\\Documents\\Sublime\\return_to_windows\\Files\\cod2019.txt", sep='\t')
fig = plt.figure()
ax = plt.axes(xlim=(0, 18), ylim=(6514, 209124))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(df.index[i], df['Likes'][i])
return line,
anim = animation.FuncAnimation(fig, animate, frames=len(df['Likes']), init_func=init, interval=300, blit=True)
plt.show()
I have tried this, but it is showing blank output with no error message. I am using python 3.83, windows machine. Can I do this using numpy? Almost all of the examples used numpy data in FuncAnimation.
I have solved it myself, I have used code of "vkakerbeck" from github as a guide to add more data points:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
df = pd.read_csv("C:\\Users\\usr\\Documents\\Sublime\\return_to_windows\\Files\\cod2019.txt", sep='\t')
dg = df['Likes']
x_data = []
y_data = []
fig, ax = plt.subplots()
ax.set_xlim(0, len(dg))
ax.set_ylim(0, dg.max() * 1.04) # multiplied with 1.04 to add some gap in y-axis
line, = ax.plot(0, 0)
This part is for formatting
ax.set_xlabel('Part No')
ax.set_ylabel('Number of Likes')
ax.set_title('Likes in Call of Duty 2019')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
fig = plt.gcf()
fig.set_size_inches(12.8, 7.2) # 720p output
I have used this from that guide to add more data points to make the animation less jumpy:
x = np.array(dg.index)
y = np.array(dg)
def augment(xold, yold, numsteps):
xnew = []
ynew = []
for i in range(len(xold) - 1):
difX = xold[i + 1] - xold[i]
stepsX = difX / numsteps
difY = yold[i + 1] - yold[i]
stepsY = difY / numsteps
for s in range(numsteps):
xnew = np.append(xnew, xold[i] + s * stepsX)
ynew = np.append(ynew, yold[i] + s * stepsY)
return xnew, ynew
XN, YN = augment(x, y, 3)
augmented = pd.DataFrame(YN, XN)
ylikes = augmented[0].reset_index() # Index reset to avoid key error
Main Function:
def animation_frame(i):
x_data.append(augmented.index[i])
y_data.append(ylikes[0][i])
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
plt.cla()
plt.tight_layout()
anima = animation.FuncAnimation(fig, func=animation_frame, frames=len(augmented), interval=80)
plt.show()
Export as mp4
Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, bitrate=1000)
anima.save('lines3.mp4', writer=writer)

How to get animation plot to display time on the x-axis as opposed to numerical value

I am reading data in from a csv file and plotting a "live stream" using matplotlib animation. Everything is working fine except that I want to display time on the x-axis as opposed to the "matplotlib.dates.date2num" values. Is there a simple way to do this?
import numpy
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import animation
import datetime
from numpy import genfromtxt
cv = numpy.genfromtxt ('file.csv', delimiter=",")
second = cv[:,0]
third = cv[:,2]
FMT = '%Y-%m-%d-%H%M%S.%f'
data = numpy.genfromtxt('file.csv', delimiter=',', skip_header=2,names=['t', 'in', 'x', 'y','z'], dtype=['object', 'int8', 'float', 'float', 'float'])
d = [datetime.datetime.strptime(i.decode('ascii'), FMT) for i in data['t']]
conversion3 = [matplotlib.dates.date2num(j) for j in d]
mytime3 = numpy.array(conversion3)
x = mytime3
y = third
fig, ax = plt.subplots()
line, = ax.plot([], [], 'b-')
ax.margins(0.05)
def init():
line.set_data(x[:2],y[:2])
return line,
def animate(i):
win = 150
imin = min(max(0, i - win), x.size - win)
xdata = x[imin:i]
ydata = y[imin:i]
line.set_data(xdata, ydata)
ax.relim()
ax.autoscale()
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, interval=25)
plt.show()
The csv file is of the form:
2016-07-11-095303.810,1,79
2016-07-11-095303.900,1,77
2016-07-11-095303.990,1,59
2016-07-11-095304.080,1,48
2016-07-11-095304.170,1,48
2016-07-11-095304.260,1,77
2016-07-11-095304.350,1,81
2016-07-11-095304.440,1,63
2016-07-11-095304.530,1,54
2016-07-11-095304.620,1,29
You may use plot_date instead of plot, this will format the ticks automatically. You then should plot the dates, not the converted numbers.
The following runs fine:
u = u"""2016-07-11-095303.810,1,79
2016-07-11-095303.900,1,77
2016-07-11-095303.990,1,59
2016-07-11-095304.080,1,48
2016-07-11-095304.170,1,48
2016-07-11-095304.260,1,77
2016-07-11-095304.350,1,81
2016-07-11-095304.440,1,63
2016-07-11-095304.530,1,54
2016-07-11-095304.620,1,29"""
import io
import numpy
import matplotlib.pyplot as plt
from matplotlib import animation
import datetime
from numpy import genfromtxt
cv = numpy.genfromtxt (io.StringIO(u), delimiter=",")
second = cv[:,0]
third = cv[:,2]
FMT = '%Y-%m-%d-%H%M%S.%f'
data = numpy.genfromtxt(io.StringIO(u), delimiter=',', skip_header=2,
names=['t', 'in', 'x', 'y','z'],
dtype=['object', 'int8', 'float'])
d = [datetime.datetime.strptime(i.decode('ascii'), FMT) for i in data['t']]
x = d
y = data["x"]
fig, ax = plt.subplots()
line, = ax.plot_date([], [], 'b-')
ax.margins(0.05)
def init():
line.set_data(x[:2],y[:2])
return line,
def animate(i):
imin = 0 #min(max(0, i - win), x.size - win)
xdata = x[imin:i+2]
ydata = y[imin:i+2]
line.set_data(xdata, ydata)
ax.relim()
ax.autoscale()
return line,
anim = animation.FuncAnimation(fig, animate, frames=7,init_func=init, interval=150)
plt.show()

Categories