def linearize_freqs(self, delta_freq=None): """Rebin frequencies so that the frequency axis is linear. Parameters ---------- delta_freq : float Difference between consecutive values on the new frequency axis. Defaults to half of smallest delta in current frequency axis. Compare Nyquist-Shannon sampling theorem. """ if delta_freq is None: # Nyquist–Shannon sampling theorem delta_freq = _min_delt(self.freq_axis) / 2. nsize = (self.freq_axis.max() - self.freq_axis.min()) / delta_freq + 1 new = np.zeros((nsize, self.shape[1]), dtype=self.data.dtype) freqs = self.freq_axis - self.freq_axis.max() freqs = freqs / delta_freq midpoints = np.round((freqs[:-1] + freqs[1:]) / 2) fillto = np.concatenate( [midpoints - 1, np.round([freqs[-1]]) - 1] ) fillfrom = np.concatenate( [np.round([freqs[0]]), midpoints - 1] ) fillto = np.abs(fillto) fillfrom = np.abs(fillfrom) for row, from_, to_ in izip(self, fillfrom, fillto): new[from_: to_] = row vrs = self._get_params() vrs.update({ 'freq_axis': np.linspace( self.freq_axis.max(), self.freq_axis.min(), nsize ) }) return self.__class__(new, **vrs)
def interpolate(self, frequency): """ Linearly interpolate intensity at unknown frequency using linear interpolation of its two neighbours. Parameters ---------- frequency : float or int Unknown frequency for which to linearly interpolate the intensities. freq_axis[0] >= frequency >= self_freq_axis[-1] """ lfreq, lvalue = None, None for freq, value in izip(self.freq_axis, self.data[:, :]): if freq < frequency: break lfreq, lvalue = freq, value else: raise ValueError("Frequency not in interpolation range") if lfreq is None: raise ValueError("Frequency not in interpolation range") diff = frequency - freq # pylint: disable=W0631 ldiff = lfreq - frequency return (ldiff * value + diff * lvalue) / (diff + ldiff) # pylint: disable=W0631
def linearize_freqs(self, delta_freq=None): """Rebin frequencies so that the frequency axis is linear. Parameters ---------- delta_freq : float Difference between consecutive values on the new frequency axis. Defaults to half of smallest delta in current frequency axis. Compare Nyquist-Shannon sampling theorem. """ if delta_freq is None: # Nyquist–Shannon sampling theorem delta_freq = _min_delt(self.freq_axis) / 2. nsize = (self.freq_axis.max() - self.freq_axis.min()) / delta_freq + 1 new = np.zeros((nsize, self.shape[1]), dtype=self.data.dtype) freqs = self.freq_axis - self.freq_axis.max() freqs = freqs / delta_freq midpoints = np.round((freqs[:-1] + freqs[1:]) / 2) fillto = np.concatenate([midpoints - 1, np.round([freqs[-1]]) - 1]) fillfrom = np.concatenate([np.round([freqs[0]]), midpoints - 1]) fillto = np.abs(fillto) fillfrom = np.abs(fillfrom) for row, from_, to_ in izip(self, fillfrom, fillto): new[from_:to_] = row vrs = self._get_params() vrs.update({ 'freq_axis': np.linspace(self.freq_axis.max(), self.freq_axis.min(), nsize) }) return self.__class__(new, **vrs)
def join_many(cls, specs, mk_arr=None, nonlinear=False, maxgap=0, fill=JOIN_REPEAT): """Produce new Spectrogram that contains spectrograms joined together in time. Parameters ---------- specs : list List of spectrograms to join together in time. nonlinear : bool If True, leave out gaps between spectrograms. Else, fill them with the value specified in fill. maxgap : float, int or None Largest gap to allow in second. If None, allow gap of arbitrary size. fill : float or int Value to fill missing values (assuming nonlinear=False) with. Can be LinearTimeSpectrogram.JOIN_REPEAT to repeat the values for the time just before the gap. mk_array: function Function that is called to create the resulting array. Can be set to LinearTimeSpectrogram.memap(filename) to create a memory mapped result array. """ # XXX: Only load header and load contents of files # on demand. mask = None if mk_arr is None: mk_arr = cls.make_array specs = sorted(specs, key=lambda x: x.start) freqs = specs[0].freq_axis if not all(np.array_equal(freqs, sp.freq_axis) for sp in specs): raise ValueError("Frequency channels do not match.") # Smallest time-delta becomes the common time-delta. min_delt = min(sp.t_delt for sp in specs) dtype_ = max(sp.dtype for sp in specs) specs = [sp.resample_time(min_delt) for sp in specs] size = sum(sp.shape[1] for sp in specs) data = specs[0] start_day = data.start xs = [] last = data for elem in specs[1:]: e_init = (SECONDS_PER_DAY * (get_day(elem.start) - get_day(start_day)).days + elem.t_init) x = int((e_init - last.t_init) / min_delt) xs.append(x) diff = last.shape[1] - x if maxgap is not None and -diff > maxgap / min_delt: raise ValueError("Too large gap.") # If we leave out undefined values, we do not want to # add values here if x > t_res. if nonlinear: size -= max(0, diff) else: size -= diff last = elem # The non existing element after the last one starts after # the last one. Needed to keep implementation below sane. xs.append(specs[-1].shape[1]) # We do that here so the user can pass a memory mapped # array if they'd like to. arr = mk_arr((data.shape[0], size), dtype_) time_axis = np.zeros((size, )) sx = 0 # Amount of pixels left out due to non-linearity. Needs to be # considered for correct time axes. sd = 0 for x, elem in izip(xs, specs): diff = x - elem.shape[1] e_time_axis = elem.time_axis elem = elem.data if x > elem.shape[1]: if nonlinear: x = elem.shape[1] else: # If we want to stay linear, fill up the missing # pixels with placeholder zeros. filler = np.zeros((data.shape[0], diff)) if fill is cls.JOIN_REPEAT: filler[:, :] = elem[:, -1, np.newaxis] else: filler[:] = fill minimum = e_time_axis[-1] e_time_axis = np.concatenate([ e_time_axis, np.linspace(minimum + min_delt, minimum + diff * min_delt, diff) ]) elem = np.concatenate([elem, filler], 1) arr[:, sx:sx + x] = elem[:, :x] if diff > 0: if mask is None: mask = np.zeros((data.shape[0], size), dtype=np.uint8) mask[:, sx + x - diff:sx + x] = 1 time_axis[sx:sx + x] = e_time_axis[:x] + data.t_delt * (sx + sd) if nonlinear: sd += max(0, diff) sx += x params = { 'time_axis': time_axis, 'freq_axis': data.freq_axis, 'start': data.start, 'end': specs[-1].end, 't_delt': data.t_delt, 't_init': data.t_init, 't_label': data.t_label, 'f_label': data.f_label, 'content': data.content, 'instruments': _union(spec.instruments for spec in specs), } if mask is not None: arr = ma.array(arr, mask=mask) if nonlinear: del params['t_delt'] return Spectrogram(arr, **params) return common_base(specs)(arr, **params)
def join_many(cls, specs, mk_arr=None, nonlinear=False, maxgap=0, fill=JOIN_REPEAT): """Produce new Spectrogram that contains spectrograms joined together in time. Parameters ---------- specs : list List of spectrograms to join together in time. nonlinear : bool If True, leave out gaps between spectrograms. Else, fill them with the value specified in fill. maxgap : float, int or None Largest gap to allow in second. If None, allow gap of arbitrary size. fill : float or int Value to fill missing values (assuming nonlinear=False) with. Can be LinearTimeSpectrogram.JOIN_REPEAT to repeat the values for the time just before the gap. mk_array: function Function that is called to create the resulting array. Can be set to LinearTimeSpectrogram.memap(filename) to create a memory mapped result array. """ # XXX: Only load header and load contents of files # on demand. mask = None if mk_arr is None: mk_arr = cls.make_array specs = sorted(specs, key=lambda x: x.start) freqs = specs[0].freq_axis if not all(np.array_equal(freqs, sp.freq_axis) for sp in specs): raise ValueError("Frequency channels do not match.") # Smallest time-delta becomes the common time-delta. min_delt = min(sp.t_delt for sp in specs) dtype_ = max(sp.dtype for sp in specs) specs = [sp.resample_time(min_delt) for sp in specs] size = sum(sp.shape[1] for sp in specs) data = specs[0] start_day = data.start xs = [] last = data for elem in specs[1:]: e_init = ( SECONDS_PER_DAY * ( get_day(elem.start) - get_day(start_day) ).days + elem.t_init ) x = int((e_init - last.t_init) / min_delt) xs.append(x) diff = last.shape[1] - x if maxgap is not None and -diff > maxgap / min_delt: raise ValueError("Too large gap.") # If we leave out undefined values, we do not want to # add values here if x > t_res. if nonlinear: size -= max(0, diff) else: size -= diff last = elem # The non existing element after the last one starts after # the last one. Needed to keep implementation below sane. xs.append(specs[-1].shape[1]) # We do that here so the user can pass a memory mapped # array if they'd like to. arr = mk_arr((data.shape[0], size), dtype_) time_axis = np.zeros((size,)) sx = 0 # Amount of pixels left out due to non-linearity. Needs to be # considered for correct time axes. sd = 0 for x, elem in izip(xs, specs): diff = x - elem.shape[1] e_time_axis = elem.time_axis elem = elem.data if x > elem.shape[1]: if nonlinear: x = elem.shape[1] else: # If we want to stay linear, fill up the missing # pixels with placeholder zeros. filler = np.zeros((data.shape[0], diff)) if fill is cls.JOIN_REPEAT: filler[:, :] = elem[:, -1, np.newaxis] else: filler[:] = fill minimum = e_time_axis[-1] e_time_axis = np.concatenate([ e_time_axis, np.linspace( minimum + min_delt, minimum + diff * min_delt, diff ) ]) elem = np.concatenate([elem, filler], 1) arr[:, sx:sx + x] = elem[:, :x] if diff > 0: if mask is None: mask = np.zeros((data.shape[0], size), dtype=np.uint8) mask[:, sx + x - diff:sx + x] = 1 time_axis[sx:sx + x] = e_time_axis[:x] + data.t_delt * (sx + sd) if nonlinear: sd += max(0, diff) sx += x params = { 'time_axis': time_axis, 'freq_axis': data.freq_axis, 'start': data.start, 'end': specs[-1].end, 't_delt': data.t_delt, 't_init': data.t_init, 't_label': data.t_label, 'f_label': data.f_label, 'content': data.content, 'instruments': _union(spec.instruments for spec in specs), } if mask is not None: arr = ma.array(arr, mask=mask) if nonlinear: del params['t_delt'] return Spectrogram(arr, **params) return common_base(specs)(arr, **params)