Best way to plot a 2d contour plot with a numpy meshgrid - python

i'm looking for the best way to create a contour plot using a numpy meshgrid.
I have excel data in columns simplyfied looking like this:
x data values: -3, -2, -1, 0, 1, 2 ,3, -3, -2, -1, 0, 1, 2, 3
y data values: 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2
z data values: 7 , 5, 6, 5, 1, 0, 9, 5, 3, 8, 3, 1, 0, 4
The x and y values define a 2d plane with the length (x-Axis) of 7 values and depth (y-Axis) of 2 values. The z values define the colour at the corresponing points (more or less a z-Axis).
I've tried:
import matplotlib.pyplot as plt
import numpy as np
x = [-3,-2,-1,0,1,2,3]
y = [1,2]
z = [7,5,6,5,1,0,9,5,3,8,3,1,0,4]
x, y = np.meshgrid(x, y)
A = np.array(z)
B = np.reshape(A, (-1, 2))
fig = plt.figure()
ax1 = plt.contourf(x, y, B)
plt.show()
I'm pretty sure i'm not getting how the meshgrid works. Do i have to use the whole List of x and y values for it to work?
How do i create a rectangular 2d plot with the length (x) of 7 and the depth (y) of 2 and the z values defining the shading/colour at the x and y values?
Thanks in advance guys!

Try
x_, y_ = np.meshgrid(x, y)
z_grid = np.array(z).reshape(2,7)
fig = plt.figure()
ax1 = plt.contourf(x_,y_,z_grid)
plt.show()
Edit: If you would like to smooth, as per your comment, you can try something like scipy.ndimage.zoom() as described here, i.e., in your case
from scipy import ndimage
z_grid = np.array(z).reshape(2,7)
z_grid_interp = ndimage.zoom(z_grid, 100)
x_, y_ = np.meshgrid(np.linspace(-3,3,z_grid_interp.shape[1]),np.linspace(1,2,z_grid_interp.shape[0]))
and then plot as before:
fig = plt.figure()
ax1 = plt.contourf(x_,y_,z_grid_interp)
plt.show()

This is one way where you use the shape of the meshgrid (X or Y) to reshape your z array. You can, moreover, add a color bar using plt.colorbar()
import matplotlib.pyplot as plt
import numpy as np
x = [-3,-2,-1,0,1,2,3]
y = [1,2]
z = np.array([7,5,6,5,1,0,9,5,3,8,3,1,0,4])
X, Y = np.meshgrid(x, y)
print (X.shape, Y.shape)
# (2, 7) (2, 7) Both have same shape
Z = z.reshape(X.shape) # Use either X or Y to define shape
fig = plt.figure()
ax1 = plt.contourf(X, Y, Z)
plt.colorbar(ax1)
plt.show()

def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2, 3 )
y = np.linspace(0, 3, 4)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
plt.contour(X, Y, Z, cmap='RdGy');

Related

Convert four-dimensional numpy array to list of x, y, z, intensity

I have a numpy array created as follows
results = np.zeros((X, Y, Z))
Then I am setting values of the points in 3D space as follows (representative of density / intensity of that point)
results[x,y,z] = 5.0
I now want to visualize this data using the x,y,z coordinates and an intensity value (like opacity or size of a scatter plot). However I cannot figure out how to convert this into four lists of x, y, z, and intensity, for a 3D scatter plot. How do I do this?
i would do smth like this:
import numpy as np
import matplotlib.pyplot as plt
dots = np.random.randint(0, 2, size = (3, 3, 3))
dots *= np.random.randint(0, 2, size = (3, 3, 3))
dots *= np.arange(27).reshape(3, 3, 3)
x, y, z = np.where(dots!=0)
o = dots[x, y, z]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
for i in range(len(x)):
print(o[i]/27)
ax.plot([x[i]], [y[i]], [z[i]], 'o', color=[0, 0, 0, float(o[i])/27])
output:
dots =
[[[ 0 0 0]
[ 0 0 0]
[ 6 0 0]]
[[ 0 0 11]
[ 0 13 0]
[15 16 17]]
[[ 0 0 0]
[21 22 23]
[24 0 0]]]
My solution:
fig = plt.figure(figsize=(15, 15))
ax = fig.add_subplot(projection="3d")
plt.title("Spherical Potential Heatmap ($J = 32, simuls = 6.4M, E = 1, cutoff = 100$)", fontsize=18)
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
mask = base_array_e0 > 100
idx = np.arange(int(np.prod(base_array_e0.shape)))
x, y, z = np.unravel_index(idx, base_array_e0.shape)
plot = ax.scatter(x, y, z, c=base_array_e0.flatten(), s=10.0 * mask, edgecolor="face", alpha=0.15, marker="o", cmap="magma", linewidth=0)
color_bar = plt.colorbar(plot, ax = ax,fraction=0.036, pad=0.04)
color_bar.set_alpha(1)
color_bar.draw_all()
color_bar.set_label('Steps')
plt.savefig('random_walk_3d_energy_sphere_0.png', bbox_inches='tight');

