I have some data that consist of unevenly sampled 2D spatial locations, where each x, y coordinate has an associated phase value theta between 0 and 2pi. I'd like to be able to interpolate the theta values onto a regular x, y grid. The data is degenerate in the sense that the same (or very nearby) x, y locations may be associated with multiple phase values, and vice versa for values of theta, so this is strictly speaking a smoothing problem rather than straight interpolation.
I've briefly experimented with scipy's radial basis functions, but these give nasty edge effects because of the discontinuity in the theta values from 2pi --> 0.
Here's a toy example (the real spatial distribution of phases is a lot messier):
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import colorbar
from matplotlib.colors import Normalize
from scipy import interpolate
# randomly sampled spatial locations
x, y = np.random.uniform(-1, 1, size=(2, 1000))
# theta varies smoothly with location apart from the singularity at 0, 0
z = np.arctan2(x, y) % (2 * np.pi)
# smooth with a simple linear RBF
rbf = interpolate.Rbf(x, y, z, function='linear', smooth=0.1)
# resample on a finer grid
xi, yi = np.mgrid[-1:1:100j, -1:1:100j].reshape(2, -1)
zi = rbf(xi, yi) % (2 * np.pi)
# plotting
fig, ax = plt.subplots(1, 1, subplot_kw={'aspect': 'equal'})
ax.hold(True)
norm = Normalize(0, 2 * np.pi)
im = ax.imshow(zi.reshape(100, 100).T, extent=(-1, 1, -1, 1),
origin='lower', cmap=plt.cm.hsv, norm=norm)
sc = ax.scatter(x, y, s=30, c=z, cmap=im.cmap, norm=norm)
cax, kw = colorbar.make_axes_gridspec(ax)
cb = plt.colorbar(im, cax=cax, **kw)
ax.set_xlabel(r'$X_0$', fontsize='x-large')
ax.set_ylabel(r'$Y_0$', fontsize='x-large')
cb.set_ticks(np.arange(0, 2.1*np.pi, np.pi/2.))
cb.set_ticklabels([r'$0$', r'$\frac{\pi}{2}$', r'$\pi$',
r'$\frac{3\pi}{2}$', r'$2\pi$'])
cb.set_label(r'$\theta$', fontsize='x-large')
cb.ax.tick_params(labelsize='x-large')
plt.show()
What would be a good way to go about interpolating angular quantities like this? Does scipy have any built in interpolation method that will deal with angles nicely, or will I have to write my own?
I feel pretty stupid now!
The answer was very simple - this answer on MathOverflow clued me in. There is no problem with discontinuity provided that I convert from a polar coordinate space to a Cartesian one, then interpolate the x and y components of the vector independently:
x, y = np.random.uniform(-1, 1, size=(2, 1000))
z = np.arctan2(y, x) % (2*np.pi)
# convert from polar --> cartesian
u, v = np.cos(z), np.sin(z)
# interpolate x and y components separately
rbf_u = interpolate.Rbf(x, y, u, function='linear', smooth=0.1)
rbf_v = interpolate.Rbf(x, y, v, function='linear', smooth=0.1)
xi, yi = np.mgrid[-1:1:100j, -1:1:100j].reshape(2, -1)
ui = rbf_u(xi, yi)
vi = rbf_v(xi, yi)
# convert from cartesian --> polar
zi = np.arctan2(ui, vi) % (2*np.pi)
It would be nice performance-wise if there was a way to avoid performing two separate interpolations on the x and y components, but I don't really see a way around this.
Expanding on the accepted answer, this can be done in a single pass by using complex numbers to store the coordinates:
x, y = np.random.uniform(-1, 1, size=(2, 1000))
z = np.arctan2(y, x) % (2*np.pi)
# convert to cartesian coordinates on the complex plane
u = np.sin(z) + np.cos(z) * 1j
# interpolate x and y components separately
rbf_u = sp.interpolate.Rbf(x, y, u, function='linear', smooth=0.1)
xi, yi = np.mgrid[-1:1:100j, -1:1:100j].reshape(2, -1)
ui = rbf_u(xi, yi)
# convert from cartesian --> polar
zi = np.angle(ui) % (2*np.pi)
Related
I have 2 arrays with 3D points (name, X, Y, Z). First array contains reference points, through which I'm drawing spline. Second array contains measured points, from which I need to calculate normals to spline and get the coordinates of the normal on spline (I need to calculate the XY and height standard deviations of the measured points). This is the test data (in fact, I have several thousand points):
1st array - reference points/ generate spline:
r1,1.5602,6.0310,4.8289
r2,1.6453,5.8504,4.8428
r3,1.7172,5.6732,4.8428
r4,1.8018,5.5296,4.8474
r5,1.8700,5.3597,4.8414
2nd array - measured points:
m1, 1.8592, 5.4707, 4.8212
m2, 1.7642, 5.6362, 4.8441
m3, 1.6842, 5.7920, 4.8424
m4, 1.6048, 5.9707, 4.8465
The code I wrote, to read the data, calculate spline (using scipy) and display it via matplotlib:
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
# import measured points
filename = "measpts.csv"
meas_pts = np.genfromtxt(filename, delimiter=',')
# import reference points
filename = "refpts.csv"
ref = np.genfromtxt(filename, delimiter=',')
# divide data to X, Y, Z
x = ref[:, 2]
y = ref[:, 1]
z = ref[:, 3]
# spline interpolation
tck, u = interpolate.splprep([x, y, z], s=0)
u_new = np.linspace(u.min(), u.max(), 1000000)
x_new, y_new, z_new = interpolate.splev(u_new, tck, der=0)
xs = tck[1][0]
ys = tck[1][1]
zs = tck[1][2]
# PLOT 3D
fig = plt.figure()
ax3d = fig.add_subplot(111, projection='3d', proj_type='ortho')
ax3d.plot(x, y, z, 'ro') # ref points
ax3d.plot(xs, ys, zs, 'yo') # spline knots
ax3d.plot(x_new, y_new, z_new, 'b--') # spline
ax3d.plot(meas_pts[:, 2], meas_pts[:, 1], meas_pts[:, 3], 'g*') # measured points
# ax3d.view_init(90, -90) # 2D TOP view
# ax3d.view_init(0, -90) # 2D from SOUTH to NORTH view
# ax3d.view_init(0, 0) # 2D from EAST to WEST view
plt.show()
To sum up: I need array contains pairs: [[measured point X, Y, Z], [closest (normal) point on the spline X,Y,Z]]
Given a point P and a line in a 3d space, the distance from the point P and the points of the line is the diagonal of the box, so you wish to minimize this diagonal, the minimum distance will be normal to the line
You can use this property. So, for example
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# generate sample line
x = np.linspace(-2, 2, 100)
y = np.cbrt( np.exp(2*x) -1 )
z = (y + 1) * (y - 2)
# a point
P = (-1, 3, 2)
# 3d plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d', proj_type='ortho')
ax.plot(x, y, z)
ax.plot(P[0], P[1], P[2], 'or')
plt.show()
def distance_3d(x, y, z, x0, y0, z0):
"""
3d distance from a point and a line
"""
dx = x - x0
dy = y - y0
dz = z - z0
d = np.sqrt(dx**2 + dy**2 + dz**2)
return d
def min_distance(x, y, z, P, precision=5):
"""
Compute minimum/a distance/s between
a point P[x0,y0,z0] and a curve (x,y,z)
rounded at `precision`.
ARGS:
x, y, z (array)
P (3dtuple)
precision (integer)
Returns min indexes and distances array.
"""
# compute distance
d = distance_3d(x, y, z, P[0], P[1], P[2])
d = np.round(d, precision)
# find the minima
glob_min_idxs = np.argwhere(d==np.min(d)).ravel()
return glob_min_idxs, d
that gives
min_idx, d = min_distance(x, y, z, P)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d', proj_type='ortho')
ax.plot(x, y, z)
ax.plot(P[0], P[1], P[2], 'or')
ax.plot(x[min_idx], y[min_idx], z[min_idx], 'ok')
for idx in min_idx:
ax.plot(
[P[0], x[idx]],
[P[1], y[idx]],
[P[2], z[idx]],
'k--'
)
plt.show()
print("distance:", d[min_idx])
distance: [2.4721]
You can implement a similar function for your needs.
I'm trying to create a plot a bit like this:
Where there are spheres above all the minima.
The surface can be approximated with a sin(x)*sin(y) plot:
import numpy as np
import matplotlib.pyplot as plt
def func(x, y):
return np.sin(2*np.pi*x)*np.sin(2*np.pi*y) / 3
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-1.0, 1.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([func(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z, color="grey")
ax.set_zlim3d(-1,1)
plt.show()
However I'm unsure how to add evenly spaced spheres into this. Would anyone be able to help?
Using matplotlib one will inevitably run into problems of objects being hidden behind others. This is also stated in the matplotlib 3d FAQ and the recommendation is to use mayavi.
In mayavi the solution would look like this:
from mayavi import mlab
import numpy as np
### SURFACE '''
x,y = np.meshgrid(np.linspace(-2.5,2), np.linspace(-2,2))
f = lambda x,y: .4*np.sin(2*np.pi*x)*np.sin(2*np.pi*y)
z=f(x,y)
mlab.surf(x.T,y.T,z.T, colormap="copper")
### SPHERES '''
px,py = np.meshgrid(np.arange(-2,2)+.25, np.arange(-2,2)+.75)
px,py = px.flatten(),py.flatten()
pz = np.ones_like(px)*0.05
r = np.ones_like(px)*.4
mlab.points3d(px,py,pz,r, color=(0.9,0.05,.3), scale_factor=1)
mlab.show()
You need to determine the minima of the function, which are (with your parametrization) at (x = integer + 0.25, y=integer + 0.75) or the other way round. Then you can simply parametrize the spheres using spherical coordinates (for example as done here: python matplotlib: drawing 3D sphere with circumferences) and plot the spheres.
Now comes some good news and some bad news:
1.) The good news is that the minima are correctly determined and that the spheres are created. In the below plot you can see that they are right above the blue parts of the surface plot (where the blue parts show indeed the minima).
2.) The bad news is that you will have a hard time looking for another angle where the spheres are actually correctly rendered. I do not know a solution to this rather annoying behaviour, therefore you will probably have to play around until you have found the right angle. Have fun!
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def func(x, y):
return np.sin(2*np.pi*x)*np.sin(2*np.pi*y) / 3
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-2.0, 2.0, 0.05)
# Get the minima of the function.
minsx1 = np.arange(int(np.amin(x)) + 0.25, int(np.amax(x)) + 0.25 + 1, 1)
minsy1 = np.arange(int(np.amin(y)) + 0.75, int(np.amax(y)) + 0.75 + 1, 1)
minsx2 = np.arange(int(np.amin(x)) + 0.75, int(np.amax(x)) + 0.75 + 1, 1)
minsy2 = np.arange(int(np.amin(y)) + 0.25, int(np.amax(y)) + 0.25 + 1, 1)
X, Y = np.meshgrid(x, y)
zs = np.array([func(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
# Color map for better detection of minima (blue)
ax.plot_surface(X, Y, Z, cmap="viridis")
ax.set_zlim3d(-1,1)
# Spherical coordinates
r = 0.15
phi = np.linspace(0, 2 * np.pi, 30)
theta = np.linspace(0, np.pi, 30)
# Write spherical coordinates in cartesian coordinates.
x = r * np.outer(np.cos(phi), np.sin(theta))
y = r * np.outer(np.sin(phi), np.sin(theta))
z = r * np.outer(np.ones(np.size(phi)), np.cos(theta))
# Plot the spheres.
for xp in minsx1:
for yp in minsy1:
sphere = ax.plot_surface(x+xp, y+yp, z+0.35, color='r')
for xp in minsx2:
for yp in minsy2:
sphere = ax.plot_surface(x+xp, y+yp, z+0.35, color='r')
ax.view_init(elev=90, azim=0)
plt.savefig('test.png')
plt.show()
I was inspired by this answer by #James to see how griddata and map_coordinates might be used. In the examples below I'm showing 2D data, but my interest is in 3D. I noticed that griddata only provides splines for 1D and 2D, and is limited to linear interpolation for 3D and higher (probably for very good reasons). However, map_coordinates seems to be fine with 3D using higher order (smoother than piece-wise linear) interpolation.
My primary question: if I have random, unstructured data (where I can not use map_coordinates) in 3D, is there some way to get smoother than piece-wise linear interpolation within the NumPy SciPy universe, or at least nearby?
My secondary question: is spline for 3D not available in griddata because it is difficult or tedious to implement, or is there a fundamental difficulty?
The images and horrible python below show my current understanding of how griddata and map_coordinates can or can't be used. Interpolation is done along the thick black line.
STRUCTURED DATA:
UNSTRUCTURED DATA:
Horrible python:
import numpy as np
import matplotlib.pyplot as plt
def g(x, y):
return np.exp(-((x-1.0)**2 + (y-1.0)**2))
def findit(x, X): # or could use some 1D interpolation
fraction = (x - X[0]) / (X[-1]-X[0])
return fraction * float(X.shape[0]-1)
nth, nr = 12, 11
theta_min, theta_max = 0.2, 1.3
r_min, r_max = 0.7, 2.0
theta = np.linspace(theta_min, theta_max, nth)
r = np.linspace(r_min, r_max, nr)
R, TH = np.meshgrid(r, theta)
Xp, Yp = R*np.cos(TH), R*np.sin(TH)
array = g(Xp, Yp)
x, y = np.linspace(0.0, 2.0, 200), np.linspace(0.0, 2.0, 200)
X, Y = np.meshgrid(x, y)
blob = g(X, Y)
xtest = np.linspace(0.25, 1.75, 40)
ytest = np.zeros_like(xtest) + 0.75
rtest = np.sqrt(xtest**2 + ytest**2)
thetatest = np.arctan2(xtest, ytest)
ir = findit(rtest, r)
it = findit(thetatest, theta)
plt.figure()
plt.subplot(2,1,1)
plt.scatter(100.0*Xp.flatten(), 100.0*Yp.flatten())
plt.plot(100.0*xtest, 100.0*ytest, '-k', linewidth=3)
plt.hold
plt.imshow(blob, origin='lower', cmap='gray')
plt.text(5, 5, "don't use jet!", color='white')
exact = g(xtest, ytest)
import scipy.ndimage.interpolation as spndint
ndint0 = spndint.map_coordinates(array, [it, ir], order=0)
ndint1 = spndint.map_coordinates(array, [it, ir], order=1)
ndint2 = spndint.map_coordinates(array, [it, ir], order=2)
import scipy.interpolate as spint
points = np.vstack((Xp.flatten(), Yp.flatten())).T # could use np.array(zip(...))
grid_x = xtest
grid_y = np.array([0.75])
g0 = spint.griddata(points, array.flatten(), (grid_x, grid_y), method='nearest')
g1 = spint.griddata(points, array.flatten(), (grid_x, grid_y), method='linear')
g2 = spint.griddata(points, array.flatten(), (grid_x, grid_y), method='cubic')
plt.subplot(4,2,5)
plt.plot(exact, 'or')
#plt.plot(ndint0)
plt.plot(ndint1)
plt.plot(ndint2)
plt.title("map_coordinates")
plt.subplot(4,2,6)
plt.plot(exact, 'or')
#plt.plot(g0)
plt.plot(g1)
plt.plot(g2)
plt.title("griddata")
plt.subplot(4,2,7)
#plt.plot(ndint0 - exact)
plt.plot(ndint1 - exact)
plt.plot(ndint2 - exact)
plt.title("error map_coordinates")
plt.subplot(4,2,8)
#plt.plot(g0 - exact)
plt.plot(g1 - exact)
plt.plot(g2 - exact)
plt.title("error griddata")
plt.show()
seed_points_rand = 2.0 * np.random.random((400, 2))
rr = np.sqrt((seed_points_rand**2).sum(axis=-1))
thth = np.arctan2(seed_points_rand[...,1], seed_points_rand[...,0])
isinside = (rr>r_min) * (rr<r_max) * (thth>theta_min) * (thth<theta_max)
points_rand = seed_points_rand[isinside]
Xprand, Yprand = points_rand.T # unpack
array_rand = g(Xprand, Yprand)
grid_x = xtest
grid_y = np.array([0.75])
plt.figure()
plt.subplot(2,1,1)
plt.scatter(100.0*Xprand.flatten(), 100.0*Yprand.flatten())
plt.plot(100.0*xtest, 100.0*ytest, '-k', linewidth=3)
plt.hold
plt.imshow(blob, origin='lower', cmap='gray')
plt.text(5, 5, "don't use jet!", color='white')
g0rand = spint.griddata(points_rand, array_rand.flatten(), (grid_x, grid_y), method='nearest')
g1rand = spint.griddata(points_rand, array_rand.flatten(), (grid_x, grid_y), method='linear')
g2rand = spint.griddata(points_rand, array_rand.flatten(), (grid_x, grid_y), method='cubic')
plt.subplot(4,2,6)
plt.plot(exact, 'or')
#plt.plot(g0rand)
plt.plot(g1rand)
plt.plot(g2rand)
plt.title("griddata")
plt.subplot(4,2,8)
#plt.plot(g0rand - exact)
plt.plot(g1rand - exact)
plt.plot(g2rand - exact)
plt.title("error griddata")
plt.show()
Good question! (and nice plots!)
For unstructured data, you'll want to switch back to functions meant for unstructured data. griddata is one option, but uses triangulation with linear interpolation in between. This leads to "hard" edges at triangle boundaries.
Splines are radial basis functions. In scipy terms, you want scipy.interpolate.Rbf. I'd recommend using function="linear" or function="thin_plate" over cubic splines, but cubic is available as well. (Cubic splines will exacerbate problems with "overshooting" compared to linear or thin-plate splines.)
One caveat is that this particular implementation of radial basis functions will always use all points in your dataset. This is the most accurate and smooth approach, but it scales poorly as the number of input observation points increases. There are several ways around this, but things will get more complex. I'll leave that for another question.
At any rate, here's a simplified example. We'll generate random data and then interpolate it at points that are on a regular grid. (Note that the input is not on a regular grid, and the interpolated points don't need to be either.)
import numpy as np
import scipy.interpolate
import matplotlib.pyplot as plt
np.random.seed(1977)
x, y, z = np.random.random((3, 10))
interp = scipy.interpolate.Rbf(x, y, z, function='thin_plate')
yi, xi = np.mgrid[0:1:100j, 0:1:100j]
zi = interp(xi, yi)
plt.plot(x, y, 'ko')
plt.imshow(zi, extent=[0, 1, 1, 0], cmap='gist_earth')
plt.colorbar()
plt.show()
Choice of spline type
I chose "thin_plate" as the type of spline. Our input observations points range from 0 to 1 (they're created by np.random.random). Notice that our interpolated values go slightly above 1 and well below zero. This is "overshooting".
Linear splines will completely avoid overshooting, but you'll wind up with "bullseye" patterns (nowhere near as severe as with IDW methods, though). For example, here's the exact same data interpolated with a linear radial basis function. Notice that our interpolated values never go above 1 or below 0:
Higher order splines will make trends in the data more continuous but will overshoot more. The default "multiquadric" is fairly similar to a thin-plate spline, but will make things a bit more continuous and overshoot a bit worse:
However, as you go to even higher order splines such as "cubic" (third order):
and "quintic" (fifth order)
You can really wind up with unreasonable results as soon as you move even slightly beyond your input data.
At any rate, here's a simple example to compare different radial basis functions on random data:
import numpy as np
import scipy.interpolate
import matplotlib.pyplot as plt
np.random.seed(1977)
x, y, z = np.random.random((3, 10))
yi, xi = np.mgrid[0:1:100j, 0:1:100j]
interp_types = ['multiquadric', 'inverse', 'gaussian', 'linear', 'cubic',
'quintic', 'thin_plate']
for kind in interp_types:
interp = scipy.interpolate.Rbf(x, y, z, function=kind)
zi = interp(xi, yi)
fig, ax = plt.subplots()
ax.plot(x, y, 'ko')
im = ax.imshow(zi, extent=[0, 1, 1, 0], cmap='gist_earth')
fig.colorbar(im)
ax.set(title=kind)
fig.savefig(kind + '.png', dpi=80)
plt.show()
I have ran into a problem relating to the drawing of the Ellipsoid.
The ellipsoid that I am drawing to draw is the following:
x**2/16 + y**2/16 + z**2/16 = 1.
So I saw a lot of references relating to calculating and plotting of an Ellipse void and in multiple questions a cartesian to spherical or vice versa calculation was mentioned.
Ran into a website that had a calculator for it, but I had no idea on how to successfully perform this calculation. Also I am not sure as to what the linspaces should be set to. Have seen the ones that I have there as defaults, but as I got no previous experience with these libraries, I really don't know what to expect from it.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=plt.figaspect(1)) # Square figure
ax = fig.add_subplot(111, projection='3d')
multip = (1, 1, 1)
# Radii corresponding to the coefficients:
rx, ry, rz = 1/np.sqrt(multip)
# Spherical Angles
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
# Cartesian coordinates
#Lots of uncertainty.
#x =
#y =
#z =
# Plot:
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
# Axis modifications
max_radius = max(rx, ry, rz)
for axis in 'xyz':
getattr(ax, 'set_{}lim'.format(axis))((-max_radius, max_radius))
plt.show()
Your ellipsoid is not just an ellipsoid, it's a sphere.
Notice that if you use the substitution formulas written below for x, y and z, you'll get an identity. It is in general easier to plot such a surface of revolution in a different coordinate system (spherical in this case), rather than attempting to solve an implicit equation (which in most plotting programs ends up jagged, unless you take some countermeasures).
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
phi = np.linspace(0,2*np.pi, 256).reshape(256, 1) # the angle of the projection in the xy-plane
theta = np.linspace(0, np.pi, 256).reshape(-1, 256) # the angle from the polar axis, ie the polar angle
radius = 4
# Transformation formulae for a spherical coordinate system.
x = radius*np.sin(theta)*np.cos(phi)
y = radius*np.sin(theta)*np.sin(phi)
z = radius*np.cos(theta)
fig = plt.figure(figsize=plt.figaspect(1)) # Square figure
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z, color='b')
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')