Matplotlib contour plot with nonuniform, matrix grids - python

I would like to contour plot a function, f(x,y), against x and x-y. The spacing in the y grid is not the same as the x grid, so x-y is 2 dimensional, whereas x is one-dimensional.
I do not know how to set up the grids. The function, tricontourf, can handle non-uniform grids, but only it seems, if both the axes are one-dimensional. contour can handle matrices, but only for f(x,y), whereas I need one of the axes to be a matrix.
Pseudocode would look like the following:
import matplotlib.pyplot as plt
def twoDfunction(x,y):
return x + y # my function is more complicated than this
xaxis = np.linspace(0,10,100)
yaxis = np.linspace(0,10,22)
xminusyaxis = np.subtract(xaxis,yaxis)
functionsurfacevalues = twoDfunction(xaxis,yaxis)
fig =plt.figure(figsize=(10,10),dpi=300,facecolor='w')
ax1 = plt.subplot(111)
ax1.tricontourf(xaxis, xminusyaxis, functionsurfacevalues)
I would like the pseudocode to plot functionsurfacevalues versus x and xminusy.

What you need to do is create your grid using np.meshgrid() and then plot a contour or contourf plot.np.meshgrid will make irregular grids based on whatever you give it. You do not need a surface plot because your data isn't really a surface.
The main problem you are having is that because your x and y axis are different lengths, you can't subtract them. Otherwise the solution is easy and you can follow the following code.
import matplotlib.pyplot as plt
def twoDfunction(x,y):
return (x + y) # my function is more complicated than this
xaxis = np.linspace(0,10,100)
yaxis = np.linspace(0,5,100)
xminusyaxis = np.subtract(xaxis,yaxis)
xx,yy = np.meshgrid(xaxis,xminusyaxis)
fig =plt.figure(figsize=(10,10),dpi=300,facecolor='w')
ax1 = plt.subplot(111)
ax1.contourf(xx, yy, twoDfunction(xx,yy))
plt.show()

Related

Scipy 2D interpolation skips first row

I build a Jupyter Notebookthat imports geoelectric VES point data and subsequently interpolates the point data over a uniform 2D Mesh. I added the relevant parts of the code below (the previous part only imports all data into a dataframe).
x = df['Distance X [m]'].to_numpy()
y = df['AB/2 [m]'].to_numpy()
z = df['Resistivity [Ohmm]'].to_numpy()
#plot
cax = plt.scatter(x, y, c=z)
cbar = plt.colorbar(cax, fraction=0.03)
plt.title('Measured Resistivity')
#invert y axis
plt.gca().invert_yaxis()
plt.savefig('datapoints.png',dpi=100)
import numpy as np
from scipy.interpolate import griddata
from matplotlib.pyplot import figure
# target grid to interpolate to
xi = np.arange(0,6500,20)
yi = np.arange(0,500,20)
xi,yi = np.meshgrid(xi,yi)
# interpolate
zi = griddata((x,y),z,(xi,yi),method='cubic')
# plot
fig = plt.figure()
figure(figsize=(12, 6), dpi=80)
#ax = fig.add_subplot(111)
plt.contourf(xi,yi,zi)
plt.plot(x,y,'k.')
plt.xlabel('xi',fontsize=16)
plt.ylabel('yi',fontsize=16)
plt.gca().invert_yaxis()
plt.colorbar()
plt.savefig('interpolated.png',dpi=100)
#plt.close(fig)
So far, I managed to import my dataset, plot it and interpolate over the grid. However, especially at higher grid spacings, it becomes obvious that for some reason, the cubic and linear do not interpolation does not include the first row of the mesh (in my context the first meters of the subsurface) which is actually supposed to have the best data coverage. Only the nearest neighbor method works fine. In the added image e.g., the first 20m are not resolved.
Link to Interpolated Section

How do I remove overflow along the z-axis for a 3D matplotlib surface?

I'm trying to graph a 3d mesh surface with matplotlib and constrain the limits of the graph. The X and Y axes are correctly constrained, but there is overflow in the Z-Axis.
What am I missing? Here's my code:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10,10))
x = np.linspace(-6,6,100)
y = np.linspace(-6,6,100)
X,Y = np.meshgrid(x,y)
def f(x,y):
return x**2 + 3*y
Z = f(X,Y)
ax = plt.axes(projection = '3d')
ax.plot_surface(X,Y,Z,cmap='viridis')
ax.title.set_text("z=x**2+3y")
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_zlim3d(zmin=-3,zmax=5)
ax.set_xlim3d(xmin=-6,xmax=6)
ax.set_ylim3d(ymin=-6,ymax=6)
plt.show()
The graph:
Edit:
When I add clipping/min/max to the Z values, the graph is a little better, but it sets z values outside the bounds to the bounds themselves. Both of the following suggestions do this. Perhaps it's because I'm on a mac?
z_tmp = np.maximum(np.minimum(5,Z),-3)
z_temp = np.clip(Z, -3, 5, None)
Your data is outside the axis boundaries. Try rotate the view and you will notice.
z = x**2 + 3*y
If you want to only show a defined area of the data you could add a max() min() limitation on the Z data to exclude the data outside your wanted limitations.
Z = f(X,Y)
z_tmp = np.maximum(np.minimum(5,Z),-3)
ax = plt.axes(projection = '3d')
ax.plot_surface(X,Y,z_tmp,cmap='viridis')
I'm not sure the matplotlib behaves as it should in your default case.

