I have a piece of code that fits each voxel in a data cube with a Gaussian plus a non-zero, sloping linear baseline. Each voxel is a spectrum of line+continuum emission that, depending on the location, can be pretty noisy and is known not to behave well at the edges of either the image or the spectral range. Therefore, sometimes it's necessary to fit the Gaussian and linear components separately in the parts of the spectra where each is most likely to occur, either because the original fit failed, or because the fitting parameters were nonsensical and I can see the line it failed to fit despite the noise. I'd skip straight to the piecewise version if I could, but the discontinuities can be problematic, not to mention it's generally a more costly procedure in terms of time and memory usage.
So my situation is this: I want to have my program respond to multiple possible conditions, where some are exceptions (ValueError and RuntimeError) and the others are Boolean or relational conditions, with the same procedure. Can that be done? Right now I have two copies of the same procedure, one in the exception block and the other in the else block with an internal if-statement, and it's annoying the heck out of me. If I can't combine them, is there a way to reroute one or the other statement to the same block of code without defining a new function?
Edit: I didn't originally include this because my attempts at reorganization were making a mess of my code at the time I wrote this. Now that I've kind of put it back together a bit (it's still a mess b/c each protocol corresponds to about 6 different cases of 3 different categories that are impossible to test for simultaneously as far as I know), here's the relevant parts of my code:
if fitfn not in ['gauss','lorentz']:
raise IOError("Fitting function name must be 'lorentz' or 'gauss'")
cubedims=np.shape(cube)
frames=np.array([(n,w) for n,w in enumerate(wvl) if (lc-0.2<w<lc+0.2)])
#^ 3 sigma is about 27% larger than 2 fwhm
inds,wls=np.transpose(frames)
# store amplitudes of Gaussian/Lorentz profile & their errors:
fdencube=np.zeros((cubedims[1],cubedims[2]))
fduncube=np.zeros((cubedims[1],cubedims[2]))
# Store spectral index (slope) of linear continuum fit:
spindex=np.zeros((cubedims[1],cubedims[2]))
spundex=np.zeros((cubedims[1],cubedims[2]))
# Store continuum-subtracted line profiles:
lincube=np.zeros((len(frames),cubedims[1],cubedims[2]))
elincube=np.zeros((len(frames),cubedims[1],cubedims[2]))
# store line-subtracted continuum profiles:
concube=np.zeros((cubedims))
econcube=np.zeros((cubedims))
for x in xrange(cubedims[1]):
for y in xrange(cubedims[2]):
spec = cube[:,x,y]
uspec = ecube[:,x,y]
try:
p,pcov=curvf(globals()[fitfn], wvl[~np.isnan(spec)],
spec[~np.isnan(spec)],sigma=uspec[~np.isnan(spec)],
bounds = [[0.01,min(wvl),np.mean(wvl[1:]-
wvl[:-1]),-10,0.],
[50.,max(wvl),0.4,10,10.0]])
fwhm=2*abs(p[2]) if fitfn=='lorentz' else
p[2]*np.sqrt(8*np.log(2))
if (fwhm < 0.16 and (lc-0.05<p[1]<lc+0.05) and 'pcov' in
globals()):
stdp=np.sqrt(np.diag(pcov))
cvw=p[-2]*frames[:,1]+p[-1]
lincube[:,x,y]=spec[inds.astype(int)]-cvw
elincube[:,x,y]=np.sqrt(uspec[inds.astype(int)]**2+
stdp[-2]**2+stdp[-1]**2)
lvw=gauss(wvl,p[0],p[1],p[2],0,0)
concube[:,x,y]=spec-lvw
econcube[:,x,y]=np.sqrt(uspec**2+stdp[0]**2+
stdp[1]**2+stdp[2]**2)
spindex[x,y]=p[-2]
spundex[x,y]=stdp[-2]
fdencube[x,y]=p[0]
fduncube[x,y]=stdp[0]
else:
try:
s=spec[~inds.astype(int)]
u=uspec[~inds.astype(int)]
q,qcov=curvf(lreg,wls[~np.isnan(s)],s[~np.isnan(s)],
sigma=u[~np.isnan(s)],bounds = [[-10,0.],
[10,10.0]])
r,rcov=curvf(globals([fitfn],wvl[inds.astype(int)],
spec[inds.astype(int)],
sigma=uspec[inds.astype(int)],
bounds = [[0.01,min(wvl),np.mean(wvl[1:]-
wvl[:-1]),-10,0.],
[50.,max(wvl),0.4,10,10.0]])
fwhmr=2*abs(r[2]) if fitfn=='lorentz' else
r[2]*np.sqrt(8*np.log(2))
if (fwhmr < 0.16 and (lc-0.05<r[1]<lc+0.05) and 'rcov'
in globals()):
stdr=np.sqrt(np.diag(rcov))
stdq=np.sqrt(np.diag(qcov))
lvw=gauss(wvl,r[0],r[1],r[2],0,0)
concube[:,x,y]=spec-lvw
econcube[:,x,y]=np.sqrt(uspec**2+stdr[0]**2+
stdr[1]**2+stdr[2]**2)
cvw=q[0]*frames[:,1]+q[1]
lincube[:,x,y]=spec[inds.astype(int)]-cvw
elincube[:,x,y]=np.sqrt(uspec[inds.astype(int)]**2+
stdq[-2]**2+stdq[-1]**2)
spindex[x,y]=q[0]
spundex[x,y]=stdq
fdencube[x,y]=r[0]
fduncube[x,y]=stdr[0]
except (ValueError,RuntimeError):
fdencube[x,y]=np.NaN
fduncube[x,y]=np.NaN
lincube[:,x,y]=np.NaN
elincube[:,x,y]=np.NaN
try:
q,qcov=curvf(lreg,wvl[~np.isnan(spec)],
spec[~np.isnan(spec)],
sigma=uspec[~np.isnan(spec)],bounds = [[-10,0.],
[10,10.0]])
if 'qcov' in globals():
concube[:,x,y]=spec
econcube[:,x,y]=uspec
spindex[x,y]=q[0]
spundex[x,y]=np.sqrt(np.diag(qcov))[0]
else:
concube[:,x,y]=spec
econcube[:,x,y]=uspec
spindex[x,y]=q[0]
spundex[x,y]=np.NaN
except (ValueError,RuntimeError):
print 'fit failed'
concube[:,x,y]=spec
econcube[:,x,y]=uspec
spindex[x,y]=np.NaN
spundex[x,y]=np.NaN
except (ValueError,RuntimeError):
try:
s=spec[~inds.astype(int)]
u=uspec[~inds.astype(int)]
q,qcov=curvf(lreg,wls[~np.isnan(s)],s[~np.isnan(s)],
sigma=u[~np.isnan(s)],bounds = [[-10,0.],
[10,10.0]])
r,rcov=curvf(globals()[fitfn],wvl[inds.astype(int)],
spec[inds.astype(int)],
sigma=uspec[inds.astype(int)],
bounds = [[0.01,min(wvl),np.mean(wvl[1:]-
wvl[:-1]),-10,0.],
[50.,max(wvl),0.4,10,10.0]])
fwhmr=2*abs(r[2]) if fitfn=='lorentz' else
r[2]*np.sqrt(8*np.log(2))
if (fwhmr < 0.16 and (lc-0.05<r[1]<lc+0.05) and 'rcov' in
globals()):
stdr=np.sqrt(np.diag(rcov))
stdq=np.sqrt(np.diag(qcov))
lvw=gauss(wvl,r[0],r[1],r[2],0,0)
concube[:,x,y]=spec-lvw
econcube[:,x,y]=np.sqrt(uspec**2+stdr[0]**2+
stdr[1]**2+stdr[2]**2)
cvw=q[0]*frames[:,1]+q[1]
lincube[:,x,y]=spec[inds.astype(int)]-cvw
elincube[:,x,y]=np.sqrt(uspec[inds.astype(int)]**2+
stdq[-2]**2+stdq[-1]**2)
spindex[x,y]=q[0]
spundex[x,y]=stdq
fdencube[x,y]=r[0]
fduncube[x,y]=stdr[0]
except (ValueError,RuntimeError):
fdencube[x,y]=np.NaN
fduncube[x,y]=np.NaN
lincube[:,x,y]=np.NaN
elincube[:,x,y]=np.NaN
try:
q,qcov=curvf(lreg,wvl[~np.isnan(spec)],
spec[~np.isnan(spec)],
sigma=uspec[~np.isnan(spec)],bounds =
[[-10,0.],[10,10.0]])
if 'qcov' in globals():
concube[:,x,y]=spec
econcube[:,x,y]=uspec
spindex[x,y]=q[0]
spundex[x,y]=np.sqrt(np.diag(qcov))[0]
else:
print 'fit failed'
concube[:,x,y]=spec
econcube[:,x,y]=uspec
spindex[x,y]=np.NaN
spundex[x,y]=np.NaN
except (ValueError,RuntimeError):
#if fit fails, assume it's continuum;
# continuum spectral profile doesn't matter as much
print 'fit failed'
concube[:,x,y]=spec
econcube[:,x,y]=uspec
spindex[x,y]=np.NaN
spundex[x,y]=np.NaN
As you can see it's a mess of duplication because I don't know how to check for exceptions, variable existence, and how a variable (assuming it exists) compares to another variable all in one line. I'm in the process of writing a module that checks for variable existence and does Boolean and relational operations such that if the variable is undefined the return value is always False, but that still leaves the exceptions. Is there a way to store an exception?