Troubles with making an animation of a 3D figure - python

I would like to animate my 3D figure, but somehow it doesnt work. The figure contains positional data in 3 axis (x,y,z) of 4 markers on a rigid body. I would like to animate how the positional data of all markers changes over time.
Can somebody help me?
This is the script:
#animation function
def animate_func(num, data, line):
ax.plot3D(x1[num:, 0], y1[num:, 0], z1[num:, 0], c='blue')
ax.plot3D(x1[num:], y1[num:], z1[num:], c='blue', marker='o')
ax.plot3D(x1[0, 0], y1[1, 0], z1[2, 0], c='black', marker='o')
#lines = ax.plot(x, y, z, lw=2, c='g')
path_scatter = ax.scatter(x1, y1, z1, lw=2, c= '#fe7d11', s=1)
start = ax.plot(x1[0], y1[0], z1[0], c='black', marker='o')
numDataPoints=len(t)
anim = animation.FuncAnimation(figure11, animate_func, frames=numDataPoints, interval=100, blit=False)
#lines = ax.plot(x, y, z, lw=2, c='g')
path_scatter = ax.scatter(x2, y2, z2, lw=2, c='#0d5ebc', s=1)
start = ax.plot(x2[0], y2[0], z2[0], c='black', marker='o')
numDataPoints=len(t)
anim = animation.FuncAnimation(figure11, animate_func, frames=numDataPoints, interval=100, blit=False)
#lines = ax.plot(x, y, z, lw=2, c='g')
path_scatter = ax.scatter(x3, y3, z3, lw=2, c='#409d2b', s=1)
start = ax.plot(x3[0], y3[0], z3[0], c='black', marker='o')
numDataPoints=len(t)
anim = animation.FuncAnimation(figure11, animate_func, frames=numDataPoints, interval=100, blit=False)
#lines = ax.plot(x, y, z, lw=2, c='g')
path_scatter = ax.scatter(x4, y4, z4, lw=2, c='#ce2602', s=1)
start = ax.plot(x4[0], y4[0], z4[0], c='black', marker='o')
numDataPoints=len(t)
anim = animation.FuncAnimation(figure11, animate_func, frames=numDataPoints, interval=100, blit=False)
writervideo = animation.FFMpegWriter(fps=60)
anim.save('animation.mp4', writer=writervideo)
plt.show()
#plt.close()
Thanks in advance!

Related

Colouring the area between two step lines with crossovers

I am trying to fill colours between two-step line plots. I have tried to do the same using fill_between function with step and interpolate parameters. However, I am not getting the output as expected. I am filling the region between two lines after comparing their values. Below is the code. Any help will be appreciated.
fig = plt.figure()
fig.tight_layout()
plt.subplot(2, 2, 1)
p1 = plt.step(df2['datetime'], df2['T1'], color='b', linewidth=3, where = 'post', label ='P1')
p2 = plt.step(df2['datetime'], df2['T3'], color='m', linewidth=3, where = 'post', label ='P2')
p3 = plt.fill_between(df2['datetime'], df2['T1'],df2['T3'], where = df2['T1'] <
df2['T3'],facecolor="blue", color='blue', alpha=0.25, step = 'post',interpolate = True ,label ='A1')
p4 = plt.fill_between(df2['datetime'], df2['T1'],df2['T3'], where = df2['T1'] >
df2['T3'],facecolor="red", color='red', alpha=0.25, step = 'post',interpolate = True, label ='A2')
plt.ylabel("T1", fontsize=12, color='black')
plt.xlabel("Hour", fontsize=12, color='black')
plt.grid(True)
plt.legend(loc='best',fontsize = 10)
plt.xticks(rotation = 90)
plt.subplot(2, 2, 2)
p1 = plt.step(df2['datetime'], df2[‘T2’], color='k', linewidth=3, where = 'post', label ='P1')
p2 = plt.step(df2['datetime'], df2['T3'], color='m', linewidth=3, where = 'post', label ='P2')
p3 = plt.fill_between(df2['datetime'], df2[‘T2’],df2['T3'], where = df2[‘T2’] <
df2['T3'],facecolor="blue", color='blue', alpha=0.25, step = 'post',label ='A1')
p4 = plt.fill_between(df2['datetime'], df2[‘T2’],df2['T3'], where = df2[‘T2’] >
df2['T3'],facecolor="red", color='red', alpha=0.25, step = 'post',label ='A2')
plt.ylabel("T2", fontsize=12, color='black')
plt.xlabel("Hour", fontsize=12, color='black')
plt.grid(True)
plt.legend(loc='best',fontsize = 10)
plt.xticks(rotation = 90)
I am also attaching my output for reference.
Left plot is with step = post and interpolate = True
Right plot is without interpolate.
As you can see filling in not working as expected near cross overs.
Apparently, fill_between between step plots and using a where parameter doesn't fill as expected.
A workaround is to mimic the step function via np.repeat:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(20)
y1 = np.random.rand(20)
y2 = np.random.rand(20)
xx = np.repeat(x, 2)[1:]
yy1 = np.repeat(y1, 2)[:-1]
yy2 = np.repeat(y2, 2)[:-1]
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(18, 4))
for ax in (ax1, ax2, ax3):
ax.step(x, y1, color='r', lw=3, where='post')
ax.step(x, y2, color='b', lw=3, where='post')
ax1.fill_between(x, y1, y2, color='b', alpha=0.3, step='post', where=y1 < y2)
ax1.fill_between(x, y1, y2, color='r', alpha=0.3, step='post', where=y1 > y2)
ax1.set_title('fill_between with step and where')
ax2.fill_between(x, y1, y2, color='b', alpha=0.3, step='post', where=y1 < y2, interpolate=True)
ax2.fill_between(x, y1, y2, color='r', alpha=0.3, step='post', where=y1 > y2, interpolate=True)
ax2.set_title('setting interpolate=True')
ax3.fill_between(xx, yy1, yy2, color='b', alpha=0.3, where=yy1 < yy2)
ax3.fill_between(xx, yy1, yy2, color='r', alpha=0.3, where=yy1 > yy2)
ax3.set_title('mimicking step')
plt.tight_layout()
plt.show()

