Related
I wish to plot a bunch of points onto a polar plot. When I apply it with simulated data, it works. When I try the same with my real data it fails and I'm not sure why.
# First with simulated data
# The angles for each point
phi = np.linspace(0, math.pi*2, 40) # full circle
phi = np.concatenate([phi, phi, phi, phi]) # 4 full circles
# The radii
rho = np.array([0,1,2,3]) # the radii for each circle
rho = np.repeat(rho, 40)
assert phi.shape == rho.shape
# First just plot the points
plt.figure()
ax = plt.subplot(111, projection='polar')
ax.scatter(phi, rho, c=rho)
ax.set_ylim(0,4)
This appears to work. Next, I use the following to draw concentric contours around the origin. The Z property is set to the radii, i.e., points that equally far away from the origin should be grouped within a contour.
# Create a meshgrid from the points
X, Y = np.meshgrid(phi, rho)
plt.figure()
ax = plt.subplot(111, projection='polar')
ax.scatter(X, Y, c=Y)
CS = ax.contourf(X,Y, Y, 2, alpha=0.4)
ax.set_ylim(0,4)
This works exactly as I want it:
Next, I want to apply this to my real data. My actual data has millions of data points, but here's just a small sample to reproduce.
rho = np.array([0.38818333, 0.73367091, 0.42336148, 1.39013061, 0.31064486,0.34546275, 0.05445943, 0.85551576, 0.55174167, 1.42371249,0.17644804, 1.76221456, 0.64519126, 0.02408941, 1.43986863,0.72718428, 0.4262945 , 0.1355583 , 0.86319986, 0.71212376,0.14891707, 1.01624534, 1.26915981, 1.39384488, 0.09623481,0.92635469, 1.74757901, 0.15811954, 0.22052651, 0.30784166,0.92740352, 1.29621377, 0.29832842, 1.04442307, 1.36185399,0.42979785, 0.94402815, 0.3786981 , 0.75865969, 1.97273479,0.61140136, 0.71452862, 0.25793468, 1.1751275 , 1.53945948,0.64150917, 0.09274101, 0.52548715, 0.7932458 , 0.90292444])
phi = np.array([1.04208195, 4.67055389, 3.32909655, 1.18709268, 0.86036178,5.820191 , 4.30457004, 1.81242968, 0.64295926, 4.85684143,2.73937709, 3.22891963, 0.25822595, 0.69526782, 0.70709764,1.92901075, 3.44538869, 5.38541473, 0.95255568, 4.01519928,0.8503274 , 5.26774545, 4.07787945, 4.51718652, 0.3170884 ,2.1946835 , 3.12550771, 5.67275731, 1.0000195 , 1.82570239,5.62578391, 0.81923255, 2.00131474, 0.48190872, 4.78875363,5.60395833, 2.01674743, 2.13494958, 5.10829845, 0.95324309,1.59531506, 4.99145225, 6.19873491, 3.32802456, 1.15590926,0.52989939, 6.02205398, 3.66013508, 4.16276819, 2.60498467])
assert rho.shape == phi.shape # both are (50,)
plt.figure()
ax = plt.subplot(111, projection='polar')
ax.scatter(phi, rho, c=rho)
ax.set_ylim(0,3)
Of course, my real data has much more variation that the simulated circles above. Next, I try to draw the contours in the same method. However, this now fails:
# Create a meshgrid from the points
X, Y = np.meshgrid(phi, rho)
plt.figure(figsize=(10,10)) # a little bigger to see better
ax = plt.subplot(111, projection='polar')
ax.scatter(X, Y, c=Y)
CS = ax.contourf(X,Y, Y, 2, alpha=0.4)
ax.set_ylim(0,4)
This behaviour is due to the fact that rho and phi are not sorted. Let's see:
import matplotlib.pyplot as plt
import numpy as np
rho = np.array([0.38818333, 0.73367091, 0.42336148, 1.39013061, 0.31064486,0.34546275, 0.05445943, 0.85551576, 0.55174167, 1.42371249,0.17644804, 1.76221456, 0.64519126, 0.02408941, 1.43986863,0.72718428, 0.4262945 , 0.1355583 , 0.86319986, 0.71212376,0.14891707, 1.01624534, 1.26915981, 1.39384488, 0.09623481,0.92635469, 1.74757901, 0.15811954, 0.22052651, 0.30784166,0.92740352, 1.29621377, 0.29832842, 1.04442307, 1.36185399,0.42979785, 0.94402815, 0.3786981 , 0.75865969, 1.97273479,0.61140136, 0.71452862, 0.25793468, 1.1751275 , 1.53945948,0.64150917, 0.09274101, 0.52548715, 0.7932458 , 0.90292444])
phi = np.array([1.04208195, 4.67055389, 3.32909655, 1.18709268, 0.86036178,5.820191 , 4.30457004, 1.81242968, 0.64295926, 4.85684143,2.73937709, 3.22891963, 0.25822595, 0.69526782, 0.70709764,1.92901075, 3.44538869, 5.38541473, 0.95255568, 4.01519928,0.8503274 , 5.26774545, 4.07787945, 4.51718652, 0.3170884 ,2.1946835 , 3.12550771, 5.67275731, 1.0000195 , 1.82570239,5.62578391, 0.81923255, 2.00131474, 0.48190872, 4.78875363,5.60395833, 2.01674743, 2.13494958, 5.10829845, 0.95324309,1.59531506, 4.99145225, 6.19873491, 3.32802456, 1.15590926,0.52989939, 6.02205398, 3.66013508, 4.16276819, 2.60498467])
plt.figure(figsize=(10,10))
ax = plt.subplot(111, projection='polar')
ax.set_ylim(0,4)
ax.scatter(phi, rho, c=rho)
#phi.sort()
#rho.sort()
X, Y = np.meshgrid(phi, rho)
CS = ax.contourf(X, Y, Y, 2, alpha=0.4)
plt.show()
gives:
If you uncomment the sorting lines:
But now the contours look weird, because the base data does not the whole geometry of interest. So we can create same data just for the purpose of the filled contours, instead:
alfa = np.radians(np.linspace(0, 360, 60))
r = np.arange(0, np.max(rho)+np.max(rho)/60, np.max(rho)/60)
r, alfa = np.meshgrid(r, alfa)
ax.contourf(alfa, r, r, 2, alpha=0.4)
Giving:
I want to draw a bar plot in 3d. I know how to do that using the following code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, projection='3d')
nbins = 50
# for c, z in zip(['r', 'g', 'b', 'y'], [30, 20, 10, 0]):
ys = np.random.normal(loc=10, scale=10, size=2000)
hist, bins = np.histogram(ys, bins=nbins)
xs = (bins[:-1] + bins[1:])/2
ax.bar(xs, hist, zs=30, zdir='y', color='r', ec='r', alpha=0.8)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
This will render something like this: https://i.stack.imgur.com/KK2If.png
However, my goal is to make the bar plot follows a line that I give as parameter. For example here, the parameter zdir='y' makes the plot have its current direction. Ideally I want to pass a parameter that makes the plot follows a given line for example y=2x+1.
Could someone help arrive at the desired result?
One way to achieve that is by using Poly3DCollection: the idea is to compute the coordinates and orientation of each bar, then add it to the plot.
The position and orientation of each bar can be computed starting from a rectangle in 3D space and applying the appropriate transformation matrix.
If you are going to change the curve, you will also need to change the bar width.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.patches import Rectangle
################
# Generates data
################
nbins = 50
ys = np.random.normal(loc=10, scale=10, size=2000)
hist, bins = np.histogram(ys, bins=nbins)
xs = (bins[:-1] + bins[1:])/2
#################################################
# Create a single bar and a transformation matrix
#################################################
# rectangle of width=height=1, centered at x,y=0
# covering the z range [0, height]
rect = np.array([
[-0.5, 0, 0, 1],
[0.5, 0, 0, 1],
[0.5, 0, 1, 1],
[-0.5, 0, 1, 1],
])
def translate(x, y, z):
d = np.eye(4, dtype=float)
d[:, -1] = [x, y, z, 1]
return d
def scale(sx, sy, sz):
d = np.eye(4, dtype=float)
d[np.diag_indices(4)] = [sx, sy, sz, 1]
return d
def rotate(t):
d = np.eye(4, dtype=float)
d[:2, :2] = np.array([
[np.cos(t), -np.sin(t)],
[np.sin(t), np.cos(t)]])
return d
def transformation_matrix(t, x, y, z, w, h):
return translate(x, y, z) # rotate(t) # scale(w, 1, h)
def apply_transform(t, x, y, z, w, h):
"""Apply the transformation matrix to the rectangle"""
verts = transformation_matrix(t, x, y, z, w, h) # rect.T
return verts.T
#################
# Create the plot
#################
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
curve = lambda x: 2 * x + 1
# curve = lambda x: np.sin(0.05 * x)
xstep = abs(xs[0] - xs[1])
# NOTE: chose an appropriate bar width
width = xstep * 1.5
ys = curve(xs)
# previous bar coordinates
xp = np.roll(xs, 1)
yp = np.roll(ys, 1)
xp[0] = xs[0] - xstep
yp[0] = curve(xp[0])
# compute the orientation of the bars
theta = np.arctan2((ys - yp), (xs - xp))
# customize the appearance of the bar
facecolor = "tab:red"
edgecolor = "k"
linewidth = 0
# loop to add each bar
for x, y, t, h in zip(xs, ys, theta, hist):
verts_matrix = apply_transform(t, x, y, 0, width, h)
x, y, z = verts_matrix[:, 0], verts_matrix[:, 1], verts_matrix[:, 2]
verts = [list(zip(x, y, z))]
c = Poly3DCollection(verts, facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth)
ax.add_collection3d(c)
# eventually show a legend
ax.legend([Rectangle((0, 0), 1, 1, facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth)], ["Bar Plot"])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_xlim(xs.min(), xs.max())
ax.set_ylim(ys.min(), ys.max())
ax.set_zlim(0, 100)
plt.show()
EDIT to explain what is going on:
Consider a generic rectangle with 4 vertices: bottom left, bottom right, top right, top left. For simplicity, let's fix width=height=1. Then we consider a reference system x,y,z and we draw this rectangle. The coordinates of vertices are: bottom left (-0.5, 0, 0), bottom right (0.5, 0, 0), top right (0.5, 0, 1) and top left (-0.5, 0, 1). Note that this rectangle is centered around the zero in the x direction. If we move it to x=2, then it will be centered at that location. You can see the above coordinates in rect: why does this variable has a fourth column filled with ones? That's a mathematical trick to be able to apply a translation matrix to the vertices.
Let's talk about transformation matrices (wikipedia has a nice page about it). Consider again our generic rectangle: we can scale it, rotate it and translate it to get a new rectangle in the position and orientation we want.
So, the code above defines a function for each transformation, translate, scale, rotate. Turns out that we can multiply together multiple transformation matrices to get an overall transformation: that's what transformation_matrix does, it combines the aforementioned transformations into a single matrix.
Finally, I used apply_transform to apply the transformation matrix to the generic rectangle: this will compute the coordinates of the vertices of the new rectangle, in the specified position/orientation with the specified size (width, height).
I'm able to plot a surface in 3d in matplotlib, but I also need to plot a line, and a point on the surface. The surface that the line are fine, but the point does not show up on the surface for some reason, though. Here is the code:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-2.0, 2.0, 0.05)
Y = np.arange(-1.0, 3.0, 0.05)
X, Y = np.meshgrid(X, Y)
Z = (np.ones([np.shape(X)[0],np.shape(X)[1]])-X)**2+100*(Y-(X)**2)**2
Gx, Gy = np.gradient(Z) # gradients with respect to x and y
G = (Gx**2+Gy**2)**.5 # gradient magnitude
N = G/G.max() # normalize 0..1
surf = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1,
facecolors=cm.jet(N),
linewidth=0,
antialiased=False,
shade=False)
plt.hold(True)
ax.hold(True)
# add the unit circle
x_1 = np.arange(-1.0, 1.0, 0.005)
x_2 = np.arange(-1.0, 1.0, 0.005)
y_1 = np.sqrt(np.ones(len(x_1)) - x_1**2)
y_2 = -np.sqrt(np.ones(len(x_2)) - x_2**2)
x = np.array(x_1.tolist() + x_2.tolist())
y = np.array(y_1.tolist() + y_2.tolist())
z = (np.ones(len(x))-x)**2+100*(y-(x)**2)**2
ax.plot(x, y, z, '-k')
plt.hold(True)
ax.hold(True)
ax.scatter(np.array([0.8]),
np.array([0.6]),
np.array([0.045]),
color='red',
s=40
)
# Get current rotation angle
print 'rotation angle is ', ax.azim
# Set rotation angle to 60 degrees
ax.view_init(azim=60)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
The issue is that the point does not show up on the surface. Now, when I replace this code:
ax.scatter(np.array([0.8]),
np.array([0.6]),
np.array([0.045]),
color='red',
s=40
)
...with this code (i.e. just adding to the last value)...
ax.scatter(np.array([0.8]),
np.array([0.6]),
np.array([0.045+800]),
color='red',
s=40
)
...then it shows up. But I can't think of a reason why it is not showing up when I want to plot the actual value in the surface. Does someone know how to fix this?
(As an aside, I'd love to get rid of the weird line in the middle of the unit circle that I plot on the surface. I can't seem to get rid of it.)
Much obliged!
I would like to have a 3d plot with matplotlib.
Data are the following: I have a matrix with each row containing Y coordinates for the 3d plot. Each row first elements are the X coordinates for the 3d plot. Finally, a second matrix contains high for each point, at a X,Y position. This second matrix thus contains my Z coordinates. Both matrices are arrays of arrays with Python. I would like to know how to transform data so as to obtain:
a plot of each 1d signal corresponding to an X, like this (photo available online)
a wireframe plot for same data, like this
I have written an helper function for a wireframe work,
######## HELPER FOR PLOT 3-D
def plot_3d(name,X,Y,Z):
fig = plt.figure(name)
ax = fig.gca(projection='3d')
X = np.array(X)
Y = np.array(Y)
Z = np.array(Z)
ax.plot_wireframe(X,Y,Z,rstride=10,cstride=10)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
plt.show()
but I dont know how to transform data X,Y,Z to make them fit requirements for matplotlib function, which want 2D lists for X, Y ,Z.
For first graph, I read help, and want to use 2d plot in 3d. Example source code gives:
x = np.linspace(0, 1, 100)
y = np.sin(x * 2 * np.pi) / 2 + 0.5
ax.plot(x, y, zs=0, zdir='z', label='zs=0, zdir=z')
where z is the constant coordinate. In my case, x is the constant coordinate. I adapt with
fig = plt.figure('2d profiles')
ax = fig.gca(projection='3d')
for i in range(10):
x = pt ## this is a scalar
y = np.array(y)
z = np.array(z)
ax.plot(xs = x, y, z, xdir='x')
plt.show()
but there is warning: non-keyword arg after keyword arg. How to fix?
Thanks and regards
Regarding the display of a serie of vectors in 3D, I came with following 'almost working' solution:
def visualizeSignals(self, imin, imax):
times = self.time[imin:imax]
nrows = (int)((times[(len(times)-1)] - times[0])/self.mod) + 1
fig = plt.figure('2d profiles')
ax = fig.gca(projection='3d')
for i in range(nrows-1):
x = self.mat1[i][0] + self.mod * i
y = np.array(self.mat1T[i])
z = np.array(self.mat2[i])
ax.plot(y, z, zs = x, zdir='z')
plt.show()
As for 2D surface or meshgrid plot, I come through using meshgrid. Note that you can reproduce a meshgrid by yourself once you know how a meshgrid is built. For more info on meshgrid, I refer to this post.
Here is the code (cannot use it as such since it refers to class members, but you can build your code based on 3d plot methods from matplotlib I am using)
def visualize(self, imin, imax, typ_ = "wireframe"):
"""
3d plot signal between imin and imax
. typ_: type of plot, "wireframce", "surface"
"""
times = self.retT[imin:imax]
nrows = (int)((times[(len(times)-1)] - times[0])/self.mod) + 1
self.modulate(imin, imax)
fig = plt.figure('3d view')
ax = fig.gca(projection='3d')
x = []
for i in range(nrows):
x.append(self.matRetT[i][0] + self.mod * i)
y = []
for i in range(len(self.matRetT[0])):
y.append(self.matRetT[0][i])
y = y[:-1]
X,Y = np.meshgrid(x,y)
z = [tuple(self.matGC2D[i]) for i in range(len(self.matGC))] # matGC a matrix
zzip = zip(*z)
for i in range(len(z)):
print len(z[i])
if(typ_ == "wireframe"):
ax.plot_wireframe(X,Y,zzip)
plt.show()
elif(typ_ == "contour"):
cset = ax.contour(X, Y, zzip, zdir='z', offset=0)
plt.show()
elif(typ_ == "surf_contours"):
surf = ax.plot_surface(X, Y, zzip, rstride=1, cstride=1, alpha=0.3)
cset = ax.contour(X, Y, zzip, zdir='z', offset=-40)
cset = ax.contour(X, Y, zzip, zdir='x', offset=-40)
cset = ax.contour(X, Y, zzip, zdir='y', offset=-40)
plt.show()
So i have a meshgrid (matrices X and Y) together with scalar data (matrix Z), and i need to visualize this. Preferably some 2D image with colors at the points showing the value of Z there.
I've done some research but haven't found anything which does exactly what i want.
pyplot.imshow(Z) has a good look, but it doesn't take my X and Y matrices, so the axes are wrong and it is unable to handle non-linearly spaced points given by X and Y.
pyplot.pcolor(X,Y,Z) makes colored squares with colors corresponding to the data at one of its corners, so it kind of misrepresents the data (it should show the data in its center or something). In addition it ignores two of the edges from the data matrix.
I pretty sure there must exist some better way somewhere in Matplotlib, but the documentation makes it hard to get an overview. So i'm asking if someone else knows of a better way. Bonus if it allows me to refresh the matrix Z to make an animation.
This looks nice, but it's inefficient:
from pylab import *
origin = 'lower'
delta = 0.025
x = y = arange(-3.0, 3.01, delta)
X, Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10 * (Z1 - Z2)
nr, nc = Z.shape
CS = contourf(
X, Y, Z,
levels = linspace(Z.min(), Z.max(), len(x)),
ls = '-',
cmap=cm.bone,
origin=origin)
CS1 = contour(
CS,
levels = linspace(Z.min(), Z.max(), len(x)),
ls = '-',
cmap=cm.bone,
origin=origin)
show()
It it were me, I'd re-interpolate (using scipy.interpolate) the data to a regular grid and use imshow(), setting the extents to fix the axes.
Edit (per comment):
Animating a contour plot can be accomplished like this, but, like I said, the above is inefficient just plain abuse of the contour plot function. The most efficient way to do what you want is to employ SciPy. Do you have that installed?
import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab
import time
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
def animate():
origin = 'lower'
delta = 0.025
x = y = arange(-3.0, 3.01, delta)
X, Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10 * (Z1 - Z2)
CS1 = ax.contourf(
X, Y, Z,
levels = linspace(Z.min(), Z.max(), 10),
cmap=cm.bone,
origin=origin)
for i in range(10):
tempCS1 = contourf(
X, Y, Z,
levels = linspace(Z.min(), Z.max(), 10),
cmap=cm.bone,
origin=origin)
del tempCS1
fig.canvas.draw()
time.sleep(0.1)
Z += x/10
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()
If your meshgrid has uniform spacing, you could continue to use pcolor, but just shift X and Y for the purposes of centering the data at the particular values rather than at the corners.
You could also use a scatter plot to explicitly place points of some size at the exact X and Y points and then set the color to Z:
x = numpy.arange(10)
y = numpy.arange(10)
X,Y = numpy.meshgrid(x,y)
Z = numpy.arange(100).reshape((10,10))
scatter(X,Y,c=Z,marker='s',s=1500)
#I picked a marker size that basically overlapped the symbols at the edges
axis('equal')
or:
pcolor(X+0.5,Y+0.5,Z)
axis('equal')
or as Paul suggested, using one of the contour functions
In case anyone comes across this article looking for what I was looking for, I took the above example and modified it to use imshow with an input stack of frames, instead of generating and using contours on the fly. Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.
def animate_frames(frames):
nBins = frames.shape[0]
frame = frames[0]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
for k in range(nBins):
frame = frames[k]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
del tempCS1
fig.canvas.draw()
#time.sleep(1e-2) #unnecessary, but useful
fig.clf()
fig = plt.figure()
ax = fig.add_subplot(111)
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)
I also found a much simpler way to go about this whole process, albeit less robust:
fig = plt.figure()
for k in range(nBins):
plt.clf()
plt.imshow(frames[k],cmap=plt.cm.gray)
fig.canvas.draw()
time.sleep(1e-6) #unnecessary, but useful
Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg
Thank you for the help with everything.
The following function creates boxes of half the size at the boundary (as shown in the attached picture).
import matplotlib.pyplot as plt
import numpy as np
from scipy.ndimage.filters import convolve
def pcolor_all(X, Y, C, **kwargs):
X = np.concatenate([X[0:1,:], X], axis=0)
X = np.concatenate([X[:,0:1], X], axis=1)
Y = np.concatenate([Y[0:1,:], Y], axis=0)
Y = np.concatenate([Y[:,0:1], Y], axis=1)
X = convolve(X, [[1,1],[1,1]])/4
Y = convolve(Y, [[1,1],[1,1]])/4
plt.pcolor(X, Y, C, **kwargs)
X, Y = np.meshgrid(
[-1,-0.5,0,0.5,1],
[-2,-1,0,1,2])
C = X**2-Y**2
plt.figure(figsize=(4,4))
pcolor_all(X, Y, C, cmap='gray')
plt.savefig('plot.png')