def test_linear_interp(self): """PiecewisePolynomial1DFit: Test underlying 1st-order interpolator.""" x = np.sort(np.random.rand(100)) * 4. - 2.5 y = np.random.randn(100) interp = PiecewisePolynomial1DFit(max_degree=1) interp.fit(x, y) assert_almost_equal(interp(x), y, decimal=10) assert_almost_equal(_linear_interp(x, y, x), y, decimal=10) assert_almost_equal(interp(self.x), _linear_interp(x, y, self.x), decimal=10)
def test_fit_eval(self): """PiecewisePolynomial1DFit: Basic function fitting and evaluation.""" interp = PiecewisePolynomial1DFit(max_degree=3) self.assertRaises(NotFittedError, interp, self.x) self.assertRaises(ValueError, interp.fit, [0, 0], [1, 2]) interp.fit(self.x[::2], self.y[::2]) y = interp(self.x) assert_almost_equal(y[5:-5], self.y[5:-5], decimal=10) # Fit a single data point interp.fit(self.x[0], self.y[0]) y = interp(self.x) assert_equal(y, np.tile(self.y[0], self.x.shape))
def test_stepwise_interp(self): """PiecewisePolynomial1DFit: Test underlying zeroth-order interpolator.""" x = np.sort(np.random.rand(100)) * 4. - 2.5 y = np.random.randn(100) interp = PiecewisePolynomial1DFit(max_degree=0) interp.fit(x, y) assert_almost_equal(interp(x), y, decimal=10) assert_almost_equal(interp(x + 1e-15), y, decimal=10) assert_almost_equal(interp(x - 1e-15), y, decimal=10) assert_almost_equal(_stepwise_interp(x, y, x), y, decimal=10) assert_almost_equal(interp(self.x), _stepwise_interp(x, y, self.x), decimal=10)
def test_fit_eval(self): """PiecewisePolynomial1DFit: Basic function fitting and evaluation using data from a known function.""" # Ignore test if SciPy version is below 0.7.0 try: import scipy.interpolate scipy.interpolate.PiecewisePolynomial except AttributeError: return interp = PiecewisePolynomial1DFit(max_degree=3) self.assertRaises(NotFittedError, interp, self.x) self.assertRaises(ValueError, interp.fit, [0, 0], [1, 2]) interp.fit(self.x[::2], self.y[::2]) y = interp(self.x) assert_almost_equal(y[5:-5], self.y[5:-5], decimal=10) # Fit a single data point interp.fit(self.x[0], self.y[0]) y = interp(self.x) assert_equal(y, np.tile(self.y[0], self.x.shape))
names += ' | ' + src['OName'] ra, dec = atca_cat[src['Name']].radec() if use_atca else \ (katpoint.deg2rad(src['_RAJ2000']), katpoint.deg2rad(src['_DEJ2000'])) tags_ra_dec = katpoint.construct_radec_target( ra, dec).add_tags('J2000 ' + src['Type']).description # Extract polarisation data for the current source from pol table pol_data = pol_table[pol_table['Name'] == src['Name']] pol_freqs_MHz = katpoint.lightspeed / (0.01 * pol_data['lambda']) / 1e6 pol_percent = pol_data['Pol'] # Remove duplicate frequencies and fit linear interpolator to data as function of frequency pol_freq, pol_perc = [], [] for freq in np.unique(pol_freqs_MHz): freqfind = (pol_freqs_MHz == freq) pol_freq.append(freq) pol_perc.append(pol_percent[freqfind].mean()) pol_interp = PiecewisePolynomial1DFit(max_degree=1).fit(pol_freq, pol_perc) # Look up source name in 1Jy catalogue and extract its flux density model flux_target = flux_cat['1Jy ' + src['Name']] if flux_target is None: flux_target = flux_cat['1Jy ' + src['Name'][:7]] flux_str = flux_target.flux_model.description if flux_target is not None else '' target_description = ', '.join((names, tags_ra_dec, flux_str)) target = katpoint.Target(target_description) # Evaluate polarised flux at expected centre frequency and filter on that pol_flux = target.flux_density(freq_MHz) * pol_interp(freq_MHz) / 100. if pol_flux < polflux_limit: print('%s skipped: polarised flux @ %d MHz: %.2f < %.2f' % (src['Name'], freq_MHz, pol_flux, polflux_limit)) continue src_strings.append(target_description + '\n') print(
def get(self, name, select=False, extract=True, **kwargs): """Sensor values interpolated to correlator data timestamps. Time selection is disabled by default, as this is a more advanced data extraction method typically called by library routines that want to operate on the full array of sensor values. For additional allowed parameters when extracting categorical data, see the docstring for :func:`sensor_to_categorical`. Parameters ---------- name : string Sensor name select : {False, True}, optional True if preset time selection will be applied to returned data extract : {True, False}, optional True if sensor data should be extracted from HDF5 file and cached categorical : {None, True, False}, optional Interpret sensor data as categorical or numerical (by default, data of type float is numerical and of any other type is categorical) interp_degree : int, optional Polynomial degree for interpolation of numerical data (default = 1) kwargs : dict, optional Additional parameters are passed to :func:`sensor_to_categorical` Returns ------- data : array or :class:`CategoricalData` or :class:`SensorData` object If extraction is disabled, this will be a :class:`SensorData` object for uncached sensors. If selection is enabled, this will be a 1-D array of values, one per selected timestamp. If selection is disabled, this will be a 1-D array of values (of the same length as the :attr:`timestamps` attribute) for numerical data, and a :class:`CategoricalData` object for categorical data. Raises ------ KeyError If sensor name was not found in cache and did not match virtual template """ try: # First try to load the actual sensor data from cache (remember to call base class here!) sensor_data = super(SensorCache, self).__getitem__(name) except KeyError: # Otherwise, iterate through virtual sensor templates and look for a match for pattern, create_sensor in self.virtual.iteritems(): # Expand variable names enclosed in braces to the relevant regular expression pattern = re.sub( '({[a-zA-Z_]\w*})', lambda m: '(?P<' + m.group(0)[1:-1] + '>[^//]+)', pattern) match = re.match(pattern, name) if match: # Call sensor creation function with extracted variables from sensor name sensor_data = create_sensor(self, name, **match.groupdict()) break else: raise KeyError( "Unknown sensor '%s' (does not match actual name or virtual template)" % (name, )) # If this is the first time this sensor is accessed, extract its data and store it in cache, if enabled if isinstance(sensor_data, SensorData) and extract: # Look up properties associated with this specific sensor self.props[name] = props = self.props.get(name, {}) # Look up properties associated with this class of sensor for key, val in self.props.iteritems(): if key[0] == '*' and name.endswith(key[1:]): props.update(val) # Any properties passed directly to this method takes precedence props.update(kwargs) # Clean up sensor data if non-empty if len(sensor_data) > 0: # Sort sensor events in chronological order and discard duplicates and unreadable sensor values sensor_data = remove_duplicates(sensor_data) # Explicitly cast status to string type, as k7_augment produced sensors with integer statuses sensor_data.data = np.atleast_1d(sensor_data[ sensor_data['status'].astype('|S7') != 'failure']) if len(sensor_data) == 0: sensor_data = dummy_sensor_data( name, value=props.get('initial_value'), dtype=sensor_data.dtype) logger.warning( "No usable data found for sensor '%s' - replaced with dummy data (%r)" % (name, sensor_data['value'][0])) # If this is the first time any sensor is accessed, obtain all data timestamps via indexer self.timestamps = self.timestamps[:] if not isinstance( self.timestamps, np.ndarray) else self.timestamps # Determine if sensor produces categorical or numerical data (by default, float data are non-categorical) categ = props.get('categorical', not np.issubdtype(sensor_data.dtype, np.float)) props['categorical'] = categ if categ: sensor_data = sensor_to_categorical(sensor_data['timestamp'], sensor_data['value'], self.timestamps, self.dump_period, **props) else: # Interpolate numerical data onto data timestamps (fallback option is linear interpolation) props['interp_degree'] = interp_degree = props.get( 'interp_degree', 1) sensor_timestamps = sensor_data['timestamp'] # Warn if sensor data will be extrapolated to start or end of data set with potentially bogus results if interp_degree > 0 and len(sensor_timestamps) > 1: if sensor_timestamps[0] > self.timestamps[0]: logger.warning(( "First data point for sensor '%s' only arrives %g seconds into data set" % (name, sensor_timestamps[0] - self.timestamps[0]) ) + " - extrapolation may lead to ridiculous values") if sensor_timestamps[-1] < self.timestamps[-1]: logger.warning(( "Last data point for sensor '%s' arrives %g seconds before end of data set" % (name, self.timestamps[-1] - sensor_timestamps[-1]) ) + " - extrapolation may lead to ridiculous values") if PiecewisePolynomial1DFit is not None: interp = PiecewisePolynomial1DFit(max_degree=interp_degree) interp.fit(sensor_timestamps, sensor_data['value']) sensor_data = interp(self.timestamps) else: if interp_degree != 1: logger.warning( 'Requested sensor interpolation with polynomial degree ' + str(interp_degree) + ' but scikits.fitting not installed - falling back to linear interpolation' ) sensor_data = _safe_linear_interp(sensor_timestamps, sensor_data['value'], self.timestamps) self[name] = sensor_data return sensor_data[self.keep] if select else sensor_data