make matplotlib fill_between overwrite the subplot in front

I am trying to plot several lines which partially overlap and occlude each other. This is what I tried:
# Create the figure
fig = plt.figure(figsize=(7, 4))
ax = plt.subplot(111)
# remove grid
ax.set_xticks([])
ax.set_yticks([])
# define data
X = np.linspace(-2*np.pi, 2*np.pi, 400)
Y1 = np.cos(2*X)
Y2 = X**2/10-0.5
ax.plot(X, Y1, lw=1)
ax.fill_between(X, Y1, -1, facecolor=(1,0,0,1))
ax.plot(X, Y2)
ax.fill_between(X, Y2, -1, facecolor=(1,1,1,1))
plt.show()
which produces
but when the second fill_between goes down to -1, I would like it to also occlude the blue graph. Like so:
Any suggestions are appreciated.
My solution:
# Create the figure
fig = plt.figure(figsize=(7, 4))
ax = plt.subplot(111)
# remove grid
ax.set_xticks([])
ax.set_yticks([])
# define data
X = np.linspace(-2*np.pi, 2*np.pi, 400)
Y1 = np.cos(2*X)
Y2 = X**2/10-0.5
# NEW #########################
for i in range(Y1.size):
if Y1[i] < Y2[i] :
Y1[i] = Y2[i]
###############################
ax.plot(X, Y1, lw=1, color="blue")
ax.fill_between(X, Y1, -1, facecolor=(1,0,0,1))
ax.plot(X, Y2, color="orange")
ax.fill_between(X, Y2, -1, facecolor=(1,1,1,1))
plt.show()
Output:
Basically at any point Y1 is less than Y2 we set the point on Y1 equal to Y2.
I ended up going with #JohanC's suggestion, as it makes it easier to generalize to more graphs. So for example
ax.plot(X, Y1, lw=1)
ax.fill_between(X, Y1, -1, facecolor=(1,0,0,1), zorder=2)
ax.plot(X, Y2)
ax.fill_between(X, Y2, -1, facecolor=(1,1,1,1), zorder=3)
ax.plot(X, Y3, lw=1, zorder=1)
ax.fill_between(X, Y3, -1, facecolor=(0,0,1,1), zorder=1)
ax.plot(X, Y4, lw=1, zorder=0)
ax.fill_between(X, Y4, -1, facecolor=(0,1,0,0.5), zorder=0)
Can plot something like this:

Animated plot for bar graph and line graph using python subplot

