Andrew Dawson created a neat python module called
gridfill. gridfill
wraps a
FORTRAN90 program that fills missing values in a grid by solving Poisson's
equation using an iterative relaxation scheme. It is very similar o NCL's
poisson_grid_fill.
Here is an example on how to use module.
import numpy.ma as ma
from scipy.io import loadmat
data = loadmat('./data/mercator_temperature.mat', squeeze_me=True)
x, y = data['x'], data['y']
bbox = [x.min(), x.max(), y.min(), y.max()]
temp = ma.masked_invalid(data['temp'][0, ...])
The data I loaded above are from an online example about Optimum Interpolation.
The next cell contains a simple function to create a map for the figures.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
LAND = cfeature.NaturalEarthFeature('physical', 'land', '50m',
edgecolor='face',
facecolor=cfeature.COLORS['land'])
def make_map(bbox, projection=ccrs.PlateCarree()):
fig, ax = plt.subplots(figsize=(8, 6),
subplot_kw=dict(projection=projection))
ax.set_extent(bbox)
ax.add_feature(LAND, facecolor='0.75')
ax.coastlines(resolution='50m')
gl = ax.gridlines(draw_labels=True)
gl.xlabels_top = gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
return fig, ax
Here is the original data,
fig, ax = make_map(bbox=bbox)
cs = ax.pcolormesh(x, y, temp)
cbar = fig.colorbar(cs, extend='both', shrink=0.85)
There is nothing wrong with the data, but let's pretend it has several gaps due to clouds or sensor errors. (The functions below will help to create the gaps in the data.)
import numpy as np
def random_mask(data, frac=0.05):
N = data.size
frac = np.int(frac * N)
# FIXME: Must excluded the land mask, so frac has a real meaning!
idx = np.random.random_integers(0, N-1, frac)
return idx
def mask_data(data, idx):
out = data.copy()
i, j = np.unravel_index(idx, data.shape)
out[i, j] = np.NaN
return ma.masked_invalid(out)
This is how our "incomplete" data look like.
idx = random_mask(temp, frac=0.05)
t05 = mask_data(temp, idx)
fig, ax = make_map(bbox)
ax.set_title('Removed {:0.2f} % of data.'.format(100 * idx.size / temp.size))
cs = ax.pcolormesh(x, y, t05)
cbar = fig.colorbar(cs, extend='both', shrink=0.85)
Now we can use gridfill
to recover something that, hopefully, will look like
the original data.
The arguments for fill
are the gridded data, and the xdim
and ydim
dimensions that correspond to the x-coordinate and y-coordinate.
The keyword arguments are:
eps
: tolerance for the relaxationrelax
: relaxation constantitermax
: maximum number of iterationscyclic
: True if thed ata is cyclic
from gridfill import fill
kw = dict(eps=1e-4, relax=0.6, itermax=1e4, initzonal=False,
cyclic=False, verbose=True)
filled, converged = fill(t05, 1, 0, **kw)
if converged:
fig, ax = make_map(bbox)
cs = ax.pcolormesh(x, y, filled)
cbar = fig.colorbar(cs, extend='both', shrink=0.85)
Easy as pie! I used gridfill
in some real applications with good results,
like filling gaps in HF radar currents and to creating gridded CTD data for
scientific cruises.
I am trying to expand gridfill
so it does not fill over a certain mask, like
continents or a bathymetric line. As soon as I have something that works I
will create another post.
I almost forgot! gridfill
has a fill_cube
alternative that operates on
iris cubes.
HTML(html)