How can I plot non-rectangular 2D data using matplotlib? - python

I am working on a calculation in Python which calculates the physical properties of an object for angles in the range 0 < θ < π/2 and 0 < φ < π/2 (i.e. the first octant). To visualize the properties I am currently plotting them as color values on a 3D unit sphere. Producing this plot for reasonable resolution is a fairly resource intensive process, but I have no interest in being able to look at the plot from any other angle.
What I would like to create instead is a 2D image plot similar to what imshow would create except that it should have the triangular outline of a sphere octant being projected into 2D. Note that I am not asking how to project the 3D data into 2D, but rather how to display the 2D data in a manner which looks similar to a sphere octant viewed from θ = π/4, φ = π/4.
My current code is below. The specifics may not be that relevant to an answer, but it gives an idea of what I am trying to do.
'''
The code above this point produces three arrays stored in a dictionary
called phs with the three entries using the keys 'I', 'II', 'III. Each
array is a function of theta and phi where
theta = np.linspace( 0, 90, nPoints)
phi = np.linspace( 0, 90, nPoints)
also
types = ('I', 'II', 'III')
'''
# Colormaps
mx = np.maximum( np.maximum( phs['I'], phs['II']), phs['III'])
cmap = cm.ScalarMappable( cmap='BuPu')
cmap.set_array( mx)
clrs = dict()
for type in types:
clrs[type] = cmap.to_rgba( phs[type])
# Convert to Cartesian coordinates with unit radius
xM, yM, zM = plotCartesianFixedR( thetaM, phiM)
# Plot
fig = plt.figure( figsize=(16,7))
ax = dict()
ax['I'] = plt.subplot( 131, projection='3d')
ax['II'] = plt.subplot( 132, projection='3d')
ax['III'] = plt.subplot( 133, projection='3d')
surf = dict()
for type in types:
surf[type] = ax[type].plot_surface( xM, yM, zM, rstride=1, cstride=1,
facecolors=clrs[type], shade=False)
# Set axis properties
ax[type].set_xticklabels([])
ax[type].set_yticklabels([])
ax[type].set_zticklabels([])
ax[type].view_init(elev=45, azim=45)
# Colorbar
plt.colorbar( cmap, shrink=1)
ax['I'].set_title( 'Log$_{10}(|\Delta k|)$ Type I (ssf)')
ax['II'].set_title( 'Log$_{10}(|\Delta k|)$ Type II (sff)')
ax['III'].set_title( 'Log$_{10}(|\Delta k|)$ Type III (fsf)')
# Add title
if title:
plt.suptitle(title)
The output looks like:
Just to restate the problem; I would like to reproduce this plot almost exactly but in 2D without including the background axes.

Related

Plotting a parameter along the depth in 3D in Python

I have to visualize the length of cylinder in 3d along with some other parameters. I am able to plot a 3d cylinder in python using matplotlib and also define color for range of points. How I can draw a line through the middle of the cylinder and also plot other parameters like angles at that interval, varying radius etc.
The main idea is to have a well like structure and display some measurements along the depth of the cylinder.
Thanks in advance!
Code used to generate the cylinder:
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
u = np.linspace(0,2*np.pi) # divide circle into parts
#h = np.linspace(0,100,6) # divide height into parts
h = np.arange(3560.064,4213.86,step)
x = np.outer(np.sin(u), np.ones(len(h)))
y = np.outer(np.cos(u), np.ones(len(h)))
z = np.outer(np.ones(len(u)), h)
bounds = [3500,3650,4200,4214]
colors = ["blue", "red","green"]
cmap = matplotlib.colors.ListedColormap(colors)
norm = matplotlib.colors.BoundaryNorm(bounds, len(colors))
ax.plot_surface(x,y,z, cmap = cmap, norm = norm)
plt.show()
Generated Cylinder

Data points falling outside the meshgrid being interpolated over, while the meshgrid certainly covers those points