I was able to plot animated graphs using advice from the link below.
matplotlib animation multiple datasets
And my code is here.
tt = time_hr.values[:,0]
yy1 =data1.values[:,0]
yy2 =data2.values[:,0]
fig, ax = plt.subplots()
line1, = ax.plot(tt, yy1, color='k')
line2, = ax.plot(tt, yy2, color='r')
def update(num, tt, yy1, yy2, line1, line2):
line1.set_data(tt[:num], yy1[:num])
line1.axes.axis([0, 1, 0, 2500])
line2.set_data(tt[:num], yy2[:num])
line2.axes.axis([0, 1, 0, 2500])
return line1,line2
ani = animation.FuncAnimation(fig, update, len(time_hr), fargs=[tt, yy1, yy2, line1,line2],interval=25, blit=True)
plt.show()
I also have other data set for bar graph and I created animated bar plot using the code below.
x_pos = np.arange(95)
fig = plt.figure()
ax = plt.axis((-1,96,0,1000))
def animate(i):
plt.clf()
pic = plt.bar(x_pos, data3.iloc[i,:], color='c')
plt.axis((-1,96,0,1000))
return pic,
ani = animation.FuncAnimation(fig, animate, interval=25, repeat=False)
plt.show()
My ultimate goal is to plot both animated bar and line graph in one figure using subplot function so that bar graph will be (1,1) and the line graph will be at (2,1) position of the figure.
Could somebody help me to create animated bar and line graphs in one figure window in python ?
More specifically, how to combine both line graphs and bar graph in one animate function ?
Based on comments below, I modified the code like this.
x_pos = np.arange(95)
tt = time_hr.values[:,0]
yy1 =data1.values[:,0]
yy2 =data2.values[:,0]
fig, (ax1, ax2) = plt.subplots(nrows=2)
line1, = ax2.plot(tt, yy1, color='k')
line2, = ax2.plot(tt, yy2, color='r')
rects = ax1.bar(x_pos, data3.iloc[0,:], color='c')
def update(num, tt, yy1, yy2, x_pos, data3, line1, line2, rects):
line1.set_data(tt[:num], yy1[:num])
line1.axes.axis([0, 1, 0, 2500])
line2.set_data(tt[:num], yy2[:num])
line2.axes.axis([0, 1, 0, 2500])
ax1.clear()
rects= ax1.bar(x_pos, data3.iloc[num,:], color='c')
ax1.axis((-1,96,0,1000))
return line1,line2, rects
ani = animation.FuncAnimation(fig, update, len(time_hr), fargs=[tt, yy1, yy2, x_pos,data3, line1,line2,rects], interval=25, blit=True)
plt.show()
But I got error message like this.
"AttributeError: 'BarContainer' object has no attribute 'set_animated'"
Could you help me How to fix this error ?

Plot a vertical Normal Distribution in Python

