I need to create filled contour plots of sea surface temperature (SST) data within a polygon, however I am not sure the best way to do this. I have three 1D arrays containing data for X, Y, and SST which I plot using the following to create the attached plot:
p=PatchCollection(mypatches,color='none', alpha=1.0,edgecolor="purple",linewidth=2.0)
levels=np.arange(SST.min(),SST.max(),0.2)
datamap=mymap.scatter(x,y,c=SST, s=55, vmin=-2,vmax=3,alpha=1.0)
I would like to be able to plot these data as filled contours (contourf instead of scatter) that are constrained (clipped) within the polygon boundaries (the purple line). Suggestions for how to achieve this are greatly appreciated.
Update:
I had originally tried griddata, but could not get it to work properly. However, based on the answer provided by #eatHam I decided to try again. I could not get my scipy griddata to work as it kept hanging at the gridding when selecting method 'cubic', however when I switched to matplotlib.mlab.griddata and used 'linear' interpolation it worked. The suggestion for masking the boundaries provided a very coarse and not as exact solution as i would prefer.
I searched for options on how to clip contours in matplotlib and I found an answer by #pelson at this link. I tried the suggested solution implied in: "The contour set itself does not have a set_clip_path method but you can iterate over each of the contour collections and set their respective clip paths". My new and final solution looks like this (see plot below):
p=PatchCollection(mypatches,color='none', alpha=1.0,edgecolor="purple",linewidth=2.0)
levels=np.arange(SST.min(),SST.max(),0.2)
grid_x, grid_y = np.mgrid[x.min()-0.5*(x.min()):x.max()+0.5*(x.max()):200j,
y.min()-0.5*(y.min()):y.max()+0.5*(y.max()):200j]
grid_z = griddata(x,y,SST,grid_x,grid_y)
cs=mymap.contourf(grid_x, grid_y, grid_z)
for poly in mypatches:
for artist in ax.get_children():
artist.set_clip_path(poly)
ax.add_patch(poly)
mymap.drawcountries()
mymap.drawcoastlines()
mymap.fillcontinents(color='lightgrey',lake_color='none')
mymap.drawmapboundary(fill_color='none')
This solution could also be improved particularily in terms of extrapolating the extreme edges in the North. Suggestions for how to really 'fill-in' the full polygon are appreciated. I also would like to understand why mlab worked and scipy not.
I would interpolate the data using scipy.griddata. You can set the region outside of your area (mypatches) to np.nan. And then just use pyplot.contour to plot it.
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
def sst_data(x, y):
return x*(1-x)*np.cos(4*np.pi*x) * np.sin(4*np.pi*y**2)**2
#replace with ...
x = np.random.rand(1000) #... your x
y = np.random.rand(1000) #... your y
sst = sst_data(x, y) #... your sst
# interpolate to a grid
grid_x, grid_y = np.mgrid[0:1:100j, 0:1:200j]
grid_z = griddata((x,y), sst, (grid_x, grid_y), method='cubic')
# mask out the area outside of your region
nr, nc = grid_z.shape
grid_z[-nr//3:, -nc//3:] = np.nan
plt.contourf(grid_x, grid_y, grid_z)
plt.show()
EDIT: Changed variable name in the plt.contourf() call (was ..(grid_z, grid_y, grid_z))
Related
I have the data with (X,Y,Z) values. I tried to make a density plot with Z values for intensity. However the plot I get is not smooth and and has polytope i.e not completely filled.
The following is the code with the Data
but I want to obtain smooth and completely filled plot
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
import xlrd
location = "~/Desktop/Data.xlsx"
data = xlrd.open_workbook(location)
sheet = data.sheet_by_index(0)
sample=2000
x=np.array(sheet.col_values(0))[0:sample]
y=np.array(sheet.col_values(1))[0:sample]
z=np.hamming(9000)[0:sample]
print z
def plot_contour(x,y,z,resolution = 500,contour_method='cubic'):
resolution = str(resolution)+'j'
X,Y = np.mgrid[min(x):max(x):complex(resolution), min(y):max(y):complex(resolution)]
points = [[a,b] for a,b in zip(x,y)]
Z = griddata(points, z, (X, Y), method=contour_method)
return X,Y,Z
X,Y,Z = plot_contour(x,y,z,resolution = 500,contour_method='linear')
plt.style.context("seaborn-deep")
plt.contourf(X,Y,Z)
plt.colorbar()
plt.show()
This is the output:
This is what I want to achieve using contourplotf:
plt.contourf() is not the main problem here, it's just working with the data it has. The problem is the linear interpolation in scipy.interpolate.griddata().
I recommend not using griddata, but instead using one of the following methods:
scipy.interpolate.Rbf() — this is what you were using before (see my previous answer).
verde — an awesome gridding package.
sklearn.gaussian_process — or some other prediction model.
All of these methods will fill in the grid. If you plot the result with plt.imshow() you'll get the type of plot you show in your question — that is not a plt.contourf() plot.
Here's a demo notebook showing all of these approaches (including griddata).
I'm new to Python and having some trouble with matplotlib. I currently have data that is contained in two numpy arrays, call them x and y, that I am plotting on a scatter plot with coordinates for each point (x, y) (i.e I have points x[0], y[0] and x1, y1 and so on on my plot). I have been using the following code segment to color the points in my scatter plot based on the spatial density of nearby points (found this on another stackoverflow post):
http://prntscr.com/abqowk
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
x = np.random.normal(size=1000)
y = x*3 + np.random.normal(size=1000)
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)
idx = z.argsort()
fig,ax = plt.subplots()
ax.scatter(x,y,c=z,s=50,edgecolor='')
plt.show()
Output:
I've been using it without being sure exactly how it works (namely the point density calculation - if someone could explain how exactly that works, would also be much appreciated).
However, now I'd like to color code by the ratio of the spatial density of points in x,y to that of the spatial density of points in another set of numpy arrays, call them x2, y2. That is, I would like to make a plot such that I can identify how the density of points in x,y compares to the points in x2,y2 on the same scatter plot. Could someone please explain how I could go about doing this?
Thanks in advance for your help!
I've been trying to do the same thing based on that same earlier post, and I think I just figured it out! The trick is to use matplotlib.colors.Normalize() to define a scale and then weight it according to some data set (xnorm,ynorm):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mplc
import matplotlib.cm as cm
from scipy.stats import gaussian_kde
def kdeplot(x,y,xnorm,ynorm):
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)
wt = 1.0*len(x)/(len(xnorm)*1.0)
norm = mplc.Normalize(vmin=0, vmax=8/wt)
cmap = cm.gnuplot
idx = z.argsort()
x, y, z = x[idx], y[idx], z[idx]
args = (x,y)
kwargs = {'c':z,'s':10,'edgecolor':'','cmap':cmap,'norm':norm}
return args, kwargs
# (x1,y1) is some data set whose density map coloring you
# want to scale to (xnorm,ynorm)
args,kwargs = kdeplot(x1,y1,xnorm,ynorm)
plt.scatter(*args,**kwargs)
I used trial and error to optimize my normalization for my particular data and choice of colormap. Here's what my data looks like scaled to itself; here's my data scaled to some comparison data (which is on the bottom of that image).
I'm not sure this method is entirely general, but it works in my case: I know that my data and the comparison data are in similar regions of parameter space, and they both have gaussian scatter, so I can use a naive linear scaling determined by the number of data points and it results in something that gives the right idea visually.
This is my target to plot:
Several ellipses which are not regular shape.
(source: clouddn.com)
I was thinking about generating some random number as vertices location.
But it can only build a polygon. So, how to plot several arcs and make them close up?
To create any arbitrary shape, you will need to use the matplotlib.patches.Polygon class and just provide enough x,y samples to make it appear as smooth of a path as necessary (at the end of the day it's still straight line segments when you zoom in close enough).
If you only have a few points, you can use one of many interpolation methods (such as scipy.interpolate.spline) to create a smooth interpolant of the data that you can then feed to the Polygon constructor.
Here is a simple example creating a circle using the Polygon class by supplying x,y points around the circle.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
# Circle coordinates (100 points around the circle)
t = np.linspace(0, 2 * np.pi, 100).reshape(100,1)
coords = np.concatenate((np.cos(t), np.sin(t)), axis=1)
ax = plt.axes()
polygons = [];
polygons.append(Polygon(coords))
p = PatchCollection(polygons, alpha=0.4)
ax.add_collection(p)
ax.axis('equal')
Sonds just like the example in the official documentation:
http://matplotlib.org/examples/pylab_examples/ellipse_demo.html
the main part, they just construct a list of the ellipses:
ells = [Ellipse(xy=rnd.rand(2)*10, width=rnd.rand(), height=rnd.rand(), angle=rnd.rand()*360) for i in range(250)]
...or did I miss your point? :)
G'day, I'm struggling to find a way to create a rectangular mesh that best fits a smooth 3D surface. Particularly I have a model of an earthquake fault shown in this plot.
These are the depth contours to the fault. I want to find a rectangular mesh of defined dimension (say 10x10km) that best fits the surface. It doesn't have to (and it can't) be exactly on the surface, just the closest possible and it HAS to be a rectangle, not just a quadrangle. I have the nodes that define the surface and I can easily interpolate them.
Python solutions are welcome or suggestions on open-source code that my tackle this. I've tried commercial meshers (ABAQUS) but they always return quadrangles. I haven't been able to figure this out so any hints are appreciated.
If you have the nodes that define the surface, that means you have an irregular grid of coordinates and corresponding values. So you can generate a triangulation from this (most likely the tool you're using to show these filled contours uses the same behind the screens).
Matplotlib has two very useful classes that can convert a triangulation to a rectilinear grid (the more generic form of a rectangular grid): LinearTriInterpolator and CubicTriInterpolator. They are being used in this matplotlib example.
These are the basic steps from that same example, annotated by me, but credit goes to the matplotlib contributors:
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import numpy as np
# Create triangulation.
coords, earthquake_fault = get_coordinate_data() # to be filled in by you
x = coords['x']
y = coords['y']
triang = mtri.Triangulation(x, y)
# Interpolate to regularly-spaced quad grid.
z = earthquake_fault # the "height" data
xi, yi = np.meshgrid(np.linspace(x.min(), x.max() 20), np.linspace(y.min(), y.max(), 20))
interp_lin = mtri.LinearTriInterpolator(triang, z)
zi_lin = interp_lin(xi, yi)
# Plot the triangulation.
plt.subplot(121)
plt.tricontourf(triang, z)
plt.triplot(triang, 'ko-')
plt.title('Triangular grid')
# Plot linear interpolation to quad grid.
plt.subplot(122)
plt.contourf(xi, yi, zi_lin)
plt.title('Rectangular grid')
I want to use tripcolor from matplotlib.pyplot to view the colored contours of some of my data.
The data is extracted from an XY plane at z=cst using Paraview. I directly export the data in csv from Paraview which triangulates the plane for me.
The problem is that depending on the plane position (ie the mesh) tripcolor gives me sometimes good or bad results.
Here is a simple example code and results to illustrate it:
Code
import matplotlib.pyplot as plt
import numpy as np
p,u,v,w,x,y,z = np.loadtxt('./bad.csv',delimiter=',',skiprows=1,usecols=(0,1,2,3,4,5,6),unpack=True)
NbLevels = 256
plt.figure()
plt.gca().set_aspect('equal')
plt.tripcolor(x,y,w,NbLevels,cmap=plt.cm.hot_r,edgecolor='black')
cbar = plt.colorbar()
cbar.set_label('Velocity magnitude',labelpad=10)
plt.show()
Results with tripcolor
Here is the file that causes the problem.
I've heard that matplotlib's tripcolor is sometimes buggy, so is it a bug or not ?
As highlighted by #Hooked this is the normal behaviour for a Delaunay triangulation.
To remove unwanted triangles you should provide your own Triangulation by passing explicitly the triangles.
This is quite easy in your case as your data is almost structured: I suggest performing a Delaunay triangulation in the plane (r, theta) then passing these triangles to the initial (x, y) arrays. You can make use of the the built-in TriAnalyzer class to remove very flat triangles from the (r, theta) triangulation (they might exists due to round-off errors).
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as mtri
p,u,v,w,x,y,z = np.loadtxt('./bad.csv',delimiter=',',skiprows=1,usecols=(0,1,2,3,4,5,6),unpack=True)
r = np.sqrt(y**2 + x**2)
tan = (y / x)
aux_tri = mtri.Triangulation(r/np.max(r), tan/np.max(tan))
triang = mtri.Triangulation(x, y, aux_tri.triangles)
triang.set_mask(mtri.TriAnalyzer(aux_tri).get_flat_tri_mask())
NbLevels = 256
plt.figure()
plt.gca().set_aspect('equal')
plt.tripcolor(triang, w, NbLevels, cmap=plt.cm.jet, edgecolor='black')
cbar = plt.colorbar()
cbar.set_label('Velocity magnitude',labelpad=10)
plt.show()
It's probably because the Delaunay triangulation called by Paraview created a convex hull of the points (as it should). To test this, I used matplotlib.tri.Triangulation and plotted the resulting mesh from the x-y values:
import matplotlib.tri as tri
plt.scatter(x,y)
w[:] = 1
triang = tri.Triangulation(x, y)
plt.tripcolor(triang,w,alpha=.2)
which shows the same effect. It may be possible to remove the unwanted triangles from the mesh, either by hand, or using a non-convex boundary finder.