I am trying to interpolate sparse data over a meshgrid, but am observing some rather odd behavior. The white dots are precisely where I have values, and I am relying on the linear interpolation algorithm to fill in the other grids where possible. I recognize that this type of interpolation is not perfect due to the obvious lack of data, but how come some of the points where I have data fall outside the meshgrid that I am interpolating over? Is this a common phenomenon? This doesn't change even if I make the grid coarser.
I would appreciate some insight into why this happens, (perhaps how the linear interpolation works), or if there are any ways to fix this. See the red circles in the picture below for example:
Data points provided for interpolation falling outside the meshgrid that is interpolated over
The following is some code on the interpolation that generated the gridded data.
#mesh grid
xg = np.linspace(-130, -60, num=70)
yg = np.linspace(20,50,num=30)
Xg,Yg = np.meshgrid(xg,yg)
zg1 = griddata(points1, df2['tempratio'], (Xg, Yg), method = 'linear')
from mpl_toolkits.basemap import Basemap
lon_0 = xg.mean()
lat_0 = yg.mean()
m = Basemap(width=5000000, height=3500000,
resolution='l', projection='stere',\
lat_ts=40, lat_0=lat_0, lon_0=lon_0)
xm, ym = m(Xg, Yg)
cs = m.pcolormesh(xm,ym,zg1,shading='flat',cmap=plt.cm.Reds)
griddata assigns values to the vertices of a grid, so 70x30 points. pcolormesh doesn't color vertices, but the rectangles in-between. There are only 69x29 rectangles formed by the given vertices. So, one row and one column of zg1 will be dropped. To counter that, an extra row and extra column can be added to the coordinates and shifting everything half a rectangle in each direction.
It still doesn't force griddata to include all given points, but goes a step towards the desired outcome. A denser grid can also help. (Choosing 'nearest' instead of 'linear' interpolation would fill the complete grid.)
Here is some code to illustrate what's happening:
import numpy as np
from scipy.interpolate import griddata
from matplotlib import pyplot as plt
def extend_range(x):
dx = (x[1] - x[0]) / 2
return np.append( x - dx, x[-1] + dx)
N = 10
points1 = np.vstack([np.random.randint(-130, -60, N), np.random.randint(20, 50, N)]).T
tempratio = np.random.randint(0, 20, N)
xg = np.linspace(-130, -60, num=15)
yg = np.linspace(20, 50, num=10)
Xg, Yg = np.meshgrid(xg, yg)
zg1 = griddata(points1, tempratio, (Xg, Yg), method='linear')
fig, axs = plt.subplots(ncols=2, figsize=(12, 4))
for ax in axs:
ax.scatter(Xg, Yg, c=zg1, cmap='coolwarm', ec='g', s=80, zorder=2, label='griddata')
ax.scatter(points1[:,0], points1[:,1], c=tempratio, cmap='coolwarm', ec='black', s=150, zorder=3, label='given data')
if ax == axs[0]:
ax.pcolormesh(xg, yg, zg1, shading='flat', cmap='coolwarm')
ax.set_title('given x and y ranges')
else:
#todo: convert xg and yg to map coordinates
ax.pcolormesh(extend_range(xg), extend_range(yg), zg1, shading='flat', cmap='coolwarm')
ax.set_title('extended x and y ranges')
ax.legend()
plt.show()

Trouble Plotting Polar Graph

I am working on my homework for my Python course and one of our questions requires us to plot the Bessel function in Cartesian coordinates and then Polar coordinates.
I am having trouble with the latter. The problem also requires us to mask any values of radius greater than 20 and any values of theta between .4 and -.4.
This is where I'm having issues. I feel like I am setting up the mask correctly, but I can't get the graph to look like the Cartesian version. Plotting both with pcolormesh.
Picture of Code: Top is Cartesian / Bottom is Polar
In the polar I used u to define radius, j to define angle, and k equal to the Bessel function of radius 'u'. j0 represents the bessel function.
from scipy.special import j0, j1, jn
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
j = np.linspace(-np.pi, np.pi, 200)
u = np.linspace(0, 20, 200)
k = j0(u)
jmask = np.ma.masked_where((j<.4) & (j>-.4), j)
umask = np.ma.masked_greater(u, 20, copy=True)
U, J = np.meshgrid(umask, jmask)
plt.pcolormesh(J, U, j0(U))
Any help would be appreciated.

contour deformation in python

