How to make a gif out of subplot? - matplotlib

I'm using this code from matplotlib website to generate gif through list of images.
https://matplotlib.org/gallery/animation/dynamic_image2.html
However, I'm struggling to figure out how to make it work if I have subplot with two axes inside it. Thus, it is as if I have two images, which one should I append to the list?
EDIT: sample code:
ims = []
for i in range(60):
x += np.pi / 15.
y += np.pi / 20.
im = plt.imshow(f(x, y), animated=True)
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)

As explained in the page you linked, the array of artists passed to ArtistAnimation is a list of lists, each element of the list corresponds to one frame, where all the elements of the "inner" lists are updated.
Therefore
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, (ax1, ax2) = plt.subplots(1,2)
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are just animating one artist, the image, in
# each frame
ims = []
for i in range(60):
x += np.pi / 15.
y += np.pi / 20.
im1 = ax1.imshow(f(x, y), animated=True)
im2 = ax2.imshow(np.random.random(size=(100,120)))
ims.append([im1,im2])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)

Related

multi animation whit subplot

I got some sort of a problem with a pendulum animation, I tried to display my animation (the pendulum's movement) next to a graph in two separate axes, but when I try my code, it barely works displaying two axes that overlap on one another... Here is what I tried:
PS: best would be that the circles I was intended to add at the end of my pendulum appear on the final animation, but I really have no idea how to put them only on a particular ax
from numpy import sin, cos, pi, array
import numpy as np
import scipy.integrate
import matplotlib.pyplot as plt
import matplotlib.animation as animation
g = 10
y0 = np.array([np.pi / 2.0, 0]) # angle, vitesse
j = 0.2
def f(y, t):
return np.array([y[1], -g * np.sin(y[0])-j*y[1]])
t = np.linspace(0, 100, 10000)
y = scipy.integrate.odeint(f, y0, t)
theta, thetadot = y[:, 0], y[:, 1]
fig, axs = plt.subplots(1,2)
axs[0] = fig.add_subplot(xlim=(-1.5, 1.5), ylim=(-1.5, 1.5))
axs[0].grid()
axs[0].set_box_aspect(1)
# anchor = plt.Circle((0, 0), 0.01, color='black')
# mass = plt.Circle((sin(y0[0]),-cos(y0[0])), 0.2, color='black')
pendulums = axs[0].plot((0, sin(y0[0])), (0, -cos(y0[0])), 'o-', color = 'black')
# plt.gca().add_patch(weight) # adding circles
# plt.gca().add_patch(attach)
phase = axs[1].plot(theta,thetadot)
def animate(i):
angle = theta[i]
x = (0, sin(angle))
y = (0, -cos(angle))
#mass.center = (x[1],y[1])
pendulums[0].set_data(x, y)
anim = animation.FuncAnimation(fig, animate, interval=10)
plt.show()

how to add a xvline in every polar axis in matplot

I am trying to add a line to mark quantiles for every variable in a polar chart.
When I try axvline it only draws in the first axis
axes1 = plt.gca(projection='polar')
axes1.axvline(x=0, ymin=0.2,ymax=0.6, color='k',lw=3,alpha=0.5)
I want to add a different mark for every axis but I don't know how to iterate over the 5 axes of the example.
You can divide the circle (2 π) into 5 to obtain an x-coordinate for each of the 5 directions.
import matplotlib.pyplot as plt
import numpy as np
axes1 = plt.gca(projection='polar')
xs = np.linspace(0, 2 * np.pi, 5, endpoint=False) + np.pi / 2
for x in xs:
axes1.axvline(x=x, ymin=0.2, ymax=0.6, color='r', lw=3, alpha=0.5)
for ls in [':', '-']:
y = np.random.uniform(0.3, 1, 5)
plt.plot(np.concatenate([xs, xs[:1]]), np.concatenate([y, y[:1]]), color='g', ls=ls)
plt.fill_between(np.concatenate([xs, xs[:1]]), np.concatenate([y, y[:1]]), color='g', alpha=0.2)
plt.xticks(xs % (2 * np.pi))
plt.show()

how to avoid some function in the legend?

I need to include a line into a figure every time a button is clicked (I'm using pyqt4), this line has to be labeled and I also need to compare these lines with a constant function. Here is what I've tried:
labels = []
fig = plt.figure()
ax = fig.add_subplot(111, axisbg='white')
ax.hold(True)
def function(k):
x = np.linspace(0, 2, 100)
y = np.sin(k * np.pi * x) * np.exp(-5 * x)
labels.append('k = {}'.format(k))
ax.plot(x, y)
# reference line
plt.axhline(y=0.1, c='k', linestyle='--')
plt.legend(labels)
for i in range(0,5):
function(i)
plt.show()
The result:
There is a simple way to skip the constant line marker in the legend frame?
Maybe I'm not following but it doesn't look like your reference line axhline(y=0.1, ...) is included in the legend.
I would set this separately, no reason to redraw it every time you plot a new line. Also try passing the label inside the plot function
fig = plt.figure()
ax = fig.add_subplot(111, axisbg='white')
ax.hold(True)
# reference line - only draw this once
plt.axhline(y=0.1, c='k', linestyle='--')
def function(k):
x = np.linspace(0, 2, 100)
y = np.sin(k * np.pi * x) * np.exp(-5 * x)
ax.plot(x, y, linestyle='-', label='k = {}'.format(k)) # set label attribute for line
for i in range(0,5):
function(i)
plt.legend() # you only need to call this once, it will generate based on the label assigned to line objects
plt.show()
Note: If you want to do this interactively (i.e. draw on a button press) then you'll have to call plt.legend() upfront and call plt.draw() after each new line is added, that way it'll update the legend.
This is because there are actually 10 lines in your plot but your legend only shows 5 labels. If you check this by putting the label in the plot and axhline commands like this.
def function(k):
x = np.linspace(0, 2, 100)
y = np.sin(k * np.pi * x) * np.exp(-5 * x)
ax.plot(x, y, label='k = {}'.format(k))
# reference line
ax.axhline(y=0.1, c='k', linestyle='--', label='reference')
ax.legend()
print "number of lines in plot: {}".format(len(ax.lines))
Because you set the Axes.hold property to True, the Axes is not cleared, but a new line is added to Axes object every time you call these commands. This may be faster but you have to be careful to avoid adding duplicate artists. A simple solution would be to split the drawing in two functions: one to create an empty plot and one to add a line.
import matplotlib.pyplot as plt
import numpy as np
def init_plot(ax):
ax.hold(True)
ax.axhline(y=0.1, c='k', linestyle='--', label='reference')
ax.legend()
def add_line(ax, k):
x = np.linspace(0, 2, 100)
y = np.sin(k * np.pi * x) * np.exp(-5 * x)
ax.plot(x, y, label='k = {}'.format(k))
ax.legend()
def main():
fig = plt.figure()
ax = fig.add_subplot(111, axisbg='white')
init_plot(ax)
for i in range(0,5):
add_line(ax, i)
plt.show()
#raw_input('please press enter\n') # for OS-X
if __name__ == "__main__":
main()
I recommend to read the Artist tutorial and of course the Legend guide.

group boxplot histogramming

I would like to group my data and to plot the boxplot for all the groups. There are many questions and answer about that, my problem is that I want to group by a continuos variable, so I want to histogramming my data.
Here what I have done. My data:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.random.chisquare(5, size=100000)
y = np.random.normal(size=100000) / (0.05 * x + 0.1) + 2 * x
f, ax = plt.subplots()
ax.plot(x, y, '.', alpha=0.05)
plt.show()
I want to study the behaviour of y (location, width, ...) as a function of x. I am not interested in the distribution of x so I will normalized it.
f, ax = plt.subplots()
xbins = np.linspace(0, 25, 50)
ybins = np.linspace(-20, 50, 50)
H, xedges, yedges = np.histogram2d(y, x, bins=(ybins, xbins))
norm = np.sum(H, axis = 0)
H /= norm
ax.pcolor(xbins, ybins, np.nan_to_num(H), vmax=.4)
plt.show()
I can plot histogram, but I want boxplot
binning = np.concatenate(([0], np.sort(np.random.random(20) * 25), [25]))
idx = np.digitize(x, binning)
data_to_plot = [y[idx == i] for i in xrange(len(binning))]
f, ax = plt.subplots()
midpoints = 0.5 * (binning[1:] + binning[:-1])
widths = 0.9 * (binning[1:] - binning[:-1])
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
majorLocator = MultipleLocator(2)
ax.boxplot(data_to_plot, positions = midpoints, widths=widths)
ax.set_xlim(0, 25)
ax.xaxis.set_major_locator(majorLocator)
ax.set_xlabel('x')
ax.set_ylabel('median(y)')
plt.show()
Is there an automatic way to do that, like ax.magic(x, y, binning)? Is there a better way to do that? (Have a look to https://root.cern.ch/root/html/TProfile.html for example, which plot the mean and the error of the mean as error bars)
In addition, I want to minize the memory footprint (my real data are much more than 100000), I am worried about data_to_plot, is it a copy?

How to set set the marker size of a 3D scatter plot fixed to the axis?

I've asked a similar question before (How to set a fixed/static size of circle marker on a scatter plot?), but now I wanna do it in 3D. How can I do that?
thanks
As in the 2D case, you need to draw the spheres yourself. If you want nicely shaped spheres this means to draw many patches and thus gets slow quite quickly.
Here's a basic way of doing it:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
def plot_shere(ax, x, y, z, r, resolution=100, **kwargs):
""" simple function to plot a sphere to a 3d axes """
u = np.linspace(0, 2 * np.pi, resolution)
v = np.linspace(0, np.pi, resolution)
xx = r * np.outer(np.cos(u), np.sin(v)) + x
yy = r * np.outer(np.sin(u), np.sin(v)) + y
zz = r * np.outer(np.ones(np.size(u)), np.cos(v)) + z
ax.plot_surface(xx, yy, zz, rstride=4, cstride=4, **kwargs)
# create some random data (set seed to make it reproducable)
np.random.seed(0)
(x,y,z) = np.random.randint(0,10,(3,5))
r = np.random.randint(2,4,(5,))
# set up the figure
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# loop through the data and plot the spheres
for p in zip(x,y,z,r):
plot_shere(ax, *p, edgecolor='none', color=np.random.rand(3))
# set the axes limits and show the plot
ax.set_ylim([-4,14])
ax.set_xlim([-4,14])
ax.set_zlim([-4,14])
plt.show()
Result: