Why does matplotlib arrow not align in the right direction? - matplotlib

My arrows aren't pointing in the right direciton. I don't understand why the normal or the tangent is wrong. Here is the code. This arrow is supposed to be parallel with the line.
S1 = np.array([[-0.4,0.4],
[-0.6,0.5]])
y1 = 0.5
y2 = 0.4
x1 = -0.6
x2 = -0.4
n1 = -(y2-y1)
n2 = (x2-x1)
x, y = S1.T
plt.plot(x,y)
plt.quiver(-0.5, 0.45, (x2-x1), (y2-y1))
plt.show()

You are not getting the expected result because x- and y-data coordinates are differentially scaled while getting displayed. Please see matplotlib.axes.Axes.set_aspect for more details. Using plt.gca().set_aspect("equal") will align the arrow properly.
Example:
The right subplot has equal aspect ratio i.e. the difference of 0.1 on Y-axis has the same display length in the subplot as that of X-axis.
Code to reproduce the figure:
import numpy as np
import matplotlib.pyplot as plt
S1 = np.array([[-0.4,0.4],
[-0.6,0.5]])
y1 = 0.5
y2 = 0.4
x1 = -0.6
x2 = -0.4
n1 = (y2-y1)
n2 = (x2-x1)
x, y = S1.T
fig, ax = plt.subplots(nrows=1, ncols=2)
ax[0].plot(x, y)
ax[0].quiver(-0.5, 0.45, (x2-x1), (y2-y1))
ax[1].plot(x, y)
ax[1].quiver(-0.5, 0.45, (x2-x1), (y2-y1))
ax[1].set_aspect("equal")
plt.show()

Related

Clip data to axis in subplots with matplotlip

I am trying to clip data to within the axes bounds when using subplots to create multiple plots.
By setting clip_on = True the data is clipped to the figure but still is shown in the neighboring plot above, but I don't want this to happen. The code below reproducers the issue where the blue line appears in the first plot overtop of the red line.
import matplotlib as mpl
import numpy as np
fig, ax = mpl.pyplot.subplots(2,1)
x = np.linspace(-10, 10, 1000)
y1 = x**2 + 2*x + 2
y2 = x**2 + 2*x + 3
ax[0].set_ylim(0, 5)
ax[0].plot(x, y1, color = 'red', clip_on = True)
ax[1].set_ylim(0, 5)
ax[1].plot(x, y2, clip_on = True)

Matplotlib Animation : How to make a reverse animation?

I'm currently working on matplotlib animation. Currently in the plot, I have 2 circles, green and orange one. What I want to make is shrinking the green circle until it came to zero (base of the cartesian diagram). What I got was a new blue circle coming out from the (0,0) coordinate. What I have to do if I want the green circle to shrink to (0,0) since the center of the green one is (0,0) also.
Thank you for the advice
Note : I'm using the 3.9.2 python with the latest update for all the packages
My current animation:
Current code :
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots(1)
line, = ax.plot([], [], lw=2)
ax.set_xlim(-5,5)
ax.set_ylim(-5,5)
# Move left y-axis and bottim x-axis to centre, passing through (0,0)
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
# Eliminate upper and right axes
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
# Show ticks in the left and lower axes only
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# theta goes from 0 to 2pi
theta = np.linspace(0, 2*np.pi, 100)
# the radius of the circle
r = np.sqrt(1)
r2 = np.sqrt(4)
# compute x1 and x2
x1 = 1+r*np.cos(theta)
y1 = r*np.sin(theta)
x2 = r2*np.cos(theta)
y2 = r2*np.sin(theta)
def init():
line.set_data([], [])
return line,
def animate(i):
x2 = np.sqrt(i)*np.cos(theta)
y2 = np.sqrt(i)*np.sin(theta)
line.set_data(x2, y2)
return line,
# create the figure
ax.plot(x1,y1)
ax.plot(x2,y2)
ax.set_aspect(1)
plt.grid()
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=10)
plt.show()
f = r"D:/UNPAR/Semester 2/Pemrograman Komputer/Project/animation.gif"
writergif = animation.PillowWriter(fps=30)
anim.save(f, writer=writergif)

Refreshing plot in matplotlib