I have a contour map and I want to make a deformation of all the contour lines, where the contour of level 0.5 will be deformed around the blue point situated in his line and then pass on the blue point on the contour of level 1, and so on.
Original map :
Deformed map :
I think there are two steps, the first one is the delete some parts of the map and the second is to redraw the contour map.
I think i have to iterate through the contour map like this:
CS = plt.contour(X, Y, Z)
for level in CS.collections:
for kp, path in list(enumerate(level.get_paths())):
But I have no idea how to use kp and path
Any tips for doing this would be appreciated!
Here is an example on how you could change the contour plot to achieve the intended deformation.
It generates some data x,y,z which should later be modified. Then it specifies a deformation function, which when multiplied to z deforms the data in the desired way. This deformation function takes the x and y data as input as well as the angle of the line along which to perform the deformation and a width (spread) of the deformation. Finally a parameter i is used for stearing the degree of deformation (i.e. i=0 means no deformation). Of course you can use any other function to deform your contour.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
#### generate some x,y,z data ####
r = np.linspace(0,6, num=100)
phi = np.linspace(0, 2*np.pi, num=200)
R, Phi = np.meshgrid(r,phi)
x = R*np.cos(Phi)
y = R*np.sin(Phi)
z = R
##################################
fig, ax=plt.subplots()
ax.set_aspect("equal")
def update(i):
ax.clear()
f = lambda x,y, offs, width, i: 1-i*np.exp(-(np.arctan2(x,y)-offs)**2/width)
z_deformed = z*f(x,y, np.pi/4, 1./8., i=i)
ax.contour(x,y,z_deformed, 10, linewidths=4)
ax.contourf(x,y,z_deformed, 10, alpha=0.3)
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
update(0) #plot the original data
anipath = 0.5*np.sin(np.linspace(0, np.pi, num=20))**2
ani = matplotlib.animation.FuncAnimation(fig, update, frames=anipath, interval = 100)
plt.show()
Of course you can use other shapes of deformation. E.g. to get a triangular shape use
f = lambda x, A, a, b: A*(1.-np.abs((x-b)/a))*(np.abs((x-b)) < a )
z_deformed = z - f(np.arctan2(x,y), i, 1./8., np.pi/4 )

Matplotlib heatmap with changing y-values

I'm trying to plot some data for a measurement taken from between two surfaces. The z-direction in the system is defined as normal to the surfaces. The problem is that along the x-axis of my plot I'm varying the separation distance between the two surfaces which means that for every slice, the min/max of the y-axis change. I've sort circumvented this by presenting a normalized y-axis where z_min is the bottom surface and z_max is the top surface:
However, this representation somewhat distorts the data. Ideally I would like to show the actual distance to the wall on the y-axis and just leave the areas outside of the system bounds white. I (poorly) sketched what I'm envisioning here (the actual distribution on the heatmap should look different, of course):
I can pretty easily plot what I want as a 3D scatter plot like so:
But how do I get the data into a plot-able form for a heatmap?
I'm guessing I would have to blow up the MxN array and fill in missing values through interpolation or simply mark them as NAN? But then I'm also not quite sure how to add a hard cutoff to my color scheme to make everything outside of the system white.
You can do this with pcolormesh which takes the corners of quadrilaterals as the arguements
X, Y = np.meshgrid(np.linspace(0, 10, 100), np.linspace(0, 2*np.pi, 150),)
h = np.sin(Y)
Y *= np.linspace(.5, 1, 100)
fig, ax = plt.subplots(1, 1)
ax.pcolormesh(X, Y, h)
Below an implementation with triangular mesh contouring, based on CT Zhu example.
If your domain is not convex, you will need to provide your own triangles to the triangulation, as default Delaunay triangulation meshes the convex hull from your points.
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
y = np.array([np.linspace(-i, i, 51) for i in (
np.linspace(5, 10))[::-1]])
x = (np.zeros((50, 51)) +
np.linspace(1, 6, 50)[..., np.newaxis])
z = (np.zeros((50, 51)) -
np.linspace(-5, 5, 51)**2 + 10) # make up some z data
x = x.flatten()
y = y.flatten()
z = z.flatten()
print "x shape: ", x.shape
triang = mtri.Triangulation(x, y)
plt.tricontourf(triang, z)
plt.colorbar()
plt.show()
I guess, maybe 2d interpolation by using griddata will be what you want?
from matplotlib.mlab import griddata
xi=linspace(1,5,100)
yi=linspace(-10.5, 10.5, 100)
y=array([linspace(-i, i, 51) for i in (linspace(5,10))[::-1]]) #make up some y vectors with different range
x=zeros((50,51))+linspace(1,6, 50)[...,newaxis]
z=zeros((50,51))-linspace(-5, 5,51)**2+10 #make up some z data
x=x.flatten()
y=y.flatten()
z=z.flatten()
zi=griddata(x, y, z, xi, yi)
plt.contourf(xi, yi, zi, levels=-linspace(-5, 5,51)**2+10)

Categories

Resources