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

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.

Related

How to convert a matrix to heatmap image in torch [duplicate]

Using Matplotlib, I want to plot a 2D heat map. My data is an n-by-n Numpy array, each with a value between 0 and 1. So for the (i, j) element of this array, I want to plot a square at the (i, j) coordinate in my heat map, whose color is proportional to the element's value in the array.
How can I do this?
The imshow() function with parameters interpolation='nearest' and cmap='hot' should do what you want.
Please review the interpolation parameter details, and see Interpolations for imshow and Image antialiasing.
import matplotlib.pyplot as plt
import numpy as np
a = np.random.random((16, 16))
plt.imshow(a, cmap='hot', interpolation='nearest')
plt.show()
Seaborn is a high-level API for matplotlib, which takes care of a lot of the manual work.
seaborn.heatmap automatically plots a gradient at the side of the chart etc.
import numpy as np
import seaborn as sns
import matplotlib.pylab as plt
uniform_data = np.random.rand(10, 12)
ax = sns.heatmap(uniform_data, linewidth=0.5)
plt.show()
You can even plot upper / lower left / right triangles of square matrices. For example, a correlation matrix, which is square and is symmetric, so plotting all values would be redundant.
corr = np.corrcoef(np.random.randn(10, 200))
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True, cmap="YlGnBu")
plt.show()
I would use matplotlib's pcolor/pcolormesh function since it allows nonuniform spacing of the data.
Example taken from matplotlib:
import matplotlib.pyplot as plt
import numpy as np
# generate 2 2d grids for the x & y bounds
y, x = np.meshgrid(np.linspace(-3, 3, 100), np.linspace(-3, 3, 100))
z = (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)
# x and y are bounds, so z should be the value *inside* those bounds.
# Therefore, remove the last value from the z array.
z = z[:-1, :-1]
z_min, z_max = -np.abs(z).max(), np.abs(z).max()
fig, ax = plt.subplots()
c = ax.pcolormesh(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max)
ax.set_title('pcolormesh')
# set the limits of the plot to the limits of the data
ax.axis([x.min(), x.max(), y.min(), y.max()])
fig.colorbar(c, ax=ax)
plt.show()
For a 2d numpy array, simply use imshow() may help you:
import matplotlib.pyplot as plt
import numpy as np
def heatmap2d(arr: np.ndarray):
plt.imshow(arr, cmap='viridis')
plt.colorbar()
plt.show()
test_array = np.arange(100 * 100).reshape(100, 100)
heatmap2d(test_array)
This code produces a continuous heatmap.
You can choose another built-in colormap from here.
Here's how to do it from a csv:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
# Load data from CSV
dat = np.genfromtxt('dat.xyz', delimiter=' ',skip_header=0)
X_dat = dat[:,0]
Y_dat = dat[:,1]
Z_dat = dat[:,2]
# Convert from pandas dataframes to numpy arrays
X, Y, Z, = np.array([]), np.array([]), np.array([])
for i in range(len(X_dat)):
X = np.append(X, X_dat[i])
Y = np.append(Y, Y_dat[i])
Z = np.append(Z, Z_dat[i])
# create x-y points to be used in heatmap
xi = np.linspace(X.min(), X.max(), 1000)
yi = np.linspace(Y.min(), Y.max(), 1000)
# Interpolate for plotting
zi = griddata((X, Y), Z, (xi[None,:], yi[:,None]), method='cubic')
# I control the range of my colorbar by removing data
# outside of my range of interest
zmin = 3
zmax = 12
zi[(zi<zmin) | (zi>zmax)] = None
# Create the contour plot
CS = plt.contourf(xi, yi, zi, 15, cmap=plt.cm.rainbow,
vmax=zmax, vmin=zmin)
plt.colorbar()
plt.show()
where dat.xyz is in the form
x1 y1 z1
x2 y2 z2
...
Use matshow() which is a wrapper around imshow to set useful defaults for displaying a matrix.
a = np.diag(range(15))
plt.matshow(a)
https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.matshow.html
This is just a convenience function wrapping imshow to set useful defaults for displaying a matrix. In particular:
Set origin='upper'.
Set interpolation='nearest'.
Set aspect='equal'.
Ticks are placed to the left and above.
Ticks are formatted to show integer indices.
Here is a new python package to plot complex heatmaps with different kinds of row/columns annotations in Python: https://github.com/DingWB/PyComplexHeatmap

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

Location of contour lines

