Plotting a 2D mesh grid with matplotlib - python

I would like to plot a 2D discretization rectangular mesh with non-regular
x y axes values, e.g. the typical discretization meshes used in CFD.
An example of the code may be:
fig = plt.figure(1,figsize=(12,8))
axes = fig.add_subplot(111)
matplotlib.rcParams.update({'font.size':17})
axes.set_xticks(self.xPoints)
axes.set_yticks(self.yPoints)
plt.grid(color='black', linestyle='-', linewidth=1)
myName = "2D.jpg"
fig.savefig(myName)
where self.xPoints and self.yPoints are 1D non-regular vectors.
This piece of code produce a good discretization mesh, the problem are the
xtics and ytics labels because they appear for all values of xPoints and yPoints (they overlap).
How can I easily redefine the printed values in the axes?
Let's say I only want to show the minimum and maximum value for x and y and not all values from the discretization mesh.
I cann't post a example-figure because it is the first time I ask something here (I can send it by mail if requested)

the problem is that you explicitly told matplotlib to label each point when you wrote:
axes.set_xticks(self.xPoints)
axes.set_yticks(self.yPoints)
comment out those lines and see what the result looks like.
Of course, if you only want the first and last point labelled, it becomes:
axes.set_xticks([self.xPoints[0], self.xPoints[-1]])
...

If the gridline was specified by axes.set_xticks(), I don't think it would be possible to show ticks without overlap in your case.
I may have a solution for you:
...
ax = plt.gca()
#Arr_y: y-direction data, 1D numpy array or list.
for j in range(len(Arr_y)):
plt.hline(y = Arr_y[j], xmin = Arr_x.min(), xmax = Arr_x.max(), color = 'black')
#Arr_x: x-direction data, 1D numpy array or list.
for i in range(len(Arr_x)):
plt.vline(x = Arr_x[i], ymin = Arr_y.min(), ymax = Arr_y.max(), color = 'black')
#Custom your ticks here, 1D numpy array or list.
ax.set_xticks(Arr_xticks)
ax.set_yticks(Arr_yticks)
plt.xlim(Arr_x.min(), Arr_x.max())
plt.ylim(Arr_y.min(), Arr_y.max())
plt.show()
...
hlines and vlines are horizontal and vertical lines, you can specify those lines with boundary data in both x and y directions.
I tried it with 60×182 non uniform mesh grid which cost me 1.2s, hope I can post a picture here.

Related

Matplotlib - How to assign an alpha value to each point on a 3D scatter plot? Radar Data Included

So I am trying to create a 3D scatter plot of radar data, where each point is assigned an alpha value based on the amplitude of the corresponding pixel.
I have done this looping through all x,y,z points and building the scatter plot point by point assigning the alpha values on each iteration. But once the scatter plot is created it is very slow and unable to manipulate the graph without considerable time spent waiting for the plot to update.
Points is a normalised (0 to 1) array.
Here is a link to my data
Data
File preparation:
def normalise0to1(data):
normalised = (data - np.min(data)) / (np.max(data) - np.min(data))
return normalised
Data = np.loadtxt('filepath.txt')
points2D = normalise0to1(Data) #Is (101,400) size
points3D = np.reshape(points2D,(101,20,20)) #Is (101,20,20) size
And the first attempt at creating the 3D scatter plot:
def Scatter_Plot1(points3D):
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
for x in range(0,points3D[1]):
for y in range(0,points3D[2]):
for z in range(0,points3D[0]):
val = points3D[z,x,y]
ax.scatter(x, y, z, alpha=val,c='black',s=1)
plt.show()
This takes a long time to run and is very slow once created.
In 2D, I can do something like this. Bear in mind this is the same array as the 3D 'points' array, but the (20x20) has been flattened to 400. I have provided an image of the flattened array, you can see how it creates an image where intensity is scaled to the alpha value.
def Scatter_Plot2(points2D):
fig = plt.figure()
ax = fig.add_subplot()
x_=np.linspace(0,points2D.shape[1]-1,points2D.shape[1])
y_=np.linspace(0,points2D.shape[0]-1,points2D.shape[0])
x,y = np.meshgrid(x_,y_)
ax.scatter(x,y,alpha=points2D.flatten(),s=1,c='black')
plt.show()
This image is the flattened version of the 3D plot I want to create, where instead of 400 length, it would be (20x20) and overall the 3D shape is (101,20,20).
The problem comes when trying to assign the alpha to a 3D plot, in 2D it seems happy enough when I provide a flattened array to the alpha parameter.
I would like something like this, but whether that's possible is another question..
def Scatter_Plot3(points3D):
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
x_=np.linspace(0,points3D.shape[2]-1,points3D.shape[2])
y_=np.linspace(0,points3D.shape[1]-1,points3D.shape[1])
z_=np.linspace(0,points3D.shape[0]-1,points3D.shape[0])
x,y,z = np.meshgrid(x_,y_,z_)
ax.scatter(x,y,z,alpha=points3D,s=1,c='black')
plt.show()
Results in this image, which seems a bit random in terms of how the alpha is assigned. You would expect to see the dark horizontal lines as in the first picture. What I want may not be possible, I'm open to using another package, perhaps pyqtgraph or mayavi etc. But matplotlib would be preferable.
Thank you!
Edit:
I have achieved something similar to what I would like, though not exactly. I have used the c and cmap inputs. This isn't ideal as I am dealing with a 3D cube and viewing the centre is still difficult but it has correctly mapped a variation to the data. But it doesn't work when I use the alpha parameter.
Notice the 2 main horizontal bands and the dark bit in the centre which is hard to see.
What I need is the same mapping but rather opacity than colour.
c = (points2D.T.flatten())
ax.scatter(x,y,z,s=1,c=c,cmap='viridis_r')

