GLOEspy
A python implementation of Barry Madore's GLOEs algorithm. Use it to get a smooth interpolation of heterogeneously sampled 1- or 2-D data.
Size 9.9 kB - File type text/python-sourceFile contents
'''GLOESpy
Algorithm: Barry Madore
Python version: Chris Burns
Requires: Numeric,
fit_poly (http://www.ociw.edu/Code/python/burns-python-scripts/fit_poly.py)
A routine for generating a smooth 1D or 2D interpolation based on irregularly
sampled data. The basic idea is simple:
- You've got a bunch of data (x, y +/- dy)
- consider a point where you want to interpolat (x0)
- generate a normalized Gaussian window function W(x0,sigma) centered at
x0 with a certain width sigma.
- weight the data like W(x0,sigma)/dy
- Fit a polynomial at x0 and use that to interpolated the value y0.
- Repeat for each point x0.
For a 2D surface, you use an elliptical Gaussian window function and fit a
2D polynomial.'''
from Numeric import *
import sys
from fit_poly import fit2Dpoly
from fit_poly import fitpoly
def smooth(x, y, erry, xeval, sigma=None, N=10):
'''Given a set of x and y points, with associated internal errors in y given
by erry, find a smothing esimate of the data using Barry's GLOEs
algorithm evaluated at the points in xeval. The Gaussian window function
will have a sigma=sigma. '''
x,y,erry,xeval = map(asarray, [x,y,erry,xeval])
# dists[i,j] is the distance from point xeval[i] to data point x[j]
dists = x[NewAxis, :] - xeval[:, NewAxis]
if sigma is not None:
# Use the same weighting function everywhere
sigmas = xeval*0+sigma
else:
# Adapt the sigma to the sparcity of data
FWHMs = sort(absolute(dists))[:,N]
sigmas = FWHMs/1.665
# now weight these distances with a Gaussian
err_d = exp(-0.5*power(dists, 2)/power(sigmas[:,NewAxis],2))
# normalize
#norm = sum(err_d, axis=1)
norm = maximum.reduce(err_d, axis=1)
err_d = err_d/norm[:, NewAxis]
weight = err_d/erry[NewAxis,:]
# Lists of stuff we want to keep
interps = []
e_interps = []
bs = []
cs = []
for i in range(len(xeval)):
params,eparams = fitpoly(x, y, weight[i], x0=xeval[i], k=2)
interps.append(params[0])
e_interps.append(eparams[0])
bs.append(params[1])
cs.append(params[2])
# Convert the lists arrays
interps,e_interps,bs,cs = map(array, [interps,e_interps,bs,cs])
return(interps, e_interps, bs, cs)
def smooth2d(x, y, z, errz, xeval, yeval, sigmax=None, sigmay=None,
Nx=10, Ny=10, tol=0):
'''Given a set of x, y, z points, with associated internal errors in z given
by errz, find a smothing esimate of the data using Barry's GLOEs
algorithm evaluated at the points in xeval, yeval. The Gaussian window
function will have a sigma=sigmax in the x-direction and sigmay in the
y-direction.'''
x,y,z,errz,xeval,yeval = map(asarray, [x,y,z,errz,xeval,yeval])
# We re-scale the problem to 0 < x 1 and 0 < y < 1
x0 = min(x); x1 = max(x)
y0 = min(y); y1 = max(y)
# Rescaled data
u = (x - x0)/(x1 - x0)
v = (y - y0)/(y1 - y0)
# Rescaled evaluation points
si = (xeval - x0)/(x1 - x0)
sj = (yeval - y0)/(y1 - y0)
# rescaled sigmas:
if sigmax is not None:
if len(shape(sigmax)) == 0:
sigmax = sigmax + 0.0*si
sigmax = sigmax/(x1-x0)
if sigmay is not None:
if len(shape(sigmay)) == 0:
sigmay = sigmay + 0.0*sj
sigmay = sigmay/(y1-y0)
interps = []
e_interps = []
fxs = []
fys = []
fxxs = []
fyys = []
fxys = []
sigmaxs = []
sigmays = []
step = len(si)/79 + 1
for i in range(len(si)):
# [xy]dists[j] is the distance from point (si[i],sj[i]) to point u[j],v[j]
xdists = u - si[i]
ydists = v - sj[i]
sids = None
if sigmax is None:
# Find the Nx-th unique x-distance sorted by euclidean distance
dists2 = power(xdists,2) + power(ydists,2)
sids = argsort(dists2)
tempx = take(xdists,sids)
gotem = []
for dx in tempx:
if dx not in gotem:
gotem.append(dx)
if len(gotem) == Nx: break
FWHMx = maximum.reduce(absolute(gotem))
sigx = FWHMx/1.665
else:
sigx = sigmax[i]
if sigmay is None:
# Find the Nx-th unique x-distance sorted by euclidean distance
if sids is None:
dists2 = power(xdists,2) + power(ydists,2)
sids = argsort(dists2)
tempy = take(ydists,sids)
gotem = []
for dy in tempy:
if dy not in gotem:
gotem.append(dy)
if len(gotem) == Ny: break
FWHMy = maximum.reduce(absolute(gotem))
sigy = FWHMy/1.665
else:
sigy = sigmay[i]
sigmaxs.append(sigx)
sigmays.append(sigy)
dists2 = power(xdists,2)/sigx**2 + power(ydists,2)/sigy**2
err_d = exp(-0.5*dists2)
err_d = err_d/sum(err_d)
weight = err_d/errz
gids = greater(weight/maximum.reduce(weight), tol)
params,e_params = fit2Dpoly(compress(gids,u), compress(gids,v), compress(gids, z),
compress(gids, weight), k=2, x0=si[i], y0=sj[i])
interps.append(params[0])
e_interps.append(e_params[0])
fxs.append(params[1])
fxxs.append(params[2])
fys.append(params[3])
fxys.append(params[4])
fyys.append(params[5])
interps,e_interps,fxs,fys,fxxs,fyys,fxys,sigmaxs,sigmays = map(array,
[interps,e_interps,fxs,fys,fxxs,fyys,fxys,sigmaxs,sigmays])
return(interps,e_interps,fxs,fys,fxxs,fyys,fxys,sigmaxs,sigmays)
class line:
'''A class to represent a line of data. The construtor takes x,y,dy data
as arguments (and optionally the smoothing length). Member functions
allow the user to interpolate on the line.'''
def __init__(self, x,y,dy,sigma=None, N=None):
self.xdata,self.ydata,self.dydata = map(asarray, [x,y,dy])
if not (len(self.xdata) == len(self.ydata) == len(self.dydata)):
raise AttributeError, "x,y,dy must all have same length"
self.sigma = sigma
self.N = N
def chisq(self, sigma=None, N=None):
'''Compute chisq for a supplied sigma or one stored in the instance.'''
if sigma is None:
if self.sigma is None:
if N is None:
if self.N is None:
raise AttributeError, "Must specify a smoothing length"
else:
N = self.N
else:
sigma = self.sigma
res = self.eval(self.xdata, sigma=sigma, N=N)
rchisq = sum(power((res[0] - self.ydata)/self.dydata, 2))/len(self.xdata)
return(rchisq)
def find_sigma(self, low, high, ds):
'''Given a range of sigmas (low,high) and interval (ds), compute rchisq
for each and return the smoothing length that gives rchisq = 1 (or as
close as possible).'''
sigs = arange(low, high, ds)
chisqs = array([self.chisq(sigma=sig) for sig in sigs])
id = argsort(absolute(chisqs - 1))[0]
return(sigs[id])
def find_N(self, low, high):
'''Given a range of N (low, high), compute rchisq for each and return
the smoothing length that gives rchisq closest to 1.'''
Ns = range(int(low), int(high)+1)
chisqs = array([self.chisq(N=N) for N in Ns])
id = argsort(absolute(chisqs - 1))[0]
return(Ns[id])
def eval(self, x, sigma=None, N=None):
if not len(shape(x)):
scalar = 1
x = [x]
else:
scalar = 0
x = asarray(x)
if sigma is None:
if self.sigma is None:
if N is None:
if self.N is None:
raise AttributeError, "Must specify a smoothing length"
else:
N = self.N
else:
sigma = self.sigma
res = smooth(self.xdata, self.ydata, self.dydata, x, sigma=sigma, N=N)
self.x = x
self.y = res[0]
self.dy = res[1]
self.fx = res[2]
self.fxx = res[3]
if scalar:
return(self.y[0], self.dy[0])
else:
return(self.y, self.dy)
class surface:
'''A class to represent a surface of data. The construtor takes x,y,z,dz
data as arguments (and optionally the smoothing lengths). Member functions
allow the user to interpolate on the surface.'''
def __init__(self, x,y,z,dz,sigmax=None,sigmay=None, Nx=None, Ny=None):
self.xdata,self.ydata,self.zdata,self.dzdata = map(asarray, [x,y,z,dz])
if not (len(self.xdata) == len(self.ydata) == len(self.zdata) == \
len(self.dzdata)):
raise AttributeError, "x,y,dy must all have same length"
self.sigmax = sigmax
self.sigmay = sigmay
self.Nx = Nx
self.Ny = Ny
def eval(self, x, y, sigmax=None, sigmay=None, Nx=None, Ny=None, tol=0):
if not len(shape(x)):
scalar = 1
x = [x]
else:
scalar = 0
if not len(shape(y)):
scalar = 1
y = [y]
else:
scalar = 0
x = asarray(x)
y = asarray(y)
if not (len(x) == len(y)):
raise RuntimeError, "x and y must have same length"
if sigmax is None:
if self.sigmax is None:
if Nx is None:
if self.Nx is None:
raise AttributeError, "Must specify a x smoothing length"
else:
Nx = self.Nx
else:
sigmax = self.sigmax
if sigmay is None:
if self.sigmay is None:
if Ny is None:
if self.Ny is None:
raise AttributeError, "Must specify a y smoothing length"
else:
Ny = self.Ny
else:
sigmay = self.sigmay
res = smooth2d(self.xdata, self.ydata, self.zdata, self.dzdata, x, y,
sigmax=sigmax, sigmay=sigmay, Nx=Nx, Ny=Ny, tol=tol)
self.x = x
self.y = y
self.z = res[0]
self.dz = res[1]
self.fx = res[2]
self.fy = res[3]
self.fxx = res[4]
self.fyy = res[5]
self.fxy = res[6]
self.sigmaxs = res[7]
self.sigmays = res[8]
return(self.z, self.dz)
Click here to get the file