Python while loop replaces variable - python

if it runs, the figures should print out one by one. k = [3, 4, 5, 6, 2.1, 3.8, 5.5, 7.2]
[![enter image description here][1]][1]
but mine figures overwrites each other. so there has to be break or something?
[![enter image description here][2]][2]
from matplotlib import pyplot as plt
import numpy as np
def funcx(t, k, r):
x_1= r(k-1)np.cos(t)
x_2= r(np.cos((k-1)t))
x = x_1 + x_2
return x
def funcy(t, k, r):
y_1= r(k-1)np.sin(t)
y_2= rnp.sin((k-1)t)
y = y_1 - y_2
return y
def plot_function(k, r, string_plot):
plt.figure()
plt.plot(x_list, y_list, string_plot)
ax = plt.subplot()
ax.axis('off')
plt.show()
k = [3, 4, 5, 6, 2.1, 3.8, 5.5, 7.2]
r = 5
x_list =[]
y_list=[]
for i in range(0, len(k)):
for t in np.arange(0, 20*np.pi, 0.001):
x_list.append(funcx(t, k[i], r))
y_list.append(funcy(t, k[i], r))
plot_function(x_list, y_list, "b")
[![enter image description here][3]][3]
[1]: https://i.stack.imgur.com/qF3wR.png
[2]: https://i.stack.imgur.com/t5rS3.png
[3]: https://i.stack.imgur.com/lmyLL.png
this is how it should be: https://en.wikipedia.org/wiki/Hypocycloid

Do this changes to your code. Not sure if it is what you need.
import numpy as np
def funcx(t, k, r):
x_1= r*(k-1)*np.cos(t)
x_2= r*(np.cos((k-1)*t))
x = x_1 + x_2
return x
def funcy(t, k, r):
y_1= r*(k-1)*np.sin(t)
y_2= r*np.sin((k-1)*t)
y = y_1 - y_2
return y
def plot_function(xlist, ylist,k,ax):
ax[i].plot(x_list, y_list)
ax[i].set_title(f'k = {k}')
ax[i].axis('off')
#plt.plot(x_list, y_list, string_plot)
#ax = plt.subplot()
#plt.axis('off')
k = [3, 4, 5, 6, 2.1, 3.8, 5.5, 7.2]
r = 5
x_list =[]
y_list=[]
#Initialize the subplots before the for loop to avoid the overwriting.
fig, ax = plt.subplots(1,len(k),**{'figsize':(20,4)})
for i in range(0, len(k)):
for t in np.arange(0, 20*np.pi, 0.001):
x_list.append(funcx(t, k[i], r))
y_list.append(funcy(t, k[i], r))
plot_function(x_list, y_list,k[i],ax)

Related

plot data in connected horizantla lines on python

I have these array data x and i want to plot horizantal line for each value in it in a specific sub-interval of the range y
please can any one help me ?
x=[2,4,3,5]
;y=np.linspace(0,20,20)
Can be done with a bit of logic and preparing the x and y coordinates-
import matplotlib.pyplot as plt
y = [2, 4, 3, 5]
ys = []
xs = []
step_length = [5, 4, 2, 3]
for i, ycoord in enumerate(y):
ys.extend([ycoord]*(step_length[i]))
xs.extend(list(range(len(xs) - i, len(xs) + step_length[i] - i)))
plt.plot(xs, ys)
plt.show()

creating a multivariate skew normal distribution python

