import pandas as pd
import numpy as np
import warnings
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
import pysynphot.binning as astrobin
import warnings as warn
import astropy.units as u
[docs]def binning(x, y, dy=None, binwidth=None, r=None,newx= None, log = False, nan=False):
"""
This contains functionality for binning spectroscopy given an x, y and set of errors.
This is similar to IDL's regroup but in Python (obviously). Note that y is binned as the
mean(ordinates) instead of sum(ordinates), as you would want for cmputing flux in a set of
pixels. User can input a constant resolution, constant binwidth or provide a user defined
bin. The error is computed as sum(sqrt(sig1^2, sig2^2, sig3^2) )/3, for example
if there were 3 points to bin.
Parameters
----------
x : array, float
vector containing abcissae.
y : array,float
vector containing ordinates.
dy : array,float
(Optional) errors on ordinates, can be float or array
binwidth : float
(Optional) constant bin width in same units as x
r : float
(Optional) constant resolution to bin to. R is defined as w[1]/(w[2] - w[0])
to maintain consistency with `pandeia.engine`
newx : array, float
(Optional) new x axis to bin to
log : bool
(Optional) computes equal bin spacing logarithmically, Default = False
sort : bool
(Optional) sort into ascending order of x, default = True
nan : bool
(Optional) if true, this returns nan values where no points exist in a given bin
Otherwise, all nans are dropped
Returns
-------
dict
bin_y : binned ordinates
bin_x : binned abcissae
bin_edge : edges of bins (always contains len(bin_x)+1 elements)
bin_dy : error on ordinate bin
bin_n : number of points contained in each bin
Examples
--------
>>> from bintools import binning
If you want constant resolution (using output dict from PandExo):
>>> pandexo = result['FinalSpectrum']
>>> x, y, err = pandexo['wave'], pandexo['spectrum_w_rand'], pandexo['error_w_floor']
>>> final = binning(x, y, dy = err, r =100)
>>> newx, newy, newerr = final['bin_x'], final['bin_y'], final['bin_dy']
If you have a x axis that you want PandExo output to be binned to
>>> newx = np.linspace(1,5,10)
>>> final = binning(x, y, dy = err, newx =newx)
>>> newx, newy, newerr = final['bin_x'], final['bin_y'], final['bin_dy']
If you want a constant bin width and want everything to be spaced linearly
>>> final = binning(x, y, dy = err, binwidth = 0.1)
>>> newx, newy, newerr = final['bin_x'], final['bin_y'], final['bin_dy']
If you want constant bin width but want everything to spaced logarithmically
>>> final = binning(x, y, dy = err, binwidth = 0.1, log=True)
>>> newx, newy, newerr = final['bin_x'], final['bin_y'], final['bin_dy']
"""
#check x and y are same length
if len(x) != len(y):
raise Exception('X and Y are not the same length')
#check that either newx or binwidth are specified
if newx is None and binwidth is None and r is None:
raise Exception('Need to either supply new x axis, resolution, or a binwidth')
if (binwidth is None) and (log):
raise Exception("Cannot do logarithmic binning with out a binwidth")
if newx is not None:
bin_x = newx
bin_x, bin_y, bin_dy, bin_n = uniform_tophat_mean(bin_x,x, y, dy=dy,nan=nan)
bin_edge = astrobin.calculate_bin_edges(bin_x)
return {'bin_y':bin_y, 'bin_x':bin_x, 'bin_edge':bin_edge, 'bin_dy':bin_dy, 'bin_n':bin_n}
elif r is not None:
bin_x = bin_wave_to_R(x, r)
bin_x, bin_y, bin_dy, bin_n = uniform_tophat_mean(bin_x,x, y, dy=dy,nan=nan)
bin_edge = astrobin.calculate_bin_edges(bin_x)
return {'bin_y':bin_y, 'bin_x':bin_x, 'bin_edge':bin_edge, 'bin_dy':bin_dy, 'bin_n':bin_n}
elif binwidth is not None:
if (binwidth < 0) and (log):
warn.warn(UserWarning("Negative binwidth specified. Assuming this is log10(binwidth)"))
binwidth = 10**binwidth
if log:
bin_x = np.arange(np.log10(min(x)),np.log10(max(x)),np.log10(binwidth))
bin_x = 10**bin_x
elif not log:
bin_x = np.arange(min(x),max(x),binwidth)
bin_x, bin_y, bin_dy, bin_n = uniform_tophat_mean(bin_x,x, y, dy=dy,nan=nan)
bin_edge = astrobin.calculate_bin_edges(bin_x)
return {'bin_y':bin_y, 'bin_x':bin_x, 'bin_edge':bin_edge, 'bin_dy':bin_dy, 'bin_n':bin_n}
[docs]def bin_wave_to_R(w, R):
"""Creates new wavelength axis at specified resolution
Parameters
----------
w : list of float or numpy array of float
Wavelength axis to be rebinned
R : float or int
Resolution to bin axis to
Returns
-------
list of float
New wavelength axis at specified resolution
Examples
--------
>>> newwave = bin_wave_to_R(np.linspace(1,2,1000), 10)
>>> print((len(newwave)))
11
"""
wave = []
tracker = min(w)
i = 1
ind= 0
firsttime = True
while(tracker<max(w)):
if i <len(w)-1:
dlambda = w[i]-w[ind]
newR = w[i]/dlambda
if (newR < R) & (firsttime):
tracker = w[ind]
wave += [tracker]
ind += 1
i += 1
firsttime = True
elif newR < R:
tracker = w[ind]+dlambda/2.0
wave +=[tracker]
ind = (np.abs(w-tracker)).argmin()
i = ind+1
firsttime = True
else:
firsttime = False
i+=1
else:
tracker = max(w)
wave += [tracker]
return wave