Fill the area between multiple curves and lines

I have 3 curves and I want to fill the area between them. How should I do this?
This is what I have so far:
import numpy as np
import matplotlib.pyplot as plt
y = lambda z: -(z ** 2)
y1 = lambda x: x ** (1 / 3)
x = np.linspace(0, 2, 100)
z = np.linspace(0, 2, 100)
plt.plot(z, y(z), color='blue', label="y=-(x^2)")
plt.ylim(-2, 2)
plt.xlim(0, 2)
plt.plot(x, y1(x), color='red', label='y=x^(1/3)')
plt.plot([1, 1, 1], [0, -2, 2], color='black', label='x=1')
plt.grid(True, zorder=5)
plt.legend()
k = np.arange(0,2)
f = [0,-0.2]
p = [0,0.2]
plt.fill_between(k,f,p,interpolate=True)
plt.show()
You can use where in fill_between to take care of x = 1 line. See below:
import numpy as np
import matplotlib.pyplot as plt
y = lambda z: -(z ** 2)
y1 = lambda x: x ** (1 / 3)
x = np.linspace(0, 2, 100)
z = np.linspace(0, 2, 100)
plt.ylim(-2, 2)
plt.xlim(0, 2)
#plt.grid(True, zorder=5)
plt.plot(z, y(z), color='blue', label="y=-(x^2)")
plt.plot(x, y1(x), color='red', label='y=x^(1/3)')
plt.plot([1, 1, 1], [0, -2, 2], color='black', label='x=1')
plt.fill_between(x, y(z), y1(x), where=x<=1)
plt.legend()
plt.show()

Python numpy: create 2d array of values based on coordinates

I have a file containing 3 columns, where the first two are coordinates (x,y) and the third is a value (z) corresponding to that position. Here's a short example:
x y z
0 1 14
0 2 17
1 0 15
1 1 16
2 1 18
2 2 13
I want to create a 2D array of values from the third row based on their x,y coordinates in the file. I read in each column as an individual array, and I created grids of x values and y values using numpy.meshgrid, like this:
x = [[0 1 2] and y = [[0 0 0]
[0 1 2] [1 1 1]
[0 1 2]] [2 2 2]]
but I'm new to Python and don't know how to produce a third grid of z values that looks like this:
z = [[Nan 15 Nan]
[14 16 18]
[17 Nan 13]]
Replacing Nan with 0 would be fine, too; my main problem is creating the 2D array in the first place. Thanks in advance for your help!
Assuming the x and y values in your file directly correspond to indices (as they do in your example), you can do something similar to this:
import numpy as np
x = [0, 0, 1, 1, 2, 2]
y = [1, 2, 0, 1, 1, 2]
z = [14, 17, 15, 16, 18, 13]
z_array = np.nan * np.empty((3,3))
z_array[y, x] = z
print z_array
Which yields:
[[ nan 15. nan]
[ 14. 16. 18.]
[ 17. nan 13.]]
For large arrays, this will be much faster than the explicit loop over the coordinates.
Dealing with non-uniform x & y input
If you have regularly sampled x & y points, then you can convert them to grid indices by subtracting the "corner" of your grid (i.e. x0 and y0), dividing by the cell spacing, and casting as ints. You can then use the method above or in any of the other answers.
As a general example:
i = ((y - y0) / dy).astype(int)
j = ((x - x0) / dx).astype(int)
grid[i,j] = z
However, there are a couple of tricks you can use if your data is not regularly spaced.
Let's say that we have the following data:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
x, y, z = np.random.random((3, 10))
fig, ax = plt.subplots()
scat = ax.scatter(x, y, c=z, s=200)
fig.colorbar(scat)
ax.margins(0.05)
That we want to put into a regular 10x10 grid:
We can actually use/abuse np.histogram2d for this. Instead of counts, we'll have it add the value of each point that falls into a cell. It's easiest to do this through specifying weights=z, normed=False.
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
x, y, z = np.random.random((3, 10))
# Bin the data onto a 10x10 grid
# Have to reverse x & y due to row-first indexing
zi, yi, xi = np.histogram2d(y, x, bins=(10,10), weights=z, normed=False)
zi = np.ma.masked_equal(zi, 0)
fig, ax = plt.subplots()
ax.pcolormesh(xi, yi, zi, edgecolors='black')
scat = ax.scatter(x, y, c=z, s=200)
fig.colorbar(scat)
ax.margins(0.05)
plt.show()
However, if we have a large number of points, some bins will have more than one point. The weights argument to np.histogram simply adds the values. That's probably not what you want in this case. Nonetheless, we can get the mean of the points that fall in each cell by dividing by the counts.
So, for example, let's say we have 50 points:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
x, y, z = np.random.random((3, 50))
# Bin the data onto a 10x10 grid
# Have to reverse x & y due to row-first indexing
zi, yi, xi = np.histogram2d(y, x, bins=(10,10), weights=z, normed=False)
counts, _, _ = np.histogram2d(y, x, bins=(10,10))
zi = zi / counts
zi = np.ma.masked_invalid(zi)
fig, ax = plt.subplots()
ax.pcolormesh(xi, yi, zi, edgecolors='black')
scat = ax.scatter(x, y, c=z, s=200)
fig.colorbar(scat)
ax.margins(0.05)
plt.show()
With very large numbers of points, this exact method will become slow (and can be sped up easily), but it's sufficient for anything less than ~1e6 points.
Kezzos beat me to it but I had a similar approach,
x = np.array([0,0,1,1,2,2])
y = np.array([1,2,0,1,1,2])
z = np.array([14,17,15,16,18,13])
Z = np.zeros((3,3))
for i,j in enumerate(zip(x,y)):
Z[j] = z[i]
Z[np.where(Z==0)] = np.nan
You could try something like:
import numpy as np
x = [0, 0, 1, 1, 2, 2]
y = [1, 2, 0, 1, 1, 2]
z = [14, 17, 15, 16, 18, 13]
arr = np.zeros((3,3))
yx = zip(y,x)
for i, coord in enumerate(yx):
arr[coord] = z[i]
print arr
>>> [[ 0. 15. 0.]
[ 14. 16. 18.]
[ 17. 0. 13.]]
If you have scipy installed, you could take advantage of its sparse matrix module. Get the values from the text file with genfromtxt, and plug those 'columns' directly into a sparse matrix creator.
In [545]: txt=b"""x y z
0 1 14
0 2 17
1 0 15
1 1 16
2 1 18
2 2 13
"""
In [546]: xyz=np.genfromtxt(txt.splitlines(),names=True,dtype=int)
In [547]: sparse.coo_matrix((xyz['z'],(xyz['y'],xyz['x']))).A
Out[547]:
array([[ 0, 15, 0],
[14, 16, 18],
[17, 0, 13]])
But Joe's z_array=np.zeros((3,3),int); z_array[xyz['y'],xyz['x']]=xyz['z'] is considerably faster.
Nice answers by others. Thought this might be a useful snippet for someone else who might need this.
def make_grid(x, y, z):
'''
Takes x, y, z values as lists and returns a 2D numpy array
'''
dx = abs(np.sort(list(set(x)))[1] - np.sort(list(set(x)))[0])
dy = abs(np.sort(list(set(y)))[1] - np.sort(list(set(y)))[0])
i = ((x - min(x)) / dx).astype(int) # Longitudes
j = ((y - max(y)) / dy).astype(int) # Latitudes
grid = np.nan * np.empty((len(set(j)),len(set(i))))
grid[-j, i] = z # if using latitude and longitude (for WGS/West)
return grid

