Skip to content

litmus_rm.lightcurve

lightcurve.py

A handy object clas for lightcurves

HM 2024

T = np.linspace(0, 4 * np.pi, 128) module-attribute

Y = np.sin(T) * np.sqrt(2) module-attribute

E = np.random.poisson(100, size=(len(T))) / 100 * 1 module-attribute

lc = lightcurve(T, Y + np.random.randn(len(T)) * E, abs(E)) module-attribute

lc_calib = lc.normalize() module-attribute

lc_uncalib = lc_calib.unnormalize() module-attribute

lightcurve(T, Y, E=None)

Bases: object

A wrapper class for lightcurves. Construct /w array-like inputs for time, signal and error (optional) like: lightcurve(T, Y, E) or: lightcurve(T, Y) Which yields E=0 for all {T,Y}

Supports array-like addition and float-like addition / multiplication

Source code in src/litmus_rm/lightcurve.py
def __init__(self, T, Y, E=None):
    self.T = np.array(T, dtype=np.float64)
    self.Y = np.array(Y, dtype=np.float64)
    if E is None:
        self.E = np.zeros_like(T)
    else:
        self.E = E

    self._data = np.vstack(self.values()).T

    self._norm_mean, self._norm_amp = 0.0, 1.0
    self.normalized = False
T = np.array(T, dtype=(np.float64)) instance-attribute
Y = np.array(Y, dtype=(np.float64)) instance-attribute
E = np.zeros_like(T) instance-attribute
normalized = False instance-attribute
keys() -> tuple[str, str, str]

Returns the string-like names of the lightcurve's attributes

Source code in src/litmus_rm/lightcurve.py
def keys(self) -> tuple[str, str, str]:
    """
    Returns the string-like names of the lightcurve's attributes
    """
    return ("T", "Y", "E")
values() -> tuple[_types.ArrayN, _types.ArrayN, _types.ArrayN]

Returns the lightcurves' value series' in the order of keys

Source code in src/litmus_rm/lightcurve.py
def values(self) -> tuple[_types.ArrayN, _types.ArrayN, _types.ArrayN]:
    """
    Returns the lightcurves' value series' in the order of keys
    """
    return [self[key] for key in self.keys()]
normalize() -> _types.Self

Esimates the mean and amplitude of the lighturve assuming uncorrelated measurements Returns a lightcurve object with this normalization

Source code in src/litmus_rm/lightcurve.py
def normalize(self) -> _types.Self:
    """
    Esimates the mean and amplitude of the lighturve assuming uncorrelated measurements
    Returns a lightcurve object with this normalization
    """

    if self.normalized: return self

    # Check if have errorbars
    no_errs = False
    E = self.E
    if max(E) == 0:
        no_errs = True
        E = np.ones_like(self.E)

    # Get initial guess assuming no scatter
    w = E ** -2
    mean0 = np.average(self.Y, weights=w)
    var0 = np.average((self.Y - mean0) ** 2, weights=w)

    if no_errs:
        meanbest, varbest = mean0, var0
    else:
        L = lambda X: ((self.Y - X[0]) ** 2 / (self.E ** 2 + X[1]) + np.log(self.E ** 2 + X[1])).sum()
        meanbest, varbest = optimize.minimize(L, np.array([mean0, var0]), method='Nelder-Mead').x

    # Make and return copy
    out = copy(self)
    out -= meanbest
    out /= np.sqrt(varbest)

    # If any errors, revert to simple estiamte
    if np.any(np.isnan(out._data)):
        meanbest, varbest = mean0, var0
        out = copy(self)
        out -= meanbest
        out /= np.sqrt(varbest)

    # Store normalizing values for later
    out._norm_mean = meanbest
    out._norm_amp = np.sqrt(varbest)

    out.normalized = True

    return out
unnormalize() -> _types.Self

Reverses the effects of lightcurve.normalize(). Returns a lightcurve object with mean and amplitude prior to normalize()

Source code in src/litmus_rm/lightcurve.py
def unnormalize(self) -> _types.Self:
    """
    Reverses the effects of lightcurve.normalize().
    Returns a lightcurve object with mean and amplitude prior to normalize()
    """
    out = copy(self)
    out *= self._norm_amp
    out += self._norm_mean
    out._norm_amp = 1.0
    out._norm_mean = 0.0
    out.normalized = False
    return out
