Source code for stellarphot.plotting.multi_night_plots

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from astropy.stats import mad_std
from astropy.time import Time
from astropy.timeseries import LombScargle

__all__ = ["plot_magnitudes", "multi_night"]


[docs] def plot_magnitudes( mags=None, errors=None, times=None, night=None, alpha=0.25, y_range=None, ): """ Plot one night of magnitude data for one source, overlaying a rolling mean and indication of mean/deviation. Parameters ---------- mags : array of floats, optional Magnitudes of source. errors : array of floats, optional Errors on magnitudes. times : array-like, optional Times of observations. night : float, optional Night of observations. alpha : float, optional Alpha value for error bars. Default is 0.25. y_range : tuple Range of y-axis. Default is None. Returns ------- mean : float Mean magnitude of source. std : float Standard deviation of magnitudes. """ mean = np.nanmean(mags) std = np.nanstd(mags) working_times = times plt.errorbar( working_times, mags, yerr=errors, fmt="o", alpha=alpha, label=f"night: {night}", ) plt.xlim(working_times.min(), working_times.max()) plt.plot( plt.xlim(), [mean, mean], "k--", ) plt.plot(plt.xlim(), [mean + std, mean + std], "k:") plt.plot(plt.xlim(), [mean - std, mean - std], "k:") plt.plot( pd.rolling_mean(working_times, 20, center=True), pd.rolling_mean(mags, 20, center=True), color="gray", linewidth=3, ) if y_range: plt.ylim(y_range) else: # Make sure plot range is at least 0.1 mag... min_range = 0.1 ylim = plt.ylim() if ylim[1] - ylim[0] < min_range: plt.ylim(mean - min_range / 2, mean + min_range / 2) ylim = plt.ylim() # Reverse vertical axis so brighter is higher plt.ylim((ylim[1], ylim[0])) # # Add marker indicating brightness of star. # size = 1000./(mean - ref_mag + 0.1)**2 # plt.scatter([0.8*(plt.xlim()[1]-plt.xlim()[0]) + plt.xlim()[0]], # [0.8*(plt.ylim()[1] - plt.ylim()[0]) + plt.ylim()[0]], # c='red', marker='o', s=size) # plt.legend() calendar_date = Time(night, format="jd", out_subfmt="date") calendar_date.format = "iso" plt.title(f"night {calendar_date}") plt.xlabel("time (days)") return mean, std
[docs] def multi_night( sources, unique_nights, night, brightest_mag, mags, mag_err, uniform_ylim=True ): """ Plot magnitude vs time data for several sources over several nights. Parameters ---------- sources : list List of `~stellarphot.source.Source` objects. unique_nights : list List of unique nights. night : array-like Array of nights. brightest_mag : float Brightest magnitude of sources. mags : array-like Array of magnitudes. mag_err : array-like Array of magnitude errors. uniform_ylim : bool, optional If True, use the median and median absolute deviation of the magnitudes to set the y-axis range for each source. Default is True. Returns ------- None Generates a plot of magnitude vs time for each source. """ number_of_nights = len(unique_nights) for source in sources: f = plt.figure(figsize=(5 * number_of_nights, 5)) night_means = [] night_stds = [] night_bins = [] source_mags = mags[source.id - 1] if uniform_ylim: # Use median to handle outliers. source_median = np.median(source_mags[np.isfinite(source_mags)]) # Use median absolute deviation to get measure of scatter. # Helps avoid extremely points. source_variation = 3 * mad_std(source_mags[np.isfinite(source_mags)]) # Ensure y range will be at least 0.2 magnitudes if source_variation < 0.1: half_range = 0.1 else: half_range = source_variation y_range = (source_median - half_range, source_median + half_range) else: # Empty if this option wasn't chosen so that automatic limits # will be used. y_range = [] last_axis = None for i, this_night in enumerate(unique_nights): last_axis = plt.subplot(1, number_of_nights + 1, i + 1, sharey=last_axis) night_mask = night == this_night night_mean, night_std = plot_magnitudes( mags=mags[source.id - 1][night_mask], errors=mag_err[source.id - 1][night_mask], times=source.bjd_tdb[night_mask], night=this_night, y_range=y_range, ) night_means.append(night_mean) night_stds.append(night_std) night_bins.append(this_night) plt.subplot(1, number_of_nights + 1, number_of_nights + 1) if uniform_ylim: f.subplots_adjust(wspace=0) plt.setp([a.get_yticklabels() for a in f.axes[1:]], visible=False) # Plot indicators of variation, and information about this source. # For simplicity, make the x and y range of this plot be 0 to 1. x = np.array([0.0, 1]) y = x # Add invisible line to make plot. plt.plot(x, y, alpha=0, label=f"source {source.id}") night_means = np.array(night_means) # Plot bar proportional to Lomb-Scargle power. bad_mags = np.isnan(mags[source.id - 1]) | np.isinf(mags[source.id - 1]) bad_errs = np.isnan(mag_err[source.id - 1]) | np.isinf(mag_err[source.id - 1]) bads = bad_mags | bad_errs good_mags = ~bads ls = LombScargle( source.bjd_tdb[good_mags], mags[source.id - 1][good_mags], mag_err[source.id - 1][good_mags], ) freqs, power = ls.autopower(nyquist_factor=100, samples_per_peak=10) max_pow = power.max() # print(source, max_pow) if max_pow > 0.5: color = "green" elif max_pow > 0.4: color = "cyan" else: color = "gray" bar_x = 0.25 plt.plot( [bar_x, bar_x], [0, max_pow], color=color, linewidth=10, label="LS power" ) plt.legend() # Add dot for magnitude of star. size = 10000.0 / np.abs(10 ** ((source_median - brightest_mag) / 2.5)) plt.scatter([0.8], [0.2], c="red", marker="o", s=size) plt.ylim(0, 1)