I am trying to make an animated plot of planetary motion using python. I can get the correct path to show up when not animated but once I try to animate it, it just shows up blank even though r and i are being output as the correct values when I try to print each value in the console.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure(figsize=(6,6))
ax = plt.subplot(111, polar=True)
ax.set_ylim(0,1)
line, = ax.plot([],[])
semi_major_axis = 1
eccentricity = 0.1
theta = np.linspace(0,2*np.pi, num = 50)
point, = ax.plot(0,1, marker="o")
def frame(i):
r=(semi_major_axis*(1-eccentricity**2)/(1-eccentricity*np.cos(i)))
line.set_xdata(i)
line.set_ydata(r)
return line,
ax.set_rmax(semi_major_axis+1)
ax.set_rticks(np.linspace(0,semi_major_axis+1, num = 5))
ax.set_rlabel_position(-22.5)
animation = FuncAnimation(fig, func=frame, frames=theta, interval=10)
plt.show()
I think the problem is you are plotting a point and not a line. The following code worked for me:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure(figsize=(6, 6))
ax = plt.subplot(111, polar=True)
ax.set_ylim(0, 1)
semi_major_axis = 1
eccentricity = 0.1
theta = np.linspace(0, 2 * np.pi, num=50)
def frame(i):
r = (semi_major_axis * (1 - eccentricity ** 2) / (1 - eccentricity * np.cos(i)))
ax.plot(i,r,'b', marker='o')
ax.set_rmax(semi_major_axis + 1)
ax.set_rticks(np.linspace(0, semi_major_axis + 1, num=5))
ax.set_rlabel_position(-22.5)
animation = FuncAnimation(fig, func=frame, frames=theta, interval=10)
plt.show()
Related
Matplotlib can convert a plot/figure into a RGB array as follows:
import matplotlib.pyplot as plt
import numpy as np
import io
fig, ax = plt.subplots()
n=256
I, J = np.indices((n, n))
im = ax.imshow((I | J) % 19, interpolation='none')
fig.colorbar(im, ax=ax)
#Convert fig to a RGB array
io_buf = io.BytesIO()
fig.savefig(io_buf, format='raw')
io_buf.seek(0)
fig_arr = np.reshape(np.frombuffer(io_buf.getvalue(), dtype=np.uint8),
newshape=(int(fig.bbox.bounds[3]), int(fig.bbox.bounds[2]), -1))
print(f"The shape of the rgb array: {fig_arr.shape}")
plt.show()
It displays:
The shape of the rgb array: (480, 640, 4)
Is it possible to convert similarly a Plots plot into a Matrix{RGB{N0f8}}?
The first part:
using Plots
n = 255
I = [i for i in 0:n, j in 0:n]
h = heatmap(mod.((I .| I'), 19), c= :deep, yflip=true, size=(400, 400), aspect_ratio=:equal)
I searched for Julia equivalent of numpy.frombuffer, but no result has been returned
With h holding the plot, as the code in the OP has described. The following:
using FileIO
io = IOBuffer()
show(io, MIME("image/png"), h);
strm = Stream(format"PNG", io)
img = load(strm)
leaves img with the Matrix{RGB{...}}.
I have a Seaborn displot with a hued variable:
For each hued variable, I want to extract the mode of the density estimate and then plot each hue variable versus its mode, like so:
How do I do this?
You can use scipy.stats.gaussian_kde to create the density estimation function. And then call that function on an array of x-values to calculate its maximum.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.random.normal(0.001, 1, 1300).cumsum() + 30,
'hue': np.repeat(np.arange(0.08, 0.20001, 0.01), 100).round(2)})
g = sns.displot(df, x='x', hue='hue', palette='turbo', kind='kde', fill=True, height=6, aspect=1.5)
plt.show()
from scipy.stats import gaussian_kde
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots(figsize=(10, 6))
hues = df['hue'].unique()
num_hues = len(hues)
colors = sns.color_palette('turbo', num_hues)
xmin, xmax = df['x'].min(), df['x'].max()
xs = np.linspace(xmin, xmax, 500)
for hue, color in zip(hues, colors):
data = df[df['hue'] == hue]['x'].values
kde = gaussian_kde(data)
mode_index = np.argmax(kde(xs))
mode_x = xs[mode_index]
sns.scatterplot(x=[hue], y=[mode_x], color=color, s=50, ax=ax)
cmap = sns.color_palette('turbo', as_cmap=True)
norm = plt.Normalize(hues.min(), hues.max())
plt.colorbar(ScalarMappable(cmap=cmap, norm=norm), ax=ax, ticks=hues)
plt.show()
Here is another approach, extracting the kde curves. It uses the legend of the kde plot to get the correspondence between the curves and the hue values. sns.kdeplot is the axes-level function used by sns.displot(kind='kde'). fill=False creates lines instead of filled polygons for the curves, for which the values are easier to extract. (ax1.fill_between can fill the curves during a second pass). The x and y axes of the second plot are switched to align the x-axes of both plots.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.random.normal(0.007, 0.1, 1300).cumsum() + 30,
'hue': np.repeat(np.arange(0.08, 0.20001, 0.01), 100).round(2)})
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(12, 10), sharex=True)
sns.kdeplot(data=df, x='x', hue='hue', palette='turbo', fill=False, ax=ax1)
hues = [float(txt.get_text()) for txt in ax1.legend_.get_texts()]
ax2.set_yticks(hues)
ax2.set_ylabel('hue')
for hue, line in zip(hues, ax1.lines[::-1]):
color = line.get_color()
x = line.get_xdata()
y = line.get_ydata()
ax1.fill_between(x, y, color=color, alpha=0.3)
mode_ind = np.argmax(y)
mode_x = x[mode_ind]
sns.scatterplot(x=[mode_x], y=hue, color=color, s=50, ax=ax2)
sns.despine()
plt.tight_layout()
plt.show()
I'm trying to create a plot in Python where the data that is being plotted gets updated as my simulation progresses. In MATLAB, I could do this with the following code:
t = linspace(0, 1, 100);
figure
for i = 1:100
x = cos(2*pi*i*t);
plot(x)
drawnow
end
I'm trying to use matplotlib's FuncAnimation function in the animation module to do this inside a class. It calls a function plot_voltage which recalculates voltage after each timestep in my simulation. I have it set up as follows:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def __init__(self):
ani = animation.FuncAnimation(plt.figure(2), self.plot_voltage)
plt.draw()
def plot_voltage(self, *args):
voltages = np.zeros(100)
voltages[:] = np.nan
# some code to calculate voltage
ax1 = plt.figure(2).gca()
ax1.clear()
ax1.plot(np.arange(0, len(voltages), 1), voltages, 'ko-')`
When my simulation runs, the figures show up but just freeze. The code runs without error, however. Could someone please let me know what I am missing?
Here is a translation of the matlab code into matplotlib using FuncAnimation:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
t = np.linspace(0, 1, 100)
fig = plt.figure()
line, = plt.plot([],[])
def update(i):
x = np.cos(2*np.pi*i*t)
line.set_data(t,x)
ani = animation.FuncAnimation(fig, update,
frames=np.linspace(1,100,100), interval=100)
plt.xlim(0,1)
plt.ylim(-1,1)
plt.show()
In the following code I have two separate animations and I have plotted them in a two separate subplots. I want both of them to run in a single plot instead of this. I have tried the approach explained below but it is giving me issues as explained below. Please help
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time as t
x = np.linspace(0,5,100)
fig = plt.figure()
p1 = fig.add_subplot(2,1,1)
p2 = fig.add_subplot(2,1,2)
def gen1():
i = 0.5
while(True):
yield i
i += 0.1
def gen2():
j = 0
while(True):
yield j
j += 1
def run1(c):
p1.clear()
p1.set_xlim([0,15])
p1.set_ylim([0,100])
y = c*x
p1.plot(x,y,'b')
def run2(c):
p2.clear()
p2.set_xlim([0,15])
p2.set_ylim([0,100])
y = c*x
p2.plot(x,y,'r')
ani1 = animation.FuncAnimation(fig,run1,gen1,interval=1)
ani2 = animation.FuncAnimation(fig,run2,gen2,interval=1)
fig.show()
I tried creating a single subplot instead of p1 and p2 and have both the plots graphed in that single subplot. That is just plotting one graph and not both of them. As far as I can say it is because one of them is getting cleared right after it is plotted.
How do I get around this problem?
As you do not show the code that is actually producing the problem, it's hard to tell where the problem lies.
But to answer the question of how to animate two lines in the same axes (subplot), we can just get rid of the clear() command and update the lines, instead of producing a new plot for every frame (which is more efficient anyways).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,15,100)
fig = plt.figure()
p1 = fig.add_subplot(111)
p1.set_xlim([0,15])
p1.set_ylim([0,100])
# set up empty lines to be updates later on
l1, = p1.plot([],[],'b')
l2, = p1.plot([],[],'r')
def gen1():
i = 0.5
while(True):
yield i
i += 0.1
def gen2():
j = 0
while(True):
yield j
j += 1
def run1(c):
y = c*x
l1.set_data(x,y)
def run2(c):
y = c*x
l2.set_data(x,y)
ani1 = animation.FuncAnimation(fig,run1,gen1,interval=1)
ani2 = animation.FuncAnimation(fig,run2,gen2,interval=1)
plt.show()
I wish to appear a figure (and certain text) as if they are printed on a page of an open book. Is it possible to transform an jpg image programmatically or in matplotlib to have such an effect?
You can use a background axis along with an open source book image to do something like this,
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax2 = fig.add_axes([0.2, 0.3, 0.25, 0.3])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax1.imshow(im, origin='lower')
# Plot a graph and set background to transparent
x = np.linspace(0,4.*np.pi,40)
y = np.sin(x)
ax2.plot(x,y,'-ro',alpha=0.5)
ax2.set_ylim([-1.1,1.1])
ax2.patch.set_alpha(0.0)
from matplotlib import rc
rc('text', usetex=True)
margin = im.shape[0]*0.075
ytext = im.shape[1]/2.+10
ax1.text(margin, ytext, "The following text is an example")
ax1.text(margin, 90, "Figure 1. Showing a sine function")
plt.show()
Which looks like this,
where I used the following book image.
UPDATE: Added non-affine transformation based on scikit-image warp example, but with Maxwell distribution. The solution saves the matplotlib line as an image in order to apply a pointwise transform. Mapping for vector graphics may be possible but I think this will be more complicated...
import numpy as np
import matplotlib.pyplot as plt
def maxwellian_transform_image(image):
from skimage.transform import PiecewiseAffineTransform, warp
rows, cols = image.shape[0], image.shape[1]
src_cols = np.linspace(0, cols, 20)
src_rows = np.linspace(0, rows, 10)
src_rows, src_cols = np.meshgrid(src_rows, src_cols)
src = np.dstack([src_cols.flat, src_rows.flat])[0]
# add maxwellian to row coordinates
x = np.linspace(0, 3., src.shape[0])
dst_rows = src[:, 1] + (np.sqrt(2/np.pi)*x**2 * np.exp(-x**2/2)) * 50
dst_cols = src[:, 0]
dst_rows *= 1.5
dst_rows -= 1.0 * 50
dst = np.vstack([dst_cols, dst_rows]).T
tform = PiecewiseAffineTransform()
tform.estimate(src, dst)
out_rows = image.shape[0] - 1.5 * 50
out_cols = cols
out = warp(image, tform, output_shape=(out_rows, out_cols))
return out
#Create the new figure
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax.imshow(im, origin='lower')
# Plot and save graph as image, will need some manipulation of location
temp, at = plt.subplots()
margin = im.shape[0]*0.1
x = np.linspace(margin,im.shape[0]/2.,40)
y = im.shape[1]/3. + 0.1*im.shape[1]*np.sin(12.*np.pi*x/im.shape[0])
at.plot(x,y,'-ro',alpha=0.5)
temp.savefig("lineplot.png",transparent=True)
#Read in plot as an image and apply transform
plot = plt.imread("./lineplot.png")
out = maxwellian_transform_image(plot)
ax.imshow(out, extent=[0,im.shape[1],0,im.shape[0]])
plt.show()
The figure now looks like,