delayed_copy(lag=0.0, Tmin=None, Tmax=None) -> _types.Self

Returns a copy subsampled to only datapoints in the domain T in [Tmin,Tmax] and offset by lag

Source code in src/litmus_rm/lightcurve.py
def delayed_copy(self, lag=0.0, Tmin=None, Tmax=None) -> _types.Self:
    """
    Returns a copy subsampled to only datapoints in the domain T in [Tmin,Tmax] and offset by lag
    """
    if Tmin is None: Tmin = self.T.min()
    if Tmax is None: Tmax = self.T.max()
    I = np.where((self.T + lag > Tmin) * (self.T + lag < Tmax))[0]

    return (lightcurve(T=self.T[I] + lag,
                       Y=self.Y[I],
                       E=self.E[I]
                       ))
trimmed_copy(Tmin=None, Tmax=None) -> _types.Self

Returns a copy subsampled to only datapoints in the domain T in [Tmin,Tmax]

Source code in src/litmus_rm/lightcurve.py
def trimmed_copy(self, Tmin=None, Tmax=None) -> _types.Self:
    """
    Returns a copy subsampled to only datapoints in the domain T in [Tmin,Tmax]
    """
    return self.delayed_copy(0, Tmin, Tmax)
concatenate(other)
Source code in src/litmus_rm/lightcurve.py
def concatenate(self, other):
    T1, T2 = self.T, other.T
    Y1, Y2 = self.Y, other.Y
    E1, E2 = self.E, other.E
    T, Y, E = np.concatenate([T1, T2]), np.concatenate([Y1, Y2]), np.concatenate([E1, E2])
    return lightcurve(T, Y, E)
plot(axis=None, show=True, **kwargs) -> None

Plots an errorbar series to a matplotlib axis. If show=True, will plt.show() after plotting. If axis is None, will create a new figure. Pass in any plotting kwargs for plt.errorbar at **kwargs

Source code in src/litmus_rm/lightcurve.py
def plot(self, axis=None, show=True, **kwargs) -> None:
    """
    Plots an errorbar series to a matplotlib axis.
    If show=True, will plt.show() after plotting.
    If axis is None, will create a new figure.
    Pass in any plotting kwargs for plt.errorbar at **kwargs
    """
    if axis is None:
        plt.figure()
        axis = plt.gca()
        axis.set_xlabel("T")
        axis.set_ylabel("Y")
    axis.errorbar(self.T, self.Y, self.E, fmt='none', **kwargs)

    if show: plt.show()
copy()
Source code in src/litmus_rm/lightcurve.py
def copy(self):
    return copy(self)

lightcurve_iter(base_lc: lightcurve, r: float = np.exp(-1), Evary: bool = True)

Bases: lightcurve

An extension of the lightcurve class that support bootstrapping. Call like lightcurve_iter(base_lightcurve, r, Evary)

Where 'r' is the subsampling fraction (default 1/e per bootstrapping) Evary is a sign to indicate whether we want to also vary measurements within errorbars

Source code in src/litmus_rm/lightcurve.py
def __init__(self, base_lc: lightcurve, r: float = np.exp(-1), Evary: bool = True):
    T, Y, E = base_lc.values()
    super().__init__(T, Y, E)
    self.r = r
    self.Evary = Evary

    self._N = self.N
    self._T = self.T
    self._Y = self.Y
    self._E = self.E

    self.subsample()
r = r instance-attribute
Evary = Evary instance-attribute
subsample() -> None

Subsamples and (if self.Evary==True) shimmies the Y values within measurement uncertainty Updates self values

Source code in src/litmus_rm/lightcurve.py
def subsample(self) -> None:
    """
    Subsamples and (if self.Evary==True) shimmies the Y values within measurement uncertainty
    Updates self values
    """
    n = int(self._N * self.r)
    I = np.random.choice(np.arange(self._N), n, replace=False)
    self.T, self.Y, self.E = self._T[I], self._Y[I], self._E[I]
    if self.Evary: self.Y += np.random.randn(self.N) * self.E