How can I create a multivariate skew normal function, where then by inputting x and y points we can create a surface diagram in 3d (x,y and z coordinates)
I wrote a blog post about this, but here is complete working code:
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import (multivariate_normal as mvn,
norm)
class multivariate_skewnorm:
def __init__(self, a, cov=None):
self.dim = len(a)
self.a = np.asarray(a)
self.mean = np.zeros(self.dim)
self.cov = np.eye(self.dim) if cov is None else np.asarray(cov)
def pdf(self, x):
return np.exp(self.logpdf(x))
def logpdf(self, x):
x = mvn._process_quantiles(x, self.dim)
pdf = mvn(self.mean, self.cov).logpdf(x)
cdf = norm(0, 1).logcdf(np.dot(x, self.a))
return np.log(2) + pdf + cdf
xx = np.linspace(-2, 2, 100)
yy = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(xx, yy)
pos = np.dstack((X, Y))
fig = plt.figure(figsize=(10, 10), dpi=150)
axes = [
fig.add_subplot(1, 3, 1, projection='3d'),
fig.add_subplot(1, 3, 2, projection='3d'),
fig.add_subplot(1, 3, 3, projection='3d')
]
for a, ax in zip([[0, 0], [5, 1], [1, 5]], axes):
Z = multivariate_skewnorm(a=a).pdf(pos)
ax.plot_surface(X, Y, Z, cmap=cm.viridis)
ax.set_title(r'$\alpha$ = %s, cov = $\mathbf{I}$' % str(a), fontsize=18)
That code will generate this figure:
You can add direction to multivariate normal distribution by adding a sigma covariance matrix:
import numpy as np
from scipy.stats import multivariate_normal
mu = [20,20] # center of distribution.
sigma_size_top, sigma_size_bot = np.random.uniform(5, 20, size=2)
cov_max = np.sqrt(sigma_size_top * sigma_size_bot) * 0.9 # Cov max can't be larger than sqrt of the other elements
sigma_cov = np.random.uniform(-cov_max, cov_max)
sigma = np.array([[sigma_size_top, sigma_cov],[sigma_cov, sigma_size_bot]])
And then pass it in to your multivariate_normal:
dist = multivariate_normal(mu, sigma)
Put this into a 2D mapping by:
x = np.linspace(0, 40, 41)
y = x.copy()
xx, yy = np.meshgrid(x, y)
pos = np.empty(xx.shape + (2,))
pos[:, :, 0] = xx
pos[:, :, 1] = yy
my_map = dist.pdf(pos)
You'll then have a skewed multivariate normal distribution on a matrix. I suggest you scale this matrix as the values will be small.

Show z-value at mouse pointer position in status line with matplotlib's pcolormesh()