Matplotlib - Contourf - How to have a non-uniform ticks spacing?

I would like to plot contourf with (lat,depth,temp) and then have similar spacing as in the figure below (the temperature vary more near the surface then at depth, so I want to emphasized this region).
My depth array is not uniform (i.e. depth = [5,15,...,4975,5185,...]. I want to have such non-uniform vertical spacing.
I would like to show yticks = [10,100,500,1000,1500,2000,3000,4000,5000], and depth array does not have those exact values.
z = np.arange(0,50) # I want uniform spacing
pos = ([0,2,5,10,15,20,30,40,48]) # I want some yticks (not all of them)
ax=plt.contourf(lat,z,temp) # temp is a variable with dimensions (lat,depth)
plt.colorbar()
plt.gca().yaxis.set_ticks(pos) # Set some yticks, not all of them
plt.yticks(z[pos],depth[pos].astype(int)) # Replace the dummy values of z-array by something meaningful
plt.gca().invert_yaxis()
plt.grid(linestyle=':')
plt.gca().set(ylabel='depth (m)',xlabel='Latitude')'''
Potential Temperature of the Atlantic Ocean:
Per the matplotlib docs on yticks, you can specify the labels you want to use. In your case, if you want to show the labels [10,100,500,1000,1500,2000,3000,4000,5000] you can simply pass that list as the second argument in plt.yticks(), like so
plt.yticks(z[pos], [10,100,500,1000,1500,2000,3000,4000,5000])
and it will display the yticks accordingly. The issue arises in the specification of the positions - since the depth array does not have points corresponding exactly to the desired ytick values you will need to interpolate in order to find the exact position at which to place the labels. Unless the approximate positions specified in pos are already sufficient, in which case the above suffices.
If the depth data are not uniformly spaced then you can use numpy.interp to perform the interpolation, as shown below
import matplotlib.pyplot as plt
import numpy as np
# Create some depth data that is not uniformly spaced over [0, 5500]
depth = [(np.random.random() - 0.5)*25 + ii for ii in np.linspace(0, 5500, 50)]
lat = np.linspace(-75, 75, 50)
z = np.linspace(0,50, 50)
yticks = [10,100,500,1000,1500,2000,3000,4000,5000]
# Interpolate depths to get z-positions
pos = np.interp(yticks, depth, z)
temp = np.outer(lat, z) # Arbitrarily populate temp for demonstration
ax = plt.contourf(lat,z,temp)
plt.colorbar()
plt.gca().yaxis.set_ticks(pos)
plt.yticks(pos,yticks) # Place yticks at interpolated z-positions
plt.gca().invert_yaxis()
plt.grid(linestyle=':')
plt.gca().set(ylabel='Depth (m)',xlabel='Latitude')
plt.show()
This will find the exact positions where the yticks would fall if the depth array had data at those positions and place them accordingly as shown below.

Hatch area using pcolormesh in Basemap

I try to hatch only the regions where I have statistically significant results. How can I do this using Basemap and pcolormesh?
plt.figure(figsize=(12,12))
lons = iris_cube.coord('longitude').points
lats = iris_cube.coord('latitude').points
m = Basemap(llcrnrlon=lons[0], llcrnrlat=lats[0], urcrnrlon=lons[-1], urcrnrlat=lats[-1], resolution='l')
lon, lat = np.meshgrid(lons, lats)
plt.subplot(111)
cs = m.pcolormesh(lon, lat, significant_data, cmap=cmap, norm=norm, hatch='/')
It seems pcolormesh does not support hatching (see https://github.com/matplotlib/matplotlib/issues/3058). Instead, the advice is to use pcolor, which starting from this example would look like,
import matplotlib.pyplot as plt
import numpy as np
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)
z = z[:-1, :-1]
zm = np.ma.masked_less(z, 0.3)
cm = plt.pcolormesh(x, y, z)
plt.pcolor(x, y, zm, hatch='/', alpha=0.)
plt.colorbar(cm)
plt.show()
where a mask array is used to get the values of z greater than 0.3 and these are hatched using pcolor.
To avoid plotting another colour over the top (so you get only hatching) I've set alpha to 0. in pcolor which feels a bit like a hack. The alternative is to use patch and assign to the areas you want. See this example Python: Leave Numpy NaN values from matplotlib heatmap and its legend. This may be more tricky for basemaps, etc than just choosing areas with pcolor.
I have a simple solution for this problem, using only pcolormesh and not pcolor: Plot the color mesh, then hatch the entire plot, and then plot the original mesh again, this time by masking statistically significant cells, so that the only hatching visible is those on significant cells. Alternatively, you can put a marker on every cell (looks good too), instead of hatching the entire figure.
(I use cartopy instead of basemap, but this shouldn't matter.)
Step 1: Plot your field (z) normally, using pcolormesh.
mesh = plt.pcolormesh(x,y,z)
where x/y can be lons/lats.
Step 2: Hatch the entire plot. For this, use fill_between:
hatch = plt.fill_between([xmin,xmax],y1,y2,hatch='///////',color="none",edgecolor='black')
Check details of fill_between to set xmin, xmax, y1 and y2. You simply define two horizontal lines beyond the bounds of your plot, and hatch the area in between. Use more, or less /s to set hatch density.
To adjust hatch thickness, use below lines:
import matplotlib as mpl
mpl.rcParams['hatch.linewidth'] = 0.3
As an alternative to hatching everything, you can plot all your x-y points (or, lon-lat couples) as markers. A simple solution is putting a dot (x also looks good).
hatch = plt.plot(x,y,'.',color='black',markersize=1.5)
One of the above will be the basis of your 'hatch'. This is how it should look after Step 2:
Step 3: On top of these two, plot your color mesh once again with pcolormesh, this time masking cells containing statistically significant values. This way, the markers on your 'insignificant' cells become invisible again, while significant markers stay visible.
Assuming you have an identically sized array containing the t statistic for each cell (t_z), you can mask significant values using numpy's ma module.
z_masked = numpy.ma.masked_where(t_z >= your_threshold, z)
Then, plot the color mesh, using the masked array.
mesh_masked = plt.pcolormesh(x,y,z_masked)
Use zorder to make sure the layers are in correct order. This is how it should look after Step 3:

Plotting two y axes on the same scale on an implot (matplotlib)

I have an image plot, representing a matrix, with two axes. The y axis on th left of my image plot represents the rows and the x axis represents the column, while each grid cell represents the value as a function of x and y.
I'd like to plot my y-axis in another form on the right side of my image plot, which takes on much smaller values, but should still be in the same positions as the y-axis on the left, as the values are just different forms of one another. The problem is that when I use fig.twinx()and go to plot the y axis, it doesn't even show up! Does anyone know what's gong on? Thanks.
Current code:
# Setup the figure
fig5 = pyplot.figure(5, figsize=(10,9), facecolor='white')
pyplot.gcf().clear()
# plt.rc('xtick', labelsize=20)
# plt.rc('ytick', labelsize=20)
plt.rcParams.update({'font.size': 18})
fig5ax = pyplot.axes()
# Code to calculate extent based on min/max range and my x values
implot = pyplot.imshow(valgrid, extent=MyExtent , aspect='auto', vmin = myVmin, vmax = myVmax)
fig5ax.yaxis.set_major_formatter(plt.ticker.FixedFormatter([str(x) for x in ranges]))
fig5ax.yaxis.set_major_locator(plt.ticker.FixedLocator(ranges))
fig5Ax2 = fig5ax.twinx()
fig5Ax2.yaxis.set_major_formatter(plt.ticker.FixedFormatter([str(x) for x in time]))
# Setting locater the same as ranges, because I want them to line up
# as they are different forms of the same y value
fig5Ax2.yaxis.set_major_locator(plt.ticker.FixedLocator(ranges))
pyplot.show()
The answer was:
fig5Ax2.yaxis.set_view_interval(minRange, maxRange)

Python / Pyplot: Plotting 2D-Data at given (X,Y)

I have some data of the following type:
grid = np.array([posx, posy]) where posx and posy are the X/Y position some, stored in another array.
The (transposed) grid may look like:
grid = np.array([posx, posy])
print grid.T
[[ 2.47685286 2.51629155]
[ 2.47685286 8.51629155]
[ 2.47685286 14.51629155]
[ 8.47685286 5.51629155]
[ 8.47685286 11.51629155]
[ 14.47685286 2.51629155]
[ 14.47685286 8.51629155]
[ 14.47685286 14.51629155]]
Especially the y-Position is not identical in each "row" and the number of points differs, which I assume to be one of my problems.
Additionally, the corresponding data is stored in another (1D-)array like data = [2.3 4.7 -0.3 .....] having the same amount of entrys as I have points.
My aim is to plot this data in kind of a smooth heatmap displaying by colours indicating position of high / low values. So far I used:
import numpy as np
import matplotlib.pyplot as p
p.imshow(data, interpolation=None)
p.colorbar()
p.show()
Obviously my problem is that I need to adjust the positon of my points.
I searched some other posts but with this shape of data it never worked out.
Also I tried to adjust this by simply reshaping the data but this didn't work due to the irregular number of points
As I am new here I am also happy for comments on how to improve my post (more input needed etc.)
Thanks in advance!
There are several solutions to this problem.
If what you want is simply to have the points shown as markers of some size, with colors depending on the values in the z array, then a scatterplot will do nicely. If the space between the points should also be colored however, you should use interpolation and contouring. Fortunately those things have also been implemented in matplotlib for irregularly spaced data (data on an "unstructured grid"), which is what you have as the points cannot be easily mapped to a regular grid (although in the small example you've given, there does seem to be a tendency for equal-sized triangles).
Here are 3 examples that illustrate the functions you might want to look further into: plt.scatter, plt.tripcolor and plt.tricontourf. I've made the dataset to play with a bit larger, so that you can get a feeling of the function that is represented by z.
x,y = (2*np.random.rand(50)-1 for _ in range(2))
z = np.exp(-x*x - y*y) - np.cos(x) # turtle-surface around origin
f, ax = plt.subplots(1,3, sharex=True, sharey=True, num=2, subplot_kw={'xlim': (-1,1), 'ylim': (-1, 1)})
ax[0].scatter(x,y, s=500*(z-z.min()), c=z, cmap='hot') # scatterplot with variable-sized markers and colors
ax[1].tripcolor(x, y, z, cmap='hot') # creates a tesselation and colors the formed triangles based on the values in the 3 nodes
ax[2].tricontourf(x, y, z, cmap='hot') # estimates the underlying surface
for indx in (1,2):
ax[indx].triplot(x,y, 'ko ') # add the locations of the points
for axes in ax: # turn off the needless clutter
axes.tick_params(axis='both', which='both', bottom='off', left='off', labelbottom='off', labelleft='off')
ax[0].set_title('scatter')
ax[1].set_title('tripcolor')
ax[2].set_title('tricontourf')
I think your problem could be solved by creating a regular 2d-matrix and then using scipy.interpolate to interpolate the data between your data points. Example can be found at: http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html#id1

Categories

Resources