def test_fit_eval_diagcov(self): """GaussianFit (shared stdev): Basic function fitting and evaluation using data from a known function.""" interp = GaussianFit(self.init_mean, self.init_std, self.init_height) interp.fit(self.x, self.y) y = interp(self.x) assert_almost_equal(interp.mean, self.true_mean, decimal=7) assert_almost_equal(interp.std, self.true_std, decimal=7) assert_almost_equal(interp.height, self.true_height, decimal=7) assert_almost_equal(y, self.y, decimal=7)
def test_fit_eval_diagcov(self): """GaussianFit (independent stdevs): Basic fitting and evaluation.""" interp = GaussianFit(self.init_mean, self.init_std, self.init_height) interp.fit(self.x, self.y) y = interp(self.x) assert_almost_equal(interp.mean, self.true_mean, decimal=7) assert_almost_equal(interp.std, self.true_std, decimal=7) assert_almost_equal(interp.height, self.true_height, decimal=7) assert_almost_equal(y, self.y, decimal=7)
def setUp(self): self.true_mean, self.true_std, self.true_height = [0, 0], 1., 0. true_gauss = GaussianFit(self.true_mean, self.true_std, self.true_height) self.x = 7 * np.random.randn(2, 80) self.y = true_gauss(self.x) self.init_mean, self.init_std, self.init_height = [0, 0], 1, 0
def __init__(self, center, width, height): ScatterFit.__init__(self) if not np.isscalar(width): width = np.atleast_1d(width) self._interp = GaussianFit(center, fwhm_to_sigma(width), height) self.center = self._interp.mean self.width = sigma_to_fwhm(self._interp.std) self.height = self._interp.height self.expected_width = width # Initial guess for radius of first null # XXX: POTENTIAL TWEAK self.radius_first_null = 1.3 * np.mean(self.expected_width) # Beam initially unrefined and invalid self.refined = 0 self.is_valid = False self.std_center = self.std_width = self.std_height = None
def setUp(self): # For a more challenging fit, move the true mean away from the origin, i.e. away from the region # being randomly sampled in self.x. Fitting a Gaussian to a segment that does not contain a clear peak # works fine if the fit is done to the log of the data, but fails in the linear domain. self.true_mean, self.true_std, self.true_height = [0., 0.], [3., 5.], 4. true_gauss = GaussianFit(self.true_mean, self.true_std, self.true_height) self.x = 7. * np.random.randn(2, 300) self.y = true_gauss(self.x) self.init_mean, self.init_std, self.init_height = [3., -2.], [1., 1.], 1.
def test_cov_params(self): """GaussianFit (independent stdevs): Obtain sample statistics of params and compare to calculated cov matrix.""" interp = GaussianFit(self.init_mean, self.init_std, self.init_height) true_params = np.r_[self.true_mean, self.true_height, self.true_std] std_y = 0.1 M = 200 param_set = np.zeros((len(true_params), M)) for n in range(M): interp.fit(self.x, self.y + std_y * np.random.randn(len(self.y)), std_y) param_set[:, n] = np.r_[interp.mean, interp.height, interp.std] mean_params = param_set.mean(axis=1) norm_params = param_set - mean_params[:, np.newaxis] cov_params = np.dot(norm_params, norm_params.T) / M estm_std_params = np.sqrt(np.diag(cov_params)) std_params = np.r_[interp.std_mean, interp.std_height, interp.std_std] self.assertTrue((np.abs(mean_params - true_params) / std_params < 0.5).all(), "Sample mean parameter vector differs too much from true value") # Only check diagonal of cov matrix - the rest is probably affected by linearisation self.assertTrue((np.abs(estm_std_params - std_params) / std_params < 0.2).all(), "Sample parameter standard deviation differs too much from expected one")
def test_cov_params(self): """GaussianFit (independent stdevs): Obtain sample statistics of parameters and compare to calculated covariance matrix.""" interp = GaussianFit(self.init_mean, self.init_std, self.init_height) true_params = np.r_[self.true_mean, self.true_height, self.true_std] std_y = 0.1 M = 200 param_set = np.zeros((len(true_params), M)) for n in range(M): interp.fit(self.x, self.y + std_y * np.random.randn(len(self.y)), std_y) param_set[:, n] = np.r_[interp.mean, interp.height, interp.std] mean_params = param_set.mean(axis=1) norm_params = param_set - mean_params[:, np.newaxis] cov_params = np.dot(norm_params, norm_params.T) / M estm_std_params = np.sqrt(np.diag(cov_params)) std_params = np.r_[interp.std_mean, interp.std_height, interp.std_std] self.assertTrue((np.abs(mean_params - true_params) / std_params < 0.5).all(), "Sample mean parameter vector differs too much from true value") # Only check diagonal of cov matrix - the rest is probably affected by linearisation self.assertTrue((np.abs(estm_std_params - std_params) / std_params < 0.2).all(), "Sample parameter standard deviation differs too much from expected one")
def setUp(self): self.true_mean = [0, 0] self.true_std = np.sqrt(10) self.true_height = 4 true_gauss = GaussianFit(self.true_mean, self.true_std, self.true_height) self.x = 7 * np.random.randn(2, 80) self.y = true_gauss(self.x) self.init_mean = [3, -2] self.init_std = 1 self.init_height = 1
class BeamPatternFit(ScatterFit): """Fit analytic beam pattern to total power data defined on 2-D plane. This fits a two-dimensional Gaussian curve (with diagonal covariance matrix) to total power data as a function of 2-D coordinates. The Gaussian bump represents an antenna beam pattern convolved with a point source. Parameters ---------- center : sequence of 2 floats Initial guess of 2-element beam center, in target coordinate units width : sequence of 2 floats, or float Initial guess of single beamwidth for both dimensions, or 2-element beamwidth vector, expressed as FWHM in units of target coordinates height : float Initial guess of beam pattern amplitude or height Attributes ---------- expected_width : real array, shape (2,), or float Initial guess of beamwidth, saved as expected width for checks radius_first_null : float Radius of first null in beam in target coordinate units (stored here for convenience, but not calculated internally) refined : int Number of scan-based baselines used to refine beam (0 means unrefined) is_valid : bool True if beam parameters are within reasonable ranges after fit std_center : array of float, shape (2,) Standard error of beam center, only set after :func:`fit` std_width : array of float, shape (2,), or float Standard error of beamwidth(s), only set after :func:`fit` std_height : float Standard error of beam height, only set after :func:`fit` """ def __init__(self, center, width, height): ScatterFit.__init__(self) if not np.isscalar(width): width = np.atleast_1d(width) self._interp = GaussianFit(center, fwhm_to_sigma(width), height) self.center = self._interp.mean self.width = sigma_to_fwhm(self._interp.std) self.height = self._interp.height self.expected_width = width # Initial guess for radius of first null # XXX: POTENTIAL TWEAK self.radius_first_null = 1.3 * np.mean(self.expected_width) # Beam initially unrefined and invalid self.refined = 0 self.is_valid = False self.std_center = self.std_width = self.std_height = None def fit(self, x, y, std_y=1.0): """Fit a beam pattern to data. The center, width and height of the fitted beam pattern (and their standard errors) can be obtained from the corresponding member variables after this is run. Parameters ---------- x : array-like, shape (2, N) Sequence of 2-dimensional target coordinates (as column vectors) y : array-like, shape (N,) Sequence of corresponding total power values to fit std_y : float or array-like, shape (N,), optional Measurement error or uncertainty of `y` values, expressed as standard deviation in units of `y` """ self._interp.fit(x, y, std_y) self.center = self._interp.mean self.width = sigma_to_fwhm(self._interp.std) self.height = self._interp.height self.std_center = self._interp.std_mean self.std_width = sigma_to_fwhm(self._interp.std_std) self.std_height = self._interp.std_height self.is_valid = not any(np.isnan(self.center)) and self.height > 0. # XXX: POTENTIAL TWEAK norm_width = self.width / self.expected_width self.is_valid &= all(norm_width > 0.9) and all(norm_width < 1.25) def __call__(self, x): """Evaluate fitted beam pattern function on new target coordinates. Parameters ---------- x : array-like, shape (2, M) Sequence of 2-dimensional target coordinates (as column vectors) Returns ------- y : array, shape (M,) Sequence of total power values representing fitted beam """ return self._interp(x)