When using imshow() the z-value of the mouse pointer is shown in the status line as shown in the screen shot (on the right):
How do I achieve the same behavior with pcolormesh()?
The image was generated by the following code:
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(-1, 1, 101)
X, Y = np.meshgrid(t, 2*t)
Z = np.sin(2*np.pi*(X**2+Y**2))
fig, axx = plt.subplots(1, 2)
axx[0].set_title("imshow()")
axx[0].imshow(Z, origin='lower', aspect='auto', extent=[-1, 1, -2, 2])
axx[1].set_title("pcolormesh()")
axx[1].pcolormesh(X, Y, Z)
fig.tight_layout()
plt.show()
One idea is to monkey patch the ax.format_coord function to include the desired value. This is also shown in a matplotlib example.
Specific solution
Now if you want both plots share the same function, a little bit of work needs to be spent on getting the axes limits correct.
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(-1, 1, 101)
X, Y = np.meshgrid(t, 2*t)
Z = np.sin(np.pi*(X**2+Y**2))
fig, axx = plt.subplots(1, 2)
axx[0].set_title("imshow()")
extent = [-1-(t[1]-t[0])/2., 1+(t[1]-t[0])/2., -2-(t[1]-t[0]), 2+(t[1]-t[0])]
axx[0].imshow(Z, origin='lower', aspect='auto', extent=extent)
axx[1].set_title("pcolormesh()")
axx[1].pcolormesh(X-(t[1]-t[0])/2., Y-(t[1]-t[0]), Z)
axx[1].set_xlim(-1-(t[1]-t[0])/2., 1+(t[1]-t[0])/2.)
axx[1].set_ylim( -2-(t[1]-t[0]), 2+(t[1]-t[0]) )
def format_coord(x, y):
x0, x1 = axx[1].get_xlim()
y0, y1 = axx[1].get_ylim()
col = int(np.floor((x-x0)/float(x1-x0)*X.shape[1]))
row = int(np.floor((y-y0)/float(y1-y0)*Y.shape[0]))
if col >= 0 and col < Z.shape[1] and row >= 0 and row < Z.shape[0]:
z = Z[row, col]
return 'x=%1.4f, y=%1.4f, z=%1.4f' % (x, y, z)
else:
return 'x=%1.4f, y=%1.4f' % (x, y)
axx[1].format_coord = format_coord
fig.tight_layout()
plt.show()
General solution
The above is specific to the data in the question and has the drawback of not allowing to zoom and pan in the plot. A completely general solution would need to take into account the possibility that the image does not fill the complete axes and also the fact that pcolormesh pixels may be of unequal sizes.
This could look as follows, where in addition, the pixel numbers are shown:
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
x = [-3, -2, 0, 1.5, 2.2, 3.2, 3.9, 5, 6.75, 9]
y = [7, 7.1, 7.5, 7.7, 8, 8.2, 8.4, 8.8, 9]
X,Y = np.meshgrid(x,y)
Z = np.random.randint(0, 100, size=np.array(X.shape)-1)
fig, ax = plt.subplots()
pc = ax.pcolormesh(X,Y,Z)
fig.colorbar(pc)
def format_coord(x, y):
xarr = X[0,:]
yarr = Y[:,0]
if ((x > xarr.min()) & (x <= xarr.max()) &
(y > yarr.min()) & (y <= yarr.max())):
col = np.searchsorted(xarr, x)-1
row = np.searchsorted(yarr, y)-1
z = Z[row, col]
return f'x={x:1.4f}, y={y:1.4f}, z={z:1.4f} [{row},{col}]'
else:
return f'x={x:1.4f}, y={y:1.4f}'
ax.format_coord = format_coord
plt.show()

Wireframe plots

The program calculates the reflection coefficient of the multilayer system, depending on the thickness of 50 layers (d1, d2). If I take any two numbers (d1,d2), it works correct. But I need to get Wireframe plots, where d1, d2 takes meaning in some range, I get an error: "ValueError: input must be a square array" in line 13. How can I fix it?
from math import pi
import numpy as np
import matplotlib.pyplot as plt
def R(n1, n2, d1, d2, lamda):
phy1 = (-2*pi*n1*d1/lamda)
phy2 = (-2*pi*n2*d2/lamda)
DPD1 = 0.5*np.array([[2*np.cos(phy1), 2j*np.sin(phy1)/n1], [n1*2j*np.sin(phy1), 2*np.cos(phy1) ]])
DPD2 = 0.5*np.array([[2*np.cos(phy2), 2j*np.sin(phy2)/n2], [n2*2j*np.sin(phy2), 2*np. cos(phy2) ]])
D0 = 0.5 * np.array([[1, 1], [1, -1]])
DS = np.array([[1, 1], [n1, -n1]])
DPD = np.dot(DPD1, DPD2)
DPD = np.linalg.matrix_power(DPD, 50)
M = np.dot(D0, DPD)
M = np.dot(M, DS)
return(abs(M[1,0]/M[0,0])**2)
x = np.arange(0, 10, 1)
y = np.arange(0, 10, 1)
X, Y = np.meshgrid(x, y)
Z = R(0.99910053+0.00183184j, 0.92373900+0.00644652j, X, Y, 13.5)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_wireframe(X, Y, Z, antialiased=True)
I don't know how to do it without for loop. Here is my solution:
from math import pi
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def R(n1, n2, d1, d2, lamda):
phy1 = (-2*pi*n1*d1/lamda)
phy2 = (-2*pi*n2*d2/lamda)
DPD1 = 0.5*np.array([[2*np.cos(phy1), 2j*np.sin(phy1)/n1], [n1*2j*np.sin(phy1), 2*np.cos(phy1) ]])
DPD2 = 0.5*np.array([[2*np.cos(phy2), 2j*np.sin(phy2)/n2], [n2*2j*np.sin(phy2), 2*np. cos(phy2) ]])
DPD = np.dot(DPD1, DPD2)
DPD = np.linalg.matrix_power(DPD, 50)
D0 = 0.5 * np.array([[1, 1], [1, -1]])
DS = np.array([[1, 1], [n1, -n1]])
M = np.dot(D0, DPD)
M = np.dot(M, DS)
return(abs(M[1,0]/M[0,0])**2)
x = np.arange(0, 10, 1)
y = np.arange(0, 10, 1)
X, Y = np.meshgrid(x, y)
Z = np.zeros(X.shape).ravel()
for i, (x, y) in enumerate(zip(X.ravel(), Y.ravel())):
Z[i] = R(0.99910053+0.00183184j, 0.92373900+0.00644652j, x, y, 13.5)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_wireframe(X, Y, Z.reshape(X.shape), antialiased=True)
plt.show()