As part of displaying the progression of a linear regression model fit, I need to be able to update/refresh an xy plot. Below is a simple script for 3 sets of y data, which need to be shown sequentially. However, they are piled up on top of each other. When fig.canvas.flush_events() is substituted with fig.clear() or fig.clf() the result is a blank plot. What am I - as a newbie -missing?
import torch as tc
import matplotlib.pyplot as plt
tc.manual_seed(1)
X=tc.linspace(-3,3,30)
y0=X.pow(2)+0.5*tc.randn(X.shape[0])
y1=y0/1.3
y2=y0/1.6
y=[y0,y1,y2]
fig=plt.figure()
ax=fig.add_subplot()
ax.set_xlim(-3.3,3.3)
ax.set_ylim(-0.5,9.5)
for i in range(3):
y_new=y[i]
ax.plot(X,y_new,'db')
fig.canvas.draw()
fig.canvas.flush_events()
plt.pause(1)
fig.show()
In your loop, you are creating a new line every time you call ax.plot. The better way is to create a Line2D artist and to update the coordinates of the point in the loop:
(NB i've converted your example to using numpy instead of torch)
import matplotlib.pyplot as plt
import numpy as np
X = np.linspace(-3, 3, 30)
y0 = np.power(X, 2) + 0.5 * np.random.randn(X.shape[0])
y1 = y0 / 1.3
y2 = y0 / 1.6
y = [y0, y1, y2]
fig = plt.figure()
ax = fig.add_subplot()
l, = ax.plot(X, y0, 'db')
ax.set_xlim(-3.3, 3.3)
ax.set_ylim(-0.5, 9.5)
for i in range(3):
y_new = y[i]
l.set_ydata(y_new)
fig.canvas.draw()
plt.pause(1)
plt.show()
For this kind of things, you'd be better off using the FuncAnimation module provided by maptlotlib though:
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
X = np.linspace(-3, 3, 30)
y0 = np.power(X, 2) + 0.5 * np.random.randn(X.shape[0])
y1 = y0 / 1.3
y2 = y0 / 1.6
y = [y0, y1, y2]
fig = plt.figure()
ax = fig.add_subplot()
l, = ax.plot(X, y0, 'db')
ax.set_xlim(-3.3, 3.3)
ax.set_ylim(-0.5, 9.5)
def animate(y_new):
l.set_ydata(y_new)
return l,
ani = animation.FuncAnimation(fig, func=animate, frames=y, interval=1000)
fig.show()

Plotting multiple set of data in pcolor plot python

I have data sets like (x,y,(z1,z2,z3..)). I am trying
plt.pcolor(x,y,z1)
plt.pcolor(x,y,z2)
plt.pcolor(x,y,z3)
plt.colorbar()
plt.show()
This is showing only the pcolor plot of the last data set. How can I plot all in same plot and same colorbar scale?
You could try with subplots, and make sure all the images with the same intensity scale (use the same vmin and vmax arguments of pcolor() for all your images). Below is an example:
import numpy as np
import matplotlib.pyplot as plt
dx, dy = 0.15, 0.05
y, x = np.mgrid[slice(-3, 3 + dy, dy),
slice(-3, 3 + dx, dx)]
z = (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)
z1 = z[:-1, :-1]
z2 = z[:-1, :-1]
z3 = z[:-1, :-1]
z_min, z_max = -np.abs(z).max(), np.abs(z).max()
data = [[x,y,z1],[x,y,z2],[x,y,z3]]
# Plot each slice as an independent subplot
fig, axes = plt.subplots(nrows=1, ncols=3)
for dat, ax in zip(data, axes.flat):
# The vmin and vmax arguments specify the color limits
pc = ax.pcolor(dat[0],dat[1],dat[2], vmin=z_min, vmax=z_max)
# Make an axis for the colorbar on the right side
cax = fig.add_axes([0.9, 0.1, 0.03, 0.8])
fig.colorbar(pc, cax=cax)
plt.show()
It will show like this:

how to plot gradient fill on the 3d bars in matplotlib

Right now there're some statistics plotted in 3d bar over (x, y). each bar height represents the density of the points in side the square grid of (x,y) plane. Right now, i can put different color on each bar. However, I want to put progressive color on the 3d bar, similar as the cmap, so the bar will be gradient filled depending on the density.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# height of the bars
z = np.ones((4, 4)) * np.arange(4)
# position of the bars
xpos, ypos = np.meshgrid(np.arange(4), np.arange(4))
xpos = xpos.flatten('F')
ypos = ypos.flatten('F')
zpos = np.zeros_like(xpos)
dx = 0.5 * np.ones_like(zpos)
dy = dx.copy()
dz = z.flatten()
ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', zsort='average')
plt.show()
Output the above code:
Let me first say that matplotlib may not be the tool of choice when it comes to sophisticated 3D plots.
That said, there is no built-in method to produce bar plots with differing colors over the extend of the bar.
We therefore need to mimic the bar somehow. A possible solution can be found below. Here, we use a plot_surface plot to create a bar that contains a gradient.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection= Axes3D.name)
def make_bar(ax, x0=0, y0=0, width = 0.5, height=1 , cmap="viridis",
norm=matplotlib.colors.Normalize(vmin=0, vmax=1), **kwargs ):
# Make data
u = np.linspace(0, 2*np.pi, 4+1)+np.pi/4.
v_ = np.linspace(np.pi/4., 3./4*np.pi, 100)
v = np.linspace(0, np.pi, len(v_)+2 )
v[0] = 0 ; v[-1] = np.pi; v[1:-1] = v_
x = np.outer(np.cos(u), np.sin(v))
y = np.outer(np.sin(u), np.sin(v))
z = np.outer(np.ones(np.size(u)), np.cos(v))
xthr = np.sin(np.pi/4.)**2 ; zthr = np.sin(np.pi/4.)
x[x > xthr] = xthr; x[x < -xthr] = -xthr
y[y > xthr] = xthr; y[y < -xthr] = -xthr
z[z > zthr] = zthr ; z[z < -zthr] = -zthr
x *= 1./xthr*width; y *= 1./xthr*width
z += zthr
z *= height/(2.*zthr)
#translate
x += x0; y += y0
#plot
ax.plot_surface(x, y, z, cmap=cmap, norm=norm, **kwargs)
def make_bars(ax, x, y, height, width=1):
widths = np.array(width)*np.ones_like(x)
x = np.array(x).flatten()
y = np.array(y).flatten()
h = np.array(height).flatten()
w = np.array(widths).flatten()
norm = matplotlib.colors.Normalize(vmin=0, vmax=h.max())
for i in range(len(x.flatten())):
make_bar(ax, x0=x[i], y0=y[i], width = w[i] , height=h[i], norm=norm)
X, Y = np.meshgrid([1,2,3], [2,3,4])
Z = np.sin(X*Y)+1.5
make_bars(ax, X,Y,Z, width=0.2, )
plt.show()