I would like to make a 2D contour plot given 3 numpy arrays of same size
X Y Z with (X,Y) the coordinates and Z the value at (X,Y).
Now, the function plt.contour seems to work only with a grid, and values at the intersection of the grid, and takes as input a rectangular grid only with 2D matrices as input.
see http://matplotlib.org/examples/pylab_examples/contour_demo.html
If the input for that function is (XX,YY,ZZ) then XX is a 2D rectangular array where every line is the same and every column is constant, while YY is a 2D rectangular array where every column is the same, and every line is constant.
Now, what function should I use, or what procedure should I follow, if I want to make the contour plot when the input values are NOT values taken on a grid?
thanks
Try to use plt.tricontour: http://matplotlib.org/api/pyplot_api.html?highlight=tricontour#matplotlib.pyplot.tricontour
It draws contours on an unstructured triangular grid.
Little example:
import numpy as np
import matplotlib.pyplot as plt
x = np.random.rand(100)
y = np.random.rand(100)
z = x**2+np.sin(y)*y
f, ax = plt.subplots(1,1)
ax.tricontour(x,y,z)
ax.plot(x,y, 'r. ')
plt.show()
Related
I have a 2D output matrix (say, Z) which was calculated as a function of two variables x,y.
x varies in a non-uniform manner like [1e-5,5e-5,1e-4,5e-4,1e-3,5e-3,1e-2]
y varies in a uniform manner like [300,400,500,600,700,800]
[ say, Z = np.random.rand(7,6) ]
I was trying to plot a colormap of the matrix Z by first creating a meshgrid for x,y and then using the pcolormesh. Since, my x values are non-uniform, I do not kn ow how to proceed. Any inputs would be greatly appreciated.
No need for meshgrids; regarding the non-uniform axes: In your case a log-scale works fine:
import numpy as np
from matplotlib import pyplot as plt
x = [1e-5,5e-5,1e-4,5e-4,1e-3,5e-3,1e-2]
y = [300,400,500,600,700,800]
# either enlarge x and y by one number (right-most
# endpoint for those bins), or make Z smaller as I did
Z = np.random.rand(6,5)
fig = plt.figure()
ax = fig.gca()
ax.pcolormesh(x,y,Z.T)
ax.set_xscale("log")
fig.show()
I'm trying to visualise a dataset in 3D which consists of a time series (along y) of x-z data, using Python and Matplotlib.
I'd like to create a plot like the one below (which was made in Python: http://austringer.net/wp/index.php/2011/05/20/plotting-a-dolphin-biosonar-click-train/), but where the colour varies with Z - i.e. so the intensity is shown by a colormap as well as the peak height, for clarity.
An example showing the colormap in Z is (apparently made using MATLAB):
This effect can be created using the waterfall plot option in MATLAB, but I understand there is no direct equivalent of this in Python.
I have also tried using the plot_surface option in Python (below), which works ok, but I'd like to 'force' the lines running over the surface to only be in the x direction (i.e. making it look more like a stacked time series than a surface). Is this possible?
Any help or advice greatly welcomed. Thanks.
I have generated a function that replicates the matlab waterfall behaviour in matplotlib, but I don't think it is the best solution when it comes to performance.
I started from two examples in matplotlib documentation: multicolor lines and multiple lines in 3d plot. From these examples, I only saw possible to draw lines whose color varies following a given colormap according to its z value following the example, which is reshaping the input array to draw the line by segments of 2 points and setting the color of the segment to the z mean value between the 2 points.
Thus, given the input matrixes n,m matrixes X,Y and Z, the function loops over the smallest dimension between n,m to plot each line like in the example, by 2 points segments, where the reshaping to plot by segments is done reshaping the array with the same code as the example.
def waterfall_plot(fig,ax,X,Y,Z):
'''
Make a waterfall plot
Input:
fig,ax : matplotlib figure and axes to populate
Z : n,m numpy array. Must be a 2d array even if only one line should be plotted
X,Y : n,m array
'''
# Set normalization to the same values for all plots
norm = plt.Normalize(Z.min().min(), Z.max().max())
# Check sizes to loop always over the smallest dimension
n,m = Z.shape
if n>m:
X=X.T; Y=Y.T; Z=Z.T
m,n = n,m
for j in range(n):
# reshape the X,Z into pairs
points = np.array([X[j,:], Z[j,:]]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, cmap='plasma', norm=norm)
# Set the values used for colormapping
lc.set_array((Z[j,1:]+Z[j,:-1])/2)
lc.set_linewidth(2) # set linewidth a little larger to see properly the colormap variation
line = ax.add_collection3d(lc,zs=(Y[j,1:]+Y[j,:-1])/2, zdir='y') # add line to axes
fig.colorbar(lc) # add colorbar, as the normalization is the same for all, it doesent matter which of the lc objects we use
Therefore, plots looking like matlab waterfall can be easily generated with the same input matrixes as a matplotlib surface plot:
import numpy as np; import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from mpl_toolkits.mplot3d import Axes3D
# Generate data
x = np.linspace(-2,2, 500)
y = np.linspace(-2,2, 40)
X,Y = np.meshgrid(x,y)
Z = np.sin(X**2+Y**2)
# Generate waterfall plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
waterfall_plot(fig,ax,X,Y,Z)
ax.set_xlabel('X') ; ax.set_xlim3d(-2,2)
ax.set_ylabel('Y') ; ax.set_ylim3d(-2,2)
ax.set_zlabel('Z') ; ax.set_zlim3d(-1,1)
The function assumes that when generating the meshgrid, the x array is the longest, and by default the lines have fixed y, and its the x coordinate what varies. However, if the size of the y dimension is larger, the matrixes are transposed, generating the lines with fixed x. Thus, generating the meshgrid with the sizes inverted (len(x)=40 and len(y)=500) yields:
with a pandas dataframe with the x axis as the first column and each spectra as another column
offset=0
for c in s.columns[1:]:
plt.plot(s.wavelength,s[c]+offset)
offset+=.25
plt.xlim([1325,1375])
I have a 2 dimensional Numpy NDarray filled with floats between 0 and about 8. This 2 dimensional arrays size is (1000, 1600) and there are about 1400 values, (the points in the point cloud), the remaining values are None, so matplotlib does not plot these values. You can see the plotted table in the image below. What I'd like to have is, the None-values interpolated with the values next to it to have a gradientlike heatmap. This pointcloud represents the shape of a roof and I want to process this data to an image I can give into a neural network to detect the type of roof.
The code I used for this plot is pretty short,
import matplotlib.pyplot as plt
plt.clf()
#plotGrid is the numpy.ndarray with shape (1000, 1600) and dtype float
plt.imshow(plotGrid, cmap='gray', interpolation='nearest')
plt.colorbar()
plt.show()
Image (click to enlarge and see points):
tricontourf
You might use a tricontour / tricontourf plot of the valid values. To this end, you first need to filter out all nan values (you should indeed make the invalid values np.nan instead of None).
Those values, together with their coordinates can be put into plt.tricontourf() to obtain a contour plot without the need of manual interpolation.
import matplotlib.pyplot as plt
import numpy as np
# Generate some example data
f = lambda x,y : np.exp((-(x-150)**2-(y-150)**2)/3.e3)
plotGrid = np.zeros((300,300))*np.nan
coo = np.random.randint(5,295, size=(150,2) )
for x,y in coo:
plotGrid[y,x] = f(x,y)
#plotGrid is now a numpy.ndarray with shape (300,300), mostly np.nan, and dtype float
# filter out nan values and get coordinates.
x,y = np.indices(plotGrid.shape)
x,y,z = x[~np.isnan(plotGrid)], y[~np.isnan(plotGrid)], plotGrid[~np.isnan(plotGrid)]
plt.tricontourf(x,y,z)
plt.colorbar()
plt.show()
tripcolor
Using tripcolor is another option then:
plt.tripcolor(x,y,z, shading='gouraud')
interpolate and contourf
You can also interpolate the data on a grid first, using matplotlib.mlab.griddata, and then either use a normal contourf plot,
xi = np.linspace(0, plotGrid.shape[1], plotGrid.shape[1])
yi = np.linspace(0, plotGrid.shape[0], plotGrid.shape[0])
zi = mlab.griddata(x, y, z, xi, yi, interp='linear')
plt.contourf(xi, yi, zi, 15)
interpolate and imshow
Or in the same manner use an imshow plot,
plt.imshow(zi)
I think scipy.interpolate.interp2d does what you need:
import scipy.interpolate
z_all = plotGrid.astype(float) # convert nones to nan
x_all, y_all = np.indices(plotGrid.shape) # get x and y coordinates
# convert to 1d arrays of coordinates
valid = ~np.isnan(z_all)
x, y, z = x_all[valid], y_all[valid], z_all[valid]
# interpolate
interp = scipy.interpolate.interp2d(x, y, z)
filled_data = interp(x_all[:,0], y_all[0,:]) # this is kinda gross, but `interp` doesn't
# do normal broadcasting
plt.imshow(filled_data)
I know that I can do a 4D plot in matplotlib with the following code, with the fourth dimension shown as a colormap:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
fig = plt.figure()
ax = fig.add_subplot(111,projection= '3d' )
x = np.arange(100)/ 101
y = np.sin(x) + np.cos(x)
X,Y = np.meshgrid(x,y)
Z = (X**2) / (Y**2)
A = np.sin(Z)
ax.plot_surface(X,Y, Z, facecolors=cm.Oranges(A))
plt.show()
But what if my data is not a function of the other data? How do I do this without np.meshgrid? (In other words, my Z series cannot be a function of the output of the X,Y which is the output of np.meshgrid(x,y), because Z is not a function of X and Y.)
A surface plot is a mapping of 2D points to a 1D value, i.e. for each pair of (x,y) coordinates you need exactly one z coordinate. So while it isn't strictly necessary to have Z being a function of X and Y, those arrays to plot need to have the same number of elements.
For a plot_surface the restriction is to have X and Y as gridded 2D data. Z does not have to be 2D but needs to have the same number of elements.
This requirement can be weakened using a plot_trisurf where the only requirement is that there is a strict mapping of x,y,z, i.e. the ith value in X and Y corresponds to the ith value in Z.
In any case, even if there is no analytic function to map X and Y to Z, Z still needs to be some kind of mapping. Otherwise it is even questionable what information the resulting plot would convey.
I have two arrays of data, x and y. I would like to plot on a scatter plot y vs. x. The range of x is [0,3] and the range of y is [-3, 3]. I then want to grid up this region into an n by m grid and color the points in each region based on the values of a separate 2D numpy array (same shape as the grid, n by m). So, the top-leftmost grid cell of my plot should be colored based on the value of colorarr[0][0] and so on. Anyone have any suggestions on how to do this? The closest I"ve found so far is the following:
2D grid data visualization in Python
Unfortunately this simply displays the colorarr, and not the 2D region I would like to visualize.
Thanks!
I think what you want is a 2 dimensional histogram. Matplotlib.pyplot makes this really easy.
import numpy as np
import matplotlib.pyplot as plt
# Make some points
npoints = 500
x = np.random.uniform(low=0, high=3, size=npoints)
y = np.random.uniform(low=-3, high=3, size=npoints)
# Make the plot
plt.hist2d(x, y)
plt.colorbar()
plt.show()
You can do it from just the color array by setting extent and aspect keywords of imshow
import matplotlib as plt
import numpy as np
zval = np.random.rand(100, 100)
plt.imshow(zvals, extent=[0,3,-3,3], aspect="auto")
plt.show()
What you get is the zval array just "crunched in" the [0:3, -3:3] range. Plot just the zval array in imshow to convince yourself of this.