This is my current code of my plotting with matplotlib:
from matplotlib import pyplot
import numpy as np
std=1.5
al=0.6
dpi=80
target=38.9675
mc_min=np.array([10-std, 15-std, 20-std, 25-std, 30-std, 35-std])
mc_max=np.array([2*std, 2*std, 2*std, 2*std, 2*std, 2*std])
mc_min_out=np.array([40-std, 45-std])
mc_max_out=np.array([2*std, 2*std])
x = np.linspace(10, 35, 6)
x_out=np.linspace(40, 45, 2)
a=35+((target-35)*1.5)
b=((target-35)*1.5)
#8,6
pyplot.figure(num=None, figsize=(8, 6), dpi=dpi, facecolor='w', edgecolor='k')
pyplot.bar(x, mc_min, width=3, color ='#000000', align='center', alpha=1)
pyplot.bar(x_out, mc_min_out, width=3, color ='#000000', align='center', alpha=al/2)
pyplot.bar(x, mc_max, width=3, bottom=mc_min, color ='#ff0000', align='center', alpha=al)
pyplot.bar(x_out, mc_max_out, width=3, bottom=mc_min_out, color ='#ff0000', align='center', alpha=al/2)
pyplot.scatter(35, target, s=20, c='y')
pyplot.scatter(35, a, s=20, c='b')
pyplot.scatter(30, a-5, s=20, c='b')
pyplot.scatter(25, a-10, s=20, c='b')
pyplot.scatter(20, a-15, s=20, c='b')
pyplot.scatter(15, a-20, s=20, c='b')
pyplot.scatter(10, a-25, s=20, c='b')
pyplot.axvline(x=35, ymin=0, ymax = 0.9, linewidth=1, color='k')
pyplot.axvline(x=30, ymin=0, ymax = 0.9, linewidth=1, color='k')
pyplot.axvline(x=25, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axvline(x=20, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axvline(x=15, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axvline(x=10, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axhline(y=10, xmin=0.04, xmax=0.12, linewidth=1, color='k')
pyplot.axhline(y=15, xmin=0.16, xmax=0.242, linewidth=1, color='k')
pyplot.axhline(y=20, xmin=0.278, xmax=0.36, linewidth=1, color='k')
pyplot.axhline(y=25, xmin=0.4, xmax=0.48, linewidth=1, color='k')
pyplot.axhline(y=30, xmin=0.515, xmax=0.6, linewidth=1, color='k')
pyplot.axhline(y=35, xmin=0.64, xmax=0.72, linewidth=1, color='k')
pyplot.axhline(y=target, xmin=0.67, xmax=0.69, linewidth=1, color='k')
pyplot.axhline(y=(a+b), xmin=0.66, xmax=0.70, linewidth=1, color='k')
pyplot.axhline(y=(a-5+b), xmin=0.54, xmax=0.58, linewidth=1, color='k')
pyplot.axhline(y=(a-10+b), xmin=0.42, xmax=0.46, linewidth=1, color='k')
pyplot.axhline(y=(a-15+b), xmin=0.3, xmax=0.34, linewidth=1, color='k')
pyplot.axhline(y=(a-20+b), xmin=0.18, xmax=0.22, linewidth=1, color='k')
pyplot.axhline(y=(a-25+b), xmin=0.06, xmax=0.10, linewidth=1, color='k')
pyplot.yticks(np.arange(0, 56, 5))
And this is the result:
My problem is that I want to plot a normal distribution in the vertical line that crosses the 35 x-positioned bar. The normal distribution would have a mean equal to the variable "a" and a standard deviation of value "b" and will fit between the edge of the red bar (35 x-positioned) and the top horizontal line that crosses the vertical 35 x-positioned line. The result would be as the second photo.
You can plot a Gaussian function in the position you want by adding x- and y-offsets to the plotted data. Here's an example function:
def draw_gaussian_at(support, sd=1.0, height=1.0,
xpos=0.0, ypos=0.0, ax=None, **kwargs):
if ax is None:
ax = plt.gca()
gaussian = np.exp((-support ** 2.0) / (2 * sd ** 2.0))
gaussian /= gaussian.max()
gaussian *= height
return ax.plot(gaussian + xpos, support + ypos, **kwargs)
xpos and ypos direct the center of the curve to that location, and sd and height control the shape of the curve. Use a negative value for height to have the curve "face" to the left. The support parameter is the range of y-values over which the curve runs, so in your case it would be something like np.linspace(a - 3.0 * b, a + 3.0 * b, 1000), which would plot the curve over 3 standard deviations centered at a.
Here's an example of the function's usage:
support = np.linspace(-2, 2, 1000)
fig, ax = plt.subplots()
for each in np.linspace(-2, 2, 5):
draw_gaussian_at(support, sd=0.5, height=-0.5, xpos=each, ypos=each, ax=ax, color='k')

python matplotlib plotting many subfigures with the same parameters

My plot is like the following
fig = plt.figure(figsize=(7,3))
ax1 = fig.add_subplot(1,3,1)
ax2 = fig.add_subplot(1,3,2)
ax3 = fig.add_subplot(1,3,3)
ax1.scatter(x11, y11, s=50, alpha=0.5, c='orange', marker='o')
ax1.scatter(x12, y12, s=50, alpha=0.5, c='blue', marker='s')
ax2.scatter(x21, y21, s=50, alpha=0.5, c='orange', marker='o')
ax2.scatter(x22, y22, s=50, alpha=0.5, c='blue', marker='s')
ax3.scatter(x31, y31, s=50, alpha=0.5, c='orange', marker='o')
ax3.scatter(x32, y32, s=50, alpha=0.5, c='blue', marker='s')
It seems kinda redundant to set s=50, alpha=0.5 over and over. Is there a way to set them once for all? Also for color and marker, is there a way to write them in one place so it's easier to modify?
You could do this:
fig = plt.figure(figsize=(7,3))
ax1 = fig.add_subplot(1,3,1)
ax2 = fig.add_subplot(1,3,2)
ax3 = fig.add_subplot(1,3,3)
xs = [x11, x12, x21, x22, x31, x32]
ys = [y11, y12, y21, y22, y31, y32]
cs = ['orange', 'blue']
ms = 'os'
for j in xrange(len(xs)):
ax1.scatter(xs[j], ys[j], s=50, alpha=0.5, c=cs[j % 2], marker=ms[j % 2])
I like organizing the data and styles, and then using that to organize the plotting. Generating some random data to make a runnable example:
import matplotlib.pyplot as plt
from numpy.random import random
fig, axs = plt.subplots(3, figsize=(7,3)) #axs is an array of axes
orange_styles = {'c':"orange", 'marker':'o'}
blue_styles = {'c':"blue", 'marker':'s'}
pts = []
for i in range(12):
pts.append(random(4))
orange_x = pts[0:3] # organized data is lists of lists
orange_y = pts[3:6]
blue_x = pts[6:10]
blue_y = pts[10:12]
for ax, x, y in zip(axs, orange_x, orange_y): #all the orange cases
ax.scatter(x, y, s=50, alpha=0.5, **orange_styles) # **kwds
for ax, x, y in zip(axs, blue_x, blue_y):
ax.scatter(x, y, s=50, alpha=0.5, **blue_styles)

Categories