matplotlib pyplot: subplot size

If I plot a single graph as below, it will be of size (x * y).
import matplotlib.pyplot as plt
plt.plot([1, 2], [1, 2])
However, if I plot 3 sub-graphs in the same row, each of them will be of size ((x / 3) * y).
fig, ax = plt.subplots(1, 3, sharey = True)
for i in range(3):
ax[i].plot([1, 2], [1, 2])
How can I obtain these 3 subplots, each of which is of size (x * y)?
The figure object has a default size that does not know about the number of subplots. You can change the figure size when you make the figure to suit your needs though.
import matplotlib.pyplot as plt
nx = 3
ny = 1
dxs = 8.0
dys = 6.0
fig, ax = plt.subplots(ny, nx, sharey = True, figsize=(dxs*nx, dys*ny) )
for i in range(nx):
ax[i].plot([1, 2], [1, 2])

How to plot 3D surface with X, Y, Z when Z is a list of list in Python?

In my case, X is a range(0, 100), Y is a range(0, 10), Z is a list of list. Z has the same length as X, which is 100, and each element list inside of Z has the same dimension of Y.
Z = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]].
I have the following code, but it does not work, it complains two or more arrays have incompatible dimensions on axis 1.
fig = plt.figure(figsize=(200, 6))
ax = fig.add_subplot(1, 2, 1, projection='3d')
ax.set_xticklabels(x_ax)
ax.set_yticklabels(y_ax)
ax.set_title("my title of chart")
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
ax.set_zlim(0, 100)
fig.colorbar(surf, shrink = 0.5, aspect = 5)
plt.show()
I guess the error is due to the data structure of Z, how do I make a compatible structure with X, and Y? Thanks
Here is a basic 3D surface plotting procedure. It seems that your X and Y are just 1D arrays. However, X, Y, and Z have to be 2D arrays of the same shape. numpy.meshgrid function is useful for creating 2D mesh from two 1D arrays.
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.array(np.linspace(-2,2,100))
y = np.array(np.linspace(-2,2,10))
X,Y = np.meshgrid(x,y)
Z = X * np.exp(-X**2 - Y**2);
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
fig.colorbar(surf, shrink = 0.5, aspect = 5)
plt.show()

Categories