Below is my code and plot:
import matplotlib
import matplotlib.mlab as mlab
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
delta = 0.00025
A=0
x = np.arange(0, 0.10, delta)
y = np.arange(0, 0.1, delta)
X, Y = np.meshgrid(x, y)
Z = A*(X**2+Y**2)+2*X*Y
manual_locations = [(0.1,0.1), (0.2,0.2), (0.3,0.3),
(0.015, 0.015), (0.00255, 0.0025), (0.00005,0.00005)]
line_widths = (1, 1, 1, 1, 1, 1)
plt.figure()
CS = plt.contour(X, Y, Z, 6, # add 6 contour lines
linewidths=line_widths, # line widths
colors = line_colours) # line colours
plt.clabel(CS, inline=1, # add labels
fontsize=10, # label font size
manual=manual_locations) # label locations
plt.title('Indifference Map') # title
plt.show()
It seems my manual_locations does nothing, python picks equally spaced contour lines automatically. While I want to investigate more details around 0. How can I see more curves/contour lines converging to (0,0)?
Thanks in advance.
The easiest way to explore parts of your data in more details is with levels. This sets what Z-values to examine, and in your question you phrase this as an (x,y) location to inspect, but it's a bit backwards from how contour works to specify the location points directly.
You could also do inspect the (0,0) region by changing the boundaries of the plot appropriately.
Below, I use log values for levels, but linear values work equally well, are far more common, and are easier to interpret. The log values just easily emphasis the part of the plot you're most interested in.
import matplotlib
import matplotlib.mlab as mlab
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
#%matplotlib inline
delta = 0.00025
A=0
x = np.arange(0, 0.10, delta)
y = np.arange(0, 0.1, delta)
X, Y = np.meshgrid(x, y)
Z = A*(X**2+Y**2)+2*X*Y
manual_locations = [(0.1,0.1), (0.2,0.2), (0.3,0.3),
(0.015, 0.015), (0.00255, 0.0025), (0.00005,0.00005)]
line_widths = (1, 1, 1, 1, 1, 1)
plt.figure()
CS = plt.contour(X, Y, Z, 6, # add 6 contour lines
linewidths=line_widths,
#levels=np.linspace(0, .003, 20))
levels=np.logspace(-5, -2, 20))
plt.clabel(CS, inline=1, # add labels
fontsize=10,
fmt="%.5f")
plt.title('Indifference Map') # title
plt.show()
If you really need the contour at a specific location, you could put the (x,y) values for that location into your equation to calculate the z-value at that location, and then use this value as one of the values in the levels argument.

Plotting contours over pcolormesh data

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,

Python & Matplotlib: How to create a meshgrid to plot surf?

I want to plot the a probability density function z=f(x,y).
I find the code to plot surf in Color matplotlib plot_surface command with surface gradient
But I don't know how to conver the z value into grid so I can plot it
The example code and my modification is below.
import numpy as np
import matplotlib.pyplot as plt
from sklearn import mixture
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
%matplotlib inline
n_samples = 1000
# generate random sample, two components
np.random.seed(0)
shifted_gaussian = np.random.randn(n_samples, 2) + np.array([20, 5])
sample = shifted_gaussian
# fit a Gaussian Mixture Model with two components
clf = mixture.GMM(n_components=3, covariance_type='full')
clf.fit(sample)
# Plot it
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, .25)
Y = np.arange(-5, 5, .25)
X, Y = np.meshgrid(X, Y)
## In example Code, the z is generate by grid
# R = np.sqrt(X**2 + Y**2)
# Z = np.sin(R)
# In my case,
# for each point [x,y], the probability value is
# z = clf.score([x,y])
# but How can I generate a grid Z?
Gx, Gy = np.gradient(Z) # gradients with respect to x and y
G = (Gx**2+Gy**2)**.5 # gradient magnitude
N = G/G.max() # normalize 0..1
surf = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1,
facecolors=cm.jet(N),
linewidth=0, antialiased=False, shade=False)
plt.show()
The original approach to plot z is to generate through mesh. But in my case, the fitted model cannot return result in grid-like style, so the problem is how can I generete the grid-style z value, and plot it?
If I understand correctly, you basically have a function z that takes a two scalar values x,y in a list and returns another scalar z_val. In other words z_val = z([x,y]), right?
If that's the case, the you could do the following (note that this is not written with efficiency in mind, but with focus on readability):
from itertools import product
X = np.arange(15) # or whatever values for x
Y = np.arange(5) # or whatever values for y
N, M = len(X), len(Y)
Z = np.zeros((N, M))
for i, (x,y) in enumerate(product(X,Y)):
Z[np.unravel_index(i, (N,M))] = z([x,y])
If you want to use plot_surface, then follow that with this:
X, Y = np.meshgrid(X, Y)
ax.plot_surface(X, Y, Z.T)

Categories