import numpy as np def design_matrix(t, frequency, dy=None, bias=True, nterms=1): """Compute the Lomb-Scargle design matrix at the given frequency This is the matrix X such that the periodic model at the given frequency can be expressed :math:`\\hat{y} = X \\theta`. Parameters ---------- t : array-like, shape=(n_times,) times at which to compute the design matrix frequency : float frequency for the design matrix dy : float or array-like, optional data uncertainties: should be broadcastable with `t` bias : bool (default=True) If true, include a bias column in the matrix nterms : int (default=1) Number of Fourier terms to include in the model Returns ------- X : ndarray, shape=(n_times, n_parameters) The design matrix, where n_parameters = bool(bias) + 2 * nterms """ t = np.asarray(t) frequency = np.asarray(frequency) if t.ndim != 1: raise ValueError("t should be one dimensional") if frequency.ndim != 0: raise ValueError("frequency must be a scalar") if nterms == 0 and not bias: raise ValueError("cannot have nterms=0 and no bias") if bias: cols = [np.ones_like(t)] else: cols = [] for i in range(1, nterms + 1): cols.append(np.sin(2 * np.pi * i * frequency * t)) cols.append(np.cos(2 * np.pi * i * frequency * t)) XT = np.vstack(cols) if dy is not None: XT /= dy return np.transpose(XT) def periodic_fit(t, y, dy, frequency, t_fit, center_data=True, fit_mean=True, nterms=1): """Compute the Lomb-Scargle model fit at a given frequency Parameters ---------- t, y, dy : float or array-like The times, observations, and uncertainties to fit frequency : float The frequency at which to compute the model t_fit : float or array-like The times at which the fit should be computed center_data : bool (default=True) If True, center the input data before applying the fit fit_mean : bool (default=True) If True, include the bias as part of the model nterms : int (default=1) The number of Fourier terms to include in the fit Returns ------- y_fit : ndarray The model fit evaluated at each value of t_fit """ t, y, frequency = map(np.asarray, (t, y, frequency)) if dy is None: dy = np.ones_like(y) else: dy = np.asarray(dy) t_fit = np.asarray(t_fit) if t.ndim != 1: raise ValueError("t, y, dy should be one dimensional") if t_fit.ndim != 1: raise ValueError("t_fit should be one dimensional") if frequency.ndim != 0: raise ValueError("frequency should be a scalar") if center_data: w = dy ** -2.0 y_mean = np.dot(y, w) / w.sum() y = (y - y_mean) else: y_mean = 0 X = design_matrix(t, frequency, dy=dy, bias=fit_mean, nterms=nterms) theta_MLE = np.linalg.solve(np.dot(X.T, X), np.dot(X.T, y / dy)) X_fit = design_matrix(t_fit, frequency, bias=fit_mean, nterms=nterms) return y_mean + np.dot(X_fit, theta_MLE)