def transform_bands_msd(msd, transform, fill_value=np.nan): """Convert light curve to another band set Parameters ---------- msd: MultiStateData Initial light curve object, `y` is `10^(-0.4 magn)` as produced by `OSCCurve`. All light curves should have the same length transform: BandTransformation One of `VR_to_gri`, `BR_to_gri`, `BRI_to_gri`, defined in this module fill_value: float Value to fill corrupt data, i.e. non-positive fluxes Returns ------- MultiStateData """ old_dict = { band: -2.5 * np.log10(msd.odict[band].y) for band in transform.old_bands } magn = transform(old_dict) new_odict = OrderedDict() x = msd.odict[transform.old_bands[0]].x err = np.full_like(x, np.nan) for band in transform.new_bands: y = 10**(-0.4 * magn[band]) y[np.isnan(y)] = fill_value new_odict[band] = np.rec.array((x, y, err), dtype=[('x', float), ('y', float), ('err', float)]) new_msd = MultiStateData.from_state_data(new_odict) return new_msd
def __call__(self, x=None, fill_error=False): """Evaluates the approximation for all the bands. Parameters ---------- x: 1-d ndarray or None Either evaluate all the bands at the specific x values, or use initial ones when the supplied x is equal to None. fill_error: bool Should the approximation errors be estimated. """ odict = OrderedDict() xx = x for band in self.bands: if x is None: xx = self.curve[band]['x'] recarr = np.recarray(len(xx), [('x', 'd'), ('y', 'd'), ('err', 'd')]) recarr['x'] = xx recarr['y'] = self.band_approximation(band, xx) recarr['err'] = self.band_approximation_error( band, xx) if fill_error else np.zeros(len(xx)) odict[band] = recarr return MultiStateData.from_state_data(odict)
def set_error(self, absolute=0, rel=0): """Return new SNCurve with set errors for dots without them The equation for the error is `err = absolute + rel * y`. Upper limits will not be changed. Parameters ---------- absolute: float or dict-like[str: float], optional Can be dictionary where keys are bands rel: float, optional Returns ------- SNCurve """ d = dict(self.odict) for band, lc in d.items(): lc = d[band] = lc.copy() was_writeable = lc.flags.writeable lc.flags.writeable = True inf_err_idx = (~(lc['isupperlimit'])) & (~np.isfinite(lc['err'])) if isinstance(absolute, Real): abs_band = absolute else: abs_band = absolute[band] lc.err[inf_err_idx] = abs_band + rel * lc[inf_err_idx].y lc.flags.writeable = was_writeable msd = MultiStateData.from_state_data(d) return SNCurve(msd, self.name, is_binned=self.is_binned, is_filtered=False, additional_attrs=self.__additional_attrs)
def test_msd(self): x = np.array([0.]) err = np.array([0.]) magnB = 20 magnR = 20 magnI = 20 items = (('B', np.rec.array((x, np.array([10 ** (-0.4 * magnB)]), err), dtype=[('x', float), ('y', float), ('err', float)])), ('R', np.rec.array((x, np.array([10 ** (-0.4 * magnR)]), err), dtype=[('x', float), ('y', float), ('err', float)])), ('I', np.rec.array((x, np.array([10 ** (-0.4 * magnI)]), err), dtype=[('x', float), ('y', float), ('err', float)]))) msd = MultiStateData.from_state_data(items) new_msd = transform_bands_msd(msd, BRI_to_gri) magn = -2.5 * np.log10((new_msd.arrays.y * new_msd.norm)) assert_allclose(magn, [19.84211267, 20.06126321, 20.32025792])
def convert_dict(self, d, is_binned=False, is_filtered=False): """Convert dict to SNCurve with the same attributes Parameters ---------- d: dict-like It should has the same format as `.odict` is_binned: bool, optional is_filtered: bool, optional Returns ------- SNCurve """ msd = MultiStateData.from_state_data(d) return self.convert_msd(msd, is_binned, is_filtered)
def binned(self, bin_width, discrete_time=False, bands=None): """Binned photometry data The eges of the bins will be produced by the formula `time // bin_width * bin_width`. If upper limit dots are not the only dots in the sample, they will be excluded, as the dots with infinite errors. If only upper limit dots are presented, the best will be used, if only infinite error dots are presented, their mean will be used. If any dots with finite errors are presented, then weighed mean and corresponding error is calculated. For `discrete_time=True` original time errors `err_x` are ignored. Parameters ---------- bin_width: float or None, optional The width of samples, in the units of photometry time dots discrete_time: bool, optional If `True`, all time steps between dots will be a multiple of `bin_width`, else weighted time moments will be used bands: iterable of str or None, optional Bands to use. Default is None, SNCurve.bands will be used Returns ------- SNCurve Raises ------ EmptyPhotometryError """ if bands is None: bands = self.bands else: bands = _transform_to_tuple(bands) if set(bands).difference(self.bands): raise EmptyPhotometryError(self.name, set(bands) - set(self.bands)) msd = MultiStateData.from_state_data((band, self._binning(self[band], bin_width, discrete_time)) for band in bands) return SNCurve(msd, name=self.name, is_binned=True, is_filtered=self.is_filtered, additional_attrs=self.__additional_attrs)
def setUp(self): x = np.arange(-20, 50, 0.5) self.sigma = 0.05 self.fall_time = 20 self.rise_time = 5 self.bottom = 0.2 self.scale = 2.0 np.random.seed(0) odict = OrderedDict() recarr = np.recarray(x.shape, [('x', 'd'), ('y', 'd'), ('err', 'd')]) recarr['x'] = x recarr['y'] = self.bottom + self.scale / (np.exp(-x / self.rise_time) + np.exp(x / self.fall_time)) +\ np.random.normal(0, self.sigma, x.shape) recarr['err'] = np.repeat(self.sigma, x.shape) odict['r'] = recarr self.msd = MultiStateData.from_state_data(odict) self.bf = BazinFitter(self.msd, name='Generated MSD') self.bf.fit()
def zero_negative_fluxes(msd, x_peak=None): """Convert negative y-values to zero Parameters ---------- msd: MultiStateData Light curves to process x_peak: float or None, optional If `x_peak` is `None` than all negative values are zeroed. Otherwise the closest (in time, aka `x`) "left" and "right" negative values are searched and all values before "left" and after "right" time moment are zeroed, even if some of them are positive Returns ------- MultistateData Examples -------- Zero all negative values: >>> import numpy as np >>> from multistate_kernel.util import MultiStateData >>> from snad.process.util import zero_negative_fluxes >>> >>> x = np.arange(5, dtype=np.float) >>> y = np.array([-3., 5., -2., 1., -77.]) >>> err = np.zeros_like(y) # doesn't matter in this case >>> lc = np.rec.fromarrays([x, y, err], names='x,y,err') >>> msd = MultiStateData.from_state_data({'X': lc}) >>> zeroed_msd = zero_negative_fluxes(msd) >>> print(zeroed_msd.odict['X'].y) [0. 5. 0. 1. 0.] Zero "wings" around peak: >>> import numpy as np >>> from multistate_kernel.util import MultiStateData >>> from snad.process.util import zero_negative_fluxes >>> >>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=np.float) >>> x_peak = 5. >>> y = np.array([1,-1,-2, 1, 2, 3, 2, 1, -1,-2,-3], dtype=np.float) >>> err = np.zeros_like(y) # doesn't matter in this case >>> lc = np.rec.fromarrays([x, y, err], names='x,y,err') >>> msd = MultiStateData.from_state_data({'X': lc}) >>> zeroed_msd = zero_negative_fluxes(msd, x_peak=x_peak) >>> print(zeroed_msd.odict['X'].y) [0. 0. 0. 1. 2. 3. 2. 1. 0. 0. 0.] """ new_odict = OrderedDict() for band, lc in msd.odict.items(): new_odict[band] = lc = copy.deepcopy(lc) if x_peak is None: idx = lc.y < 0 lc.y[idx] = 0 lc.err[idx] = 0 else: for i in range(lc.x.size - 1, -1, -1): if lc.x[i] >= x_peak: continue if lc.y[i] <= 0: i += 1 break i_left_nonzero = i for i in range(lc.x.size): if lc.x[i] <= x_peak: continue if lc.y[i] <= 0: i -= 1 break i_right_nonzero = i lc.y[:i_left_nonzero] = 0 lc.err[:i_left_nonzero] = 0 lc.y[i_right_nonzero + 1:] = 0 lc.err[i_right_nonzero + 1:] = 0 msd = MultiStateData.from_state_data(new_odict) return msd
def __init__(self, json_data, bands=None): d = dict() self._json = json_data name = self._json['name'] if bands is not None: bands = _transform_to_tuple(bands) bands_set = set(bands) add_attrs = {} for func, fields in iteritems(self.__additional_value_fields): for field in fields: add_attrs[field] = tuple(func(x['value']) for x in self._json.get(field, [])) add_attrs['has_spectra'] = 'spectra' in self._json add_attrs['spectrum_count'] = 0 if add_attrs['has_spectra']: add_attrs['spectrum_count'] = len(self._json['spectra']) if 'photometry' not in self._json: raise NoPhotometryError(name) for dot in self._json['photometry']: if 'time' in dot and 'band' in dot: # Model data, not real observation if 'realization' in dot or 'model' in dot: continue # Observation of host, not target object if 'host' in dot: continue if (bands is not None) and (dot.get('band') not in bands_set): continue band_curve = d.setdefault(dot['band'], []) time = dot['time'] if isinstance(time, list): time = np.mean([float(t) for t in time]) if 'e_time' in dot: e_time = float(dot['e_time']) if e_time < 0 or not np.isfinite(e_time): raise BadPhotometryDotError(name, dot, 'e_time') else: e_time = np.nan magn = float(dot['magnitude']) flux = np.power(10, -0.4 * magn) if not np.isfinite(flux): raise BadPhotometryDotError(name, dot) if 'e_lower_magnitude' in dot and 'e_upper_magnitude' in dot: e_lower_magn = float(dot['e_lower_magnitude']) e_upper_magn = float(dot['e_upper_magnitude']) flux_lower = np.power(10, -0.4 * (magn + e_lower_magn)) flux_upper = np.power(10, -0.4 * (magn - e_upper_magn)) e_flux = 0.5 * (flux_upper - flux_lower) if e_lower_magn < 0: raise BadPhotometryDotError(name, dot, 'e_lower_magnitude') if e_upper_magn < 0: raise BadPhotometryDotError(name, dot, 'e_upper_magnitude') if not np.isfinite(e_flux): raise BadPhotometryDotError(name, dot) elif 'e_magnitude' in dot: e_magn = float(dot['e_magnitude']) e_flux = 0.4 * np.log(10) * flux * e_magn if e_magn < 0: raise BadPhotometryDotError(name, dot, 'e_magnitude') if not np.isfinite(e_flux): raise BadPhotometryDotError(name, dot) if e_magn == 0: e_flux = np.nan else: e_flux = np.nan band_curve.append(( time, e_time, flux, e_flux, dot.get('upperlimit', False), )) for k, v in iteritems(d): v = d[k] = np.rec.fromrecords(v, dtype=self._photometry_dtype) if np.any(np.diff(v['x']) < 0): logging.info('Original SN {} data for band {} contains unordered dots'.format(name, k)) v[:] = v[np.argsort(v['x'])] v.flags.writeable = False if sum(len(v) for v in iteritems(d)) == 0: raise EmptyPhotometryError(name, bands) if bands is None: bands = tuple(sorted(iterkeys(d))) else: for band in bands: if band not in d: raise EmptyPhotometryError(name, (band,)) msd = MultiStateData.from_state_data((band, d[band]) for band in bands) super(OSCCurve, self).__init__(msd, name=name, is_binned=False, is_filtered=False, additional_attrs=add_attrs)
def multi_state_data(self): """Copy photometry data as MultiStateData""" return MultiStateData.from_state_data(self.odict)
def filtered(self, with_upper_limits=False, with_inf_e_flux=False, bands=None, sort='default'): """Filtered and sorted by bands SNCurve Parameters ---------- with_upper_limits: bool, optional Include observation point marked as an upper limit with_inf_e_flux: bool, optional Include observation point with infinity/NaN error bands: iterable of str or str or None, optional Bands to return. Default is None, SNCurves.bands will be used sort: str, optional How `bands` will be sorted. Should be one of the following strings: - 'default' will keep the order of `bands` - 'alphabetic' or 'alpha' will sort `bands` alphabetically - 'total' will sort `bands` by the total number of photometric points, from maximum to minimum - 'filtered' will sort `bands` by the number of returned photometric points from maximum to minimum, e.g. points filtered by `with_upper_limits` and `with_inf_e_flux` arguments Returns ------- SNCurve Raises ------ EmptyPhotometryError """ if bands is None: bands = self.bands else: bands = _transform_to_tuple(bands) if (with_upper_limits and with_inf_e_flux and (bands is None or bands == self.bands) and sort == 'default'): return self @lru_cache(maxsize=1) def fd(): if with_upper_limits and with_inf_e_flux: # Little optimization return self.odict return {band: self[band][(np.logical_not(self[band]['isupperlimit']) + with_upper_limits) & (np.isfinite(self[band]['err']) + with_inf_e_flux)] for band in bands} if sort == 'default': pass elif sort == 'alphabetic' or sort == 'alpha': bands = sorted(bands) elif sort == 'total': bands = sorted(bands, key=lambda band: self[band].size, reverse=True) elif sort == 'filtered': bands = sorted(bands, key=lambda band: fd()[band].size, reverse=True) else: raise ValueError('Argument sort={} is not supported'.format(sort)) msd = MultiStateData.from_state_data((band, fd()[band]) for band in bands) if not msd.arrays.y.size: raise EmptyPhotometryError(self.name, bands) return SNCurve(msd, name=self.name, is_binned=self.is_binned, is_filtered=True, additional_attrs=self.__additional_attrs)