Plotting contours over pcolormesh data - python

I have some 2D data that I am displaying using pcolormesh that I would like to display a few contours on top of. I create the gridded data using
import numpy as np
import matplotlib.pyplot as plt
def bin(x, y, nbins, weights=None):
hist, X, Y = np.histogram2d(x, y, bins=nbins, weights=weights)
x_grid, y_grid = np.meshgrid(X,Y)
return hist, x_grid, y_grid
data = ... # read from binary file
h,x_grid,y_grid = bin(data.x,data.y,512)
# do some calculations with h
h = masked_log(h) # "safe" log that replaces <0 elements by 0 in output
pcm = plt.pcolormesh(x_grid,y_grid,h,cmap='jet')
# Just pretend that the data are lying on the center of the grid
# points, rather than on the edges
cont = plt.contour(x_grid[0:-1,0:-1],y_grid[0:-1,0:-1],h,4,colors='k',origin='lower')
When I plot only the output of pcolormesh, everything looks great. Adding the contours makes a giant mess.
I have read through the contour demo, the API examples, the pcolormesh levels example, and this closely-related SO post (my data is already gridded, so the solution doesn't help). But nothing I have tried thus far has created 4 simple contour lines atop my pcolormesh data.

I've put together minimal example with Gaussian filter (and scipy) which I think looks like it may do what you want. First, set up some dummy data (a Gaussian) and add noise,
import matplotlib
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z += 0.1*np.random.random(Z.shape)
and try to pcolormesh/contour,
plt.figure()
CS = plt.pcolormesh(X, Y, Z)
plt.contour(X, Y, Z, 4, colors='k')
plt.colorbar(CS)
plt.show()
which looks like this,
If we add filtering as follows,
import matplotlib
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from scipy.ndimage.filters import gaussian_filter
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z += 0.1*np.random.random(Z.shape)
plt.figure()
plt.pcolormesh(X, Y, Z)
CS = plt.contour(X, Y, gaussian_filter(Z, 5.), 4, colors='k',interpolation='none')
plt.colorbar()
plt.show()
it looks much better,

Related

How to use own CSV file in matplotlib

I am facing the problem with loading CSV data into matplotlib.
This is how my code looks like this:
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import pandas as pd
csv_filename = 'heatmap_data.csv'
df = pd.read_csv(csv_filename)
delta = 0.050
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2 - Z1 # difference of Gaussians
im = plt.imshow(Z, interpolation='bilinear', cmap=cm.bwr,
origin='lower', extent=[0, 100, 100, 0],
vmax=abs(Z).max(), vmin=-abs(Z).max())
cb = plt.colorbar()
plt.savefig("heatmap.png")
CSV Data which I want to load:
x_pos,y_pos,type,importance
74,64,blue,-0.011517617893368
68,64,blue,-0.0041303878348102
32,64,red,0.049788810065569
8,64,red,0.12877712212094
88,64,red,0.0050599724578342
84,64,blue,-0.00052412736663743
80,64,blue,-0.020183850819375
78,64,blue,-0.01297132988303
72,64,red,0.080092605800612
64,64,red,0.074683215098353
62,64,blue,-0.011168199648943
58,64,red,0.030129086612831
x_pos is X coordinate
y_pos is Y coordinate
importance is something like Z coordinate. It means importance of point.
Importance points > 0 should have red color with emphasis to importance.
Importance points < 0 should have blue color with emphasis to importance.
Every coordinate on Heatmap without any data should be white or transparent. Goals is Heatmap similar to this example sample heatmap. Is it possible with this library?

How to plot smooth line in python?

NCEP data is from this website.
I want to plot a picture like this:
Or this one(this one add trough lines):
My data is different from them, so the content is different.
But, the method should be the same.
I dont't know how to smooth the line. This is my result:
Here is my code:
import numpy as np
import scipy
import matplotlib.pyplot as plt
from netCDF4 import Dataset
from scipy.interpolate import Rbf
from scipy.ndimage import zoom
from mpl_toolkits.basemap import Basemap
m=Basemap(projection='cyl',llcrnrlat=20,urcrnrlat=50,llcrnrlon=90,urcrnrlon=130)
CHNshp = 'D:\python\shapefile\data\CHN_adm_shp\CHN_adm1'
m.readshapefile(CHNshp,'CHN',drawbounds = False)
TWNshp = 'D:\python\shapefile\data\TWN_adm_shp\TWN_adm0'
m.readshapefile(TWNshp,'TWN',drawbounds = False)
for info, shape in zip(m.CHN_info, m.CHN):
x, y = zip(*shape)
m.plot(x, y, marker=None,color='k',linewidth = 0.5)
for info, shape in zip(m.TWN_info, m.TWN):
x, y = zip(*shape)
m.plot(x, y, marker=None,color='k',linewidth = 0.5)
parallels = np.arange(-90.,91.,10.)
m.drawparallels(parallels,labels=[1,0,0,1],linewidth=0.5,xoffset=1.2)
meridians = np.arange(-180.,181.,10.)
m.drawmeridians(meridians,labels=[1,0,0,1],linewidth=0.5,yoffset=1.2)
u=Dataset(r'D:\python\TRY\ncep\uwnd.2016.nc','r')
v=Dataset(r'D:\python\TRY\ncep\vwnd.2016.nc','r')
hgt_data=Dataset(r'D:\python\TRY\ncep\hgt.2016.nc','r')
uwnd=u.variables['uwnd'][728][2][:]
vwnd=v.variables['vwnd'][728][2][:]
hgt=hgt_data.variables['hgt'][728][2][:]
lat=u.variables['lat'][:]
lon=u.variables['lon'][:]
index1=np.logical_and(lon>=90,lon<=130);index2=np.logical_and(lat>=20,lat<=50)
lons=lon[index1];lats=lat[index2]
u1=uwnd[index2,:];u2=u1[:,index1]
v1=vwnd[index2,:];v2=v1[:,index1]
hgt1=hgt[index2,:];hgt2=hgt1[:,index1]
nx,ny=np.meshgrid(lons,lats)
x,y=m(nx,ny)
Q = m.quiver(x,y,u2,v2,scale=250,width=0.003)
qk = plt.quiverkey(Q, 0.85, -0.12, 20, '20 m/s', labelpos='N')
rbf = scipy.interpolate.Rbf(x, y, hgt2)
zi = rbf(x, y)
plt.contour(x,y,zi,color='k')
plt.show()
Update:
lons = zoom(lons,3,order=3)
lats = zoom(lats,3,order=3)
x,y = np.meshgrid(lons,lats,copy=False)
hgt2 = zoom(hgt2,3,order=3)
cs = plt.contour(x,y,hgt2,levels=levels,colors='k',linewidths=0.7)
Have a look at the examples for the contour() function on the matplotlib site https://matplotlib.org/examples/pylab_examples/contour_demo.html
Heres how they generate the x and y coordinates for this plot:
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
What you need to do is increase the resolution for your lons and lats fields which you use in the meshgrid() function in your own program.
higher resolution -> smoother lines

matplotlib: manually (interactively) picked contour label adds extra lines

I am finding that manually picking contour labels in matplotlib adds extra lines to the plot. In addition, the contour label is rotated from the local tangent to the contour line. For example, the following code,
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
#Define surface
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
#Plot surface
plt.figure()
CS = plt.contour(X, Y, Z)
#Manually pick labels
CS.clabel(CS.levels, manual = True, inline = True)
with some mouse clicks on the contour lines, results in this plot:
Anyone know what I am doing wrong? Perhaps this is an axes transformation bug…
In case it matters, I am running matplotlib 1.3.0 and python 2.7.5
This is a known bug and there is a fix (PR #2843). This will be fixed in the 1.4 release.

Get coordinates from the contour in matplotlib?

Background
From the documentation example here, one can easily produce the following contour plot with the code snippet.
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
# Create a simple contour plot with labels using default colors. The
# inline argument to clabel will control whether the labels are draw
# over the line segments of the contour, removing the lines beneath
# the label
plt.figure()
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
My Goal
I have obtained my contour plot and meanwhile got the matplotlib.contour.QuadContourSet instance CS. In the example snippet, CS is only used for clabel(). However for my case, I need to obtain either the equation of the contour line or the coordinate set for further computation.
How can I extract the coordinates of the contour line from the instance CS? OR
How can I achieve it in other ways?
I bet there must be a way of doing so. Otherwise, the contour thing is only a "vase for visualization" then.
You can get the coordinates of the contours from the CS.allsegs list.
Try:
dat0= CS.allsegs[0][0]
plt.plot(dat0[:,0],dat0[:,1])
to plot the first (-1) contour level.

Scattered x,y,z via python's matplotlib.pyplot.contourf

Most pyplot examples out there use linear data, but what if data is scattered?
x = 3,7,9
y = 1,4,5
z = 20,3,7
better meshgrid for contourf
xi = np.linspace(min(x)-1, max(x)+1, 9)
yi = np.linspace(min(y)-1, max(y)+1, 9)
X, Y = np.meshgrid(xi, yi)
Now "z" data got to be interpolated onto the meshgrid.
numpy.interp does little help here, while both linear and nn interpolaton of
zi = matplotlib.mlab.griddata(x,y,z,xi,yi,interp="linear")
returns rather strange results
scipy.interpolate.griddata cubic from second answer below needs something else to return data rather than nils
With custom levels data expected be looking something like this
This is what happens:
Although contour requires grid data, we can caste scatter data to a grid and then using masked arrays mask out the blank regions. I simulate this in the code below, by creating a random array, then using this to mask a test dataset (shown at bottom). The bulk of the code is taken from this matplotlib demo page.
import matplotlib
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
from numpy.random import *
import numpy.ma as ma
J = random_sample(X.shape)
mask = J > 0.7
X = ma.masked_array(X, mask=mask)
Y = ma.masked_array(Y, mask=mask)
Z = ma.masked_array(Z, mask=mask)
plt.figure()
CS = plt.contour(X, Y, Z, 20)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
plt.savefig('cat.png')
plt.show()
countourf will only work with a grid of data. If you're data is scattered, then you'll need to create an interpolated grid matching your data, like this: (note you'll need scipy to perform the interpolation)
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
import numpy.ma as ma
from numpy.random import uniform, seed
# your data
x = [3,7,9]
y = [1,4,5]
z = [20,3,7]
# define grid.
xi = np.linspace(0,10,300)
yi = np.linspace(0,6,300)
# grid the data.
zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
# contour the gridded data, plotting dots at the randomly spaced data points.
CS = plt.contour(xi,yi,zi,15,linewidths=0.5,colors='k')
CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
plt.colorbar() # draw colorbar
# plot data points.
plt.scatter(x,y,marker='o',c='b',s=5)
plt.xlim(min(x),max(x))
plt.ylim(min(y),max(y))
plt.title('griddata test (%d points)' % len(x))
plt.show()
See here for the origin of that code.

Categories