modelling crowd movement with matplotlib

I would like to model basic crowd movement with python. I want to show an animation. I have made the following program to test it with matplotlib :
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
#size of the crowd
N = 100
def gen_data():
""" init position and speed of each people """
x = y = np.zeros(N)
theta = np.random.random(N) * 360 / (2 * np.pi)
v0 = 0.1
vx, vy = v0 * np.cos(theta), v0 * np.sin(theta)
return np.array([x, y, vx, vy]).T
def init():
for line in lines:
line.set_data([],[])
return line,
def update_lines(i, lines, data):
for d, line in zip(data, lines):
d[0:2] += d[2:4]
if abs(d[0]) > 5: d[2] *= -1
if abs(d[1]) > 5: d[3] *= -1
line.set_data(d[0] ,d[1])
return lines
fig = plt.figure()
ax = plt.axes(xlim=(-5,5),ylim=(-5,5))
lines = [plt.plot([],[], 'ko')[0] for i in range(N)]
data = gen_data()
anim = animation.FuncAnimation(fig, update_lines, init_func=init, fargs=(lines, data), interval=10, blit=True)
plt.show()
Even for N=100, the animation is slow... Is there something I can do to speed it up with mathplotlib ? Is matplotlib the best graphic tool to make thins kind of animation with python ? If no, what would it be ?
Here are 3 things you can do to make the animation faster:
Replace the N calls to plt.plot with one call to plt.scatter.
Replace the for-loop in update with assignments which modify whole slices of data at once:
data[:, 0:2] += data[:, 2:4]
data[:, 2] = np.where(np.abs(data[:, 0]) > 5, -data[:, 2], data[:, 2])
data[:, 3] = np.where(np.abs(data[:, 1]) > 5, -data[:, 3], data[:, 3])
Reduce interval=10 to interval=0.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
# size of the crowd
N = 100
def gen_data():
""" init position and speed of each people """
x = y = np.zeros(N)
theta = np.random.random(N) * 360 / (2 * np.pi)
v0 = 0.1
vx, vy = v0 * np.cos(theta), v0 * np.sin(theta)
return np.column_stack([x, y, vx, vy])
def init():
pathcol.set_offsets([[], []])
return pathcol,
def update(i, pathcol, data):
data[:, 0:2] += data[:, 2:4]
data[:, 2] = np.where(np.abs(data[:, 0]) > 5, -data[:, 2], data[:, 2])
data[:, 3] = np.where(np.abs(data[:, 1]) > 5, -data[:, 3], data[:, 3])
pathcol.set_offsets(data[:, 0:2])
return [pathcol]
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-5, 5))
pathcol = plt.scatter([], [])
data = gen_data()
anim = animation.FuncAnimation(fig, update, init_func=init,
fargs=(pathcol, data), interval=0, blit=True)
plt.show()

Categories