Is there a way to plot a polar heatmap incrementally?

I am trying to have a polar heatmap appear incrementally. I want the plot to grow by adding a deltasector to the existing plot. The same maximal radius is always used.
For now I replot the old data as well, but that is only because I do not know how to add to the existing plot.
How do I add z values for the new angle to an existing heatmap?
The accepted answer here gives shows how to plot a polar heatmap:
Polar heatmaps in python
In the code below the z values are calculated as a function of the r and th. My situation is however that I read the values from a file instead.
How would I add them to the heatmap?
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import time
fig = plt.figure()
ax = Axes3D(fig)
angle = 0
rad = np.linspace(0, 5, 100)
d_angle = np.pi/100
while angle < np.pi:
azm = np.linspace(0, angle, 100)
r, th = np.meshgrid(rad, azm)
z = r/5
plt.subplot(projection="polar")
plt.pcolormesh(th, r, z)
plt.plot(azm, r, color='k', ls='none')
plt.grid()
plt.ion()
plt.show()
plt.pause(0.0001)
plt.clf()
angle += d_angle
I do not know where to start. Any pointers to docs? Or other advices?
You can retrieve the data from a plot by looking into ax.lines. Add a "gid" to your curve like so plt.plot(azm, r, color='k', ls='none', gid="a custom name") then we have a little work to do:
def append_data_to_curve(ax, gid):
for line in ax.lines: # Check every curve.
if line.get_gid() == "a custom name": # If the one you seek is found:
X = line.get_xdata() # Get its X and Y data.
Y = line.get_ydata()
X.append(x) # Add the new point (x,y) you want.
Y.append(y)
line.set_xdata(X) # Put back the modified list as curve data.
line.set_ydata(Y)
You can call this function for every iteration of a loop and add a single new point by giving it its (x,y) coordinates.

matplotlib - randomly pick N points from 2D array, and plot spatial scatter plot

I have plots like the following:
Left Plot : original 100 * 100 numpy data
Right Plot : What I want - randomly choose N data from the original data, and plot them on a surface plot
How can I randomly choose N number of data from the left plot, and plot the chosen data on a scatter plot like the right plot?
I used ax.imshow(data) to generate the surface plot on the left. data is a 2D numpy array.
If you want to colorize the randomly chosen points according to the image you can use the same colormap and normalization for the scatter as you have for the image.
import numpy as np
import matplotlib.pyplot as plt
original_data = np.random.rand(100,100)
fig, (ax, ax2) = plt.subplots(ncols=2)
im = ax.imshow(original_data, cmap="summer")
N = 89
x = np.random.randint(0,100,size=N)
y = np.random.randint(0,100,size=N)
random_sample = original_data[x,y]
sc = ax2.scatter(x,y,c=random_sample, cmap=im.cmap, norm=im.norm)
ax2.set_aspect("equal")
ax2.set(xlim=ax.get_xlim(), ylim=ax.get_ylim())
fig.colorbar(sc, ax=[ax,ax2], orientation="horizontal")
plt.show()
You just need to choose N numbers from 10,000 (100 x 100) unique points on the 2d plot. I assume you want without replacement. Then you can "unravel" them onto your x,y coordinate.
random_choices = np.random.choice(10000, size=N, replace=False)
x, y = np.unravel_index(random_choices, (100, 100))
You can use these indices to create your scatter plot and size points appropriately:
data = np.random.random((100, 100))
plt.scatter(x, y, s=data[y, x])

how can I plot on the axes

I actually want to recreate an image like the following:
Specially the little X on the xaxes
I have a
list = [[100,-3],[200,None],[120,-2] ... ]
and I do
for x in list:
if x[1]!=None:
plot(x[0],x[1],'ok')
else:
### PLot on the axes ###
But while I am plotting I do not know what the axes are. I know that some values are None, for example ( 250,None), So I want to plot on the xaxes at x = 250, but I have not idea what eventually the min(ylim()) will be.
I know I can do plot(250,-5,'X',zorder=999999) but this is only when I know what the min axes is.. (I can not do min, max and so to know the min axes. as the real data is a list inside a list inside a dictionary etc.. )
So the trick is to use a custom transformation. The regular data transformation for the x axis and the axes transformation for the y axis. Matplotlib calls that a blended transformation, which you need to create yourself. You'll find more information in this awesome guide.
And as #ThePredator already pointed out, you have to set clip_on=False, otherwise your markers will be clipped.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
fig, ax = plt.subplots()
# the x coords of this transformation are data, and the
# y coord are axes
trans = transforms.blended_transform_factory( ax.transData, ax.transAxes)
# data points on the axes
x = np.random.rand(5)*100. + 200.
y = [0]*5
ax.plot(x, y, 'kx', transform=trans, markersize=10, markeredgewidth=2,
clip_on=False)
# regular data
x = np.random.rand(5)*100. + 200.
y = np.random.rand(5)*100. + 200.
ax.plot(x, y, 'ro')
plt.show()
Result:
You can use the clip_on = False option. Example:
In your case, you can set your y limits.
Example:
x = [0,1,2,3,4,5]
y = [0,0,0,0,0,0]
plt.plot(x,y,'x',markersize=20,clip_on=False,zorder=100)
plt.ylim(0,1)
plt.show()
You can use get_ylim() in order to get the position of the axis and then plot on it.

Categories