timewise 0.5.4__py3-none-any.whl → 1.0.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- timewise/__init__.py +1 -5
- timewise/backend/__init__.py +6 -0
- timewise/backend/base.py +36 -0
- timewise/backend/filesystem.py +80 -0
- timewise/chunking.py +50 -0
- timewise/cli.py +117 -11
- timewise/config.py +34 -0
- timewise/io/__init__.py +1 -0
- timewise/io/config.py +64 -0
- timewise/io/download.py +302 -0
- timewise/io/stable_tap.py +121 -0
- timewise/plot/__init__.py +3 -0
- timewise/plot/diagnostic.py +242 -0
- timewise/plot/lightcurve.py +112 -0
- timewise/plot/panstarrs.py +260 -0
- timewise/plot/sdss.py +109 -0
- timewise/process/__init__.py +2 -0
- timewise/process/config.py +30 -0
- timewise/process/interface.py +143 -0
- timewise/process/keys.py +10 -0
- timewise/process/stacking.py +310 -0
- timewise/process/template.yml +49 -0
- timewise/query/__init__.py +6 -0
- timewise/query/base.py +45 -0
- timewise/query/positional.py +40 -0
- timewise/tables/__init__.py +10 -0
- timewise/tables/allwise_p3as_mep.py +22 -0
- timewise/tables/base.py +9 -0
- timewise/tables/neowiser_p1bs_psd.py +22 -0
- timewise/types.py +30 -0
- timewise/util/backoff.py +12 -0
- timewise/util/csv_utils.py +12 -0
- timewise/util/error_threading.py +70 -0
- timewise/util/visits.py +33 -0
- timewise-1.0.0a1.dist-info/METADATA +205 -0
- timewise-1.0.0a1.dist-info/RECORD +39 -0
- timewise-1.0.0a1.dist-info/entry_points.txt +3 -0
- timewise/big_parent_sample.py +0 -106
- timewise/config_loader.py +0 -157
- timewise/general.py +0 -52
- timewise/parent_sample_base.py +0 -89
- timewise/point_source_utils.py +0 -68
- timewise/utils.py +0 -558
- timewise/wise_bigdata_desy_cluster.py +0 -1407
- timewise/wise_data_base.py +0 -2027
- timewise/wise_data_by_visit.py +0 -672
- timewise/wise_flux_conversion_correction.dat +0 -19
- timewise-0.5.4.dist-info/METADATA +0 -56
- timewise-0.5.4.dist-info/RECORD +0 -17
- timewise-0.5.4.dist-info/entry_points.txt +0 -3
- {timewise-0.5.4.dist-info → timewise-1.0.0a1.dist-info}/WHEEL +0 -0
- {timewise-0.5.4.dist-info → timewise-1.0.0a1.dist-info}/licenses/LICENSE +0 -0
timewise/wise_data_by_visit.py
DELETED
|
@@ -1,672 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import pandas as pd
|
|
3
|
-
import numpy as np
|
|
4
|
-
import logging
|
|
5
|
-
from scipy import stats
|
|
6
|
-
import matplotlib.pyplot as plt
|
|
7
|
-
from matplotlib.lines import Line2D
|
|
8
|
-
from matplotlib.markers import MarkerStyle
|
|
9
|
-
from matplotlib.transforms import Affine2D
|
|
10
|
-
|
|
11
|
-
from timewise.wise_data_base import WISEDataBase
|
|
12
|
-
from timewise.utils import get_excess_variance
|
|
13
|
-
|
|
14
|
-
logger = logging.getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class WiseDataByVisit(WISEDataBase):
|
|
18
|
-
"""
|
|
19
|
-
WISEData class to bin lightcurve by visits. The visits typically consist of some tens of observations.
|
|
20
|
-
The individual visits are separated by about six months. The mean flux for one visit is calculated by the
|
|
21
|
-
weighted mean of the data. The error on that mean is calculated by the root-mean-squared and corrected by the
|
|
22
|
-
t-value. Outliers per visit are identified if they are more than 20 times the rms away from the mean.
|
|
23
|
-
In addition to the attributes of :class:`timewise.WISEDataBase` this class has the following attributes:
|
|
24
|
-
|
|
25
|
-
:param clean_outliers_when_binning: whether to remove outliers by brightness when binning
|
|
26
|
-
:type clean_outliers_when_binning: bool
|
|
27
|
-
:param mean_key: the key for the mean
|
|
28
|
-
:type mean_key: str
|
|
29
|
-
:param median_key: the key for the median
|
|
30
|
-
:type median_key: str
|
|
31
|
-
:param rms_key: the key for the rms
|
|
32
|
-
:type rms_key: str
|
|
33
|
-
:param upper_limit_key: the key for the upper limit
|
|
34
|
-
:type upper_limit_key: str
|
|
35
|
-
:param Npoints_key: the key for the number of points
|
|
36
|
-
:type Npoints_key: str
|
|
37
|
-
:param zeropoint_key_ext: the key for the zeropoint
|
|
38
|
-
:type zeropoint_key_ext: str
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
mean_key = '_mean'
|
|
42
|
-
median_key = '_median'
|
|
43
|
-
rms_key = '_rms'
|
|
44
|
-
upper_limit_key = '_ul'
|
|
45
|
-
Npoints_key = '_Npoints'
|
|
46
|
-
zeropoint_key_ext = '_zeropoint'
|
|
47
|
-
|
|
48
|
-
flux_error_factor = {
|
|
49
|
-
"W1": 1.6,
|
|
50
|
-
"W2": 1.2
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
def __init__(
|
|
54
|
-
self,
|
|
55
|
-
base_name,
|
|
56
|
-
parent_sample_class,
|
|
57
|
-
min_sep_arcsec,
|
|
58
|
-
n_chunks,
|
|
59
|
-
clean_outliers_when_binning=True,
|
|
60
|
-
multiply_flux_error=True
|
|
61
|
-
):
|
|
62
|
-
"""
|
|
63
|
-
Constructor of the WISEDataByVisit class.
|
|
64
|
-
|
|
65
|
-
:param base_name: the base name of the data directory
|
|
66
|
-
:type base_name: str
|
|
67
|
-
:param parent_sample_class: the parent sample class
|
|
68
|
-
:type parent_sample_class: ParentSampleBase
|
|
69
|
-
:param min_sep_arcsec: query region around source for positional query
|
|
70
|
-
:type min_sep_arcsec: float
|
|
71
|
-
:param n_chunks: number of chunks to split the sample into
|
|
72
|
-
:type n_chunks: int
|
|
73
|
-
:param clean_outliers_when_binning: if True, clean outliers when binning
|
|
74
|
-
:type clean_outliers_when_binning: bool
|
|
75
|
-
"""
|
|
76
|
-
super().__init__(base_name, parent_sample_class, min_sep_arcsec, n_chunks)
|
|
77
|
-
self.clean_outliers_when_binning = clean_outliers_when_binning
|
|
78
|
-
self.multiply_flux_error = multiply_flux_error
|
|
79
|
-
|
|
80
|
-
def calculate_epochs(self, f, e, visit_mask, counts, remove_outliers, outlier_mask=None):
|
|
81
|
-
"""
|
|
82
|
-
Calculates the binned epochs of a lightcurve.
|
|
83
|
-
|
|
84
|
-
:param f: the fluxes
|
|
85
|
-
:type f: np.array
|
|
86
|
-
:param e: the flux errors
|
|
87
|
-
:type e: np.array
|
|
88
|
-
:param visit_mask: the visit mask
|
|
89
|
-
:type visit_mask: np.array
|
|
90
|
-
:param counts: the counts
|
|
91
|
-
:type counts: np.array
|
|
92
|
-
:param remove_outliers: whether to remove outliers
|
|
93
|
-
:type remove_outliers: bool
|
|
94
|
-
:param outlier_mask: the outlier mask
|
|
95
|
-
:type outlier_mask: np.array
|
|
96
|
-
:return: the epoch
|
|
97
|
-
:rtype: float
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
if len(f) == 0:
|
|
101
|
-
return [], [], [], [], [], []
|
|
102
|
-
|
|
103
|
-
u_lims = pd.isna(e)
|
|
104
|
-
nan_mask = pd.isna(f)
|
|
105
|
-
|
|
106
|
-
# --------------------- remove outliers in the bins ---------------------- #
|
|
107
|
-
|
|
108
|
-
# if we do not want to clean outliers just set the threshold to infinity
|
|
109
|
-
outlier_thresh = np.inf if not remove_outliers else 20
|
|
110
|
-
|
|
111
|
-
# set up empty masks
|
|
112
|
-
outlier_mask = np.array([False] * len(f)) if outlier_mask is None else outlier_mask
|
|
113
|
-
median = np.nan
|
|
114
|
-
u = np.nan
|
|
115
|
-
use_mask = None
|
|
116
|
-
n_points = counts
|
|
117
|
-
|
|
118
|
-
# set up dummy values for number of remaining outliers
|
|
119
|
-
n_remaining_outlier = np.inf
|
|
120
|
-
|
|
121
|
-
# --------------------- flag upper limits ---------------------- #
|
|
122
|
-
bin_n_ulims = np.bincount(visit_mask, weights=u_lims, minlength=len(counts))
|
|
123
|
-
bin_ulim_bool = (counts - bin_n_ulims) == 0
|
|
124
|
-
use_mask_ul = ~u_lims | (u_lims & bin_ulim_bool[visit_mask])
|
|
125
|
-
|
|
126
|
-
n_loops = 0
|
|
127
|
-
|
|
128
|
-
# recalculate uncertainty and median as long as no outliers left
|
|
129
|
-
while n_remaining_outlier > 0:
|
|
130
|
-
|
|
131
|
-
# make a mask of values to use
|
|
132
|
-
use_mask = ~outlier_mask & use_mask_ul & ~nan_mask
|
|
133
|
-
n_points = np.bincount(visit_mask, weights=use_mask)
|
|
134
|
-
zero_points_mask = n_points == 0
|
|
135
|
-
|
|
136
|
-
# ------------------------- calculate median ------------------------- #
|
|
137
|
-
median = np.zeros_like(counts, dtype=float)
|
|
138
|
-
visits_at_least_one_point = np.unique(visit_mask[~zero_points_mask[visit_mask]])
|
|
139
|
-
visits_zero_points = np.unique(visit_mask[zero_points_mask[visit_mask]])
|
|
140
|
-
median[visits_at_least_one_point] = np.array([
|
|
141
|
-
np.median(f[(visit_mask == i) & use_mask]) for i in visits_at_least_one_point
|
|
142
|
-
])
|
|
143
|
-
median[visits_zero_points] = np.nan
|
|
144
|
-
|
|
145
|
-
# median is NaN for visits with 0 detections, (i.e. detections in one band and not the other)
|
|
146
|
-
# if median is NaN for other visits raise Error
|
|
147
|
-
if np.any(np.isnan(median[n_points > 0])):
|
|
148
|
-
nan_indices = np.where(np.isnan(median))[0]
|
|
149
|
-
msg = ""
|
|
150
|
-
for inan_index in nan_indices:
|
|
151
|
-
nanf = f[visit_mask == inan_index]
|
|
152
|
-
msg += f"median is nan for {inan_index}th bin\n{nanf}\n\n"
|
|
153
|
-
raise ValueError(msg)
|
|
154
|
-
|
|
155
|
-
# --------------------- calculate uncertainty ---------------------- #
|
|
156
|
-
mean_deviation = np.bincount(
|
|
157
|
-
visit_mask[use_mask],
|
|
158
|
-
weights=(f[use_mask] - median[visit_mask[use_mask]]) ** 2,
|
|
159
|
-
minlength=len(counts)
|
|
160
|
-
)
|
|
161
|
-
one_points_mask = n_points <= 1
|
|
162
|
-
# calculate standard deviation
|
|
163
|
-
std = np.zeros_like(counts, dtype=float)
|
|
164
|
-
std[~one_points_mask] = (
|
|
165
|
-
np.sqrt(mean_deviation[~one_points_mask])
|
|
166
|
-
/ (n_points[~one_points_mask] - 1)
|
|
167
|
-
* stats.t.interval(0.68, df=n_points[~one_points_mask] - 1)[1]
|
|
168
|
-
# for visits with small number of detections we have to correct according to the t distribution
|
|
169
|
-
)
|
|
170
|
-
std[one_points_mask] = -np.inf
|
|
171
|
-
|
|
172
|
-
# calculate the propagated errors of the single exposure measurements
|
|
173
|
-
single_exp_measurement_errors = np.sqrt(np.bincount(
|
|
174
|
-
visit_mask[use_mask],
|
|
175
|
-
weights=e[use_mask] ** 2,
|
|
176
|
-
minlength=len(counts)
|
|
177
|
-
))
|
|
178
|
-
e_meas = np.zeros_like(std, dtype=float)
|
|
179
|
-
e_meas[~zero_points_mask] = single_exp_measurement_errors[n_points > 0] / n_points[n_points > 0]
|
|
180
|
-
e_meas[zero_points_mask] = np.nan
|
|
181
|
-
# take the maximum value of the measured single exposure errors and the standard deviation
|
|
182
|
-
u = np.maximum(std, e_meas)
|
|
183
|
-
|
|
184
|
-
# calculate 90% confidence interval
|
|
185
|
-
u70 = np.zeros_like(counts, dtype=float)
|
|
186
|
-
u70[one_points_mask] = 1e-10
|
|
187
|
-
visits_at_least_two_point = np.unique(visit_mask[~one_points_mask[visit_mask]])
|
|
188
|
-
u70[visits_at_least_two_point] = np.array([
|
|
189
|
-
np.quantile(abs(f[(visit_mask == i) & use_mask] - median[i]), .7, method="interpolated_inverted_cdf")
|
|
190
|
-
for i in visits_at_least_two_point
|
|
191
|
-
])
|
|
192
|
-
|
|
193
|
-
# --------------------- remove outliers in the bins ---------------------- #
|
|
194
|
-
remaining_outliers = (abs(median[visit_mask] - f) > outlier_thresh * u70[visit_mask]) & ~outlier_mask
|
|
195
|
-
outlier_mask |= remaining_outliers
|
|
196
|
-
n_remaining_outlier = sum(remaining_outliers) if remove_outliers else 0
|
|
197
|
-
# setting remaining_outliers to 0 will exit the while loop
|
|
198
|
-
|
|
199
|
-
n_loops += 1
|
|
200
|
-
|
|
201
|
-
if n_loops > 20:
|
|
202
|
-
raise Exception(f"{n_loops}!")
|
|
203
|
-
|
|
204
|
-
return median, u, bin_ulim_bool, outlier_mask, use_mask, n_points
|
|
205
|
-
|
|
206
|
-
@staticmethod
|
|
207
|
-
def get_visit_map(lightcurve):
|
|
208
|
-
"""
|
|
209
|
-
Create a map datapoint to visit
|
|
210
|
-
|
|
211
|
-
:param lightcurve: the unbinned lightcurve
|
|
212
|
-
:type lightcurve: pd.DataFrame
|
|
213
|
-
:returns: visit map
|
|
214
|
-
:rtype: np.ndarray
|
|
215
|
-
"""
|
|
216
|
-
# ------------------------- find epoch intervals -------------------------- #
|
|
217
|
-
sorted_mjds = np.sort(lightcurve.mjd)
|
|
218
|
-
epoch_bounds_mask = (sorted_mjds[1:] - sorted_mjds[:-1]) > 100
|
|
219
|
-
epoch_bins = np.array(
|
|
220
|
-
[lightcurve.mjd.min() * 0.99] + # this makes sure that the first datapoint gets selected
|
|
221
|
-
list(((sorted_mjds[1:] + sorted_mjds[:-1]) / 2)[epoch_bounds_mask]) + # finding the middle between
|
|
222
|
-
# two visits
|
|
223
|
-
[lightcurve.mjd.max() * 1.01] # this just makes sure that the last datapoint gets selected as well
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
visit_mask = np.digitize(lightcurve.mjd, epoch_bins) - 1
|
|
227
|
-
return visit_mask
|
|
228
|
-
|
|
229
|
-
def bin_lightcurve(self, lightcurve):
|
|
230
|
-
"""
|
|
231
|
-
Combine the data by visits of the satellite of one region in the sky.
|
|
232
|
-
The visits typically consist of some tens of observations. The individual visits are separated by about
|
|
233
|
-
six months.
|
|
234
|
-
The mean flux for one visit is calculated by the weighted mean of the data.
|
|
235
|
-
The error on that mean is calculated by the root-mean-squared and corrected by the t-value.
|
|
236
|
-
Outliers per visit are identified if they are more than 100 times the rms away from the mean. These outliers
|
|
237
|
-
are removed from the calculation of the mean and the error if self.clean_outliers_when_binning is True.
|
|
238
|
-
|
|
239
|
-
:param lightcurve: the unbinned lightcurve
|
|
240
|
-
:type lightcurve: pandas.DataFrame
|
|
241
|
-
:return: the binned lightcurve
|
|
242
|
-
:rtype: pandas.DataFrame
|
|
243
|
-
"""
|
|
244
|
-
|
|
245
|
-
# ------------------------- create visit mask -------------------------- #
|
|
246
|
-
visit_map = self.get_visit_map(lightcurve)
|
|
247
|
-
counts = np.bincount(visit_map)
|
|
248
|
-
|
|
249
|
-
binned_data = dict()
|
|
250
|
-
|
|
251
|
-
# ------------------------- calculate mean mjd -------------------------- #
|
|
252
|
-
binned_data["mean_mjd"] = np.bincount(visit_map, weights=lightcurve.mjd) / counts
|
|
253
|
-
|
|
254
|
-
# ------------------------- loop through bands -------------------------- #
|
|
255
|
-
for b in self.bands:
|
|
256
|
-
# loop through magnitude and flux and save the respective datapoints
|
|
257
|
-
|
|
258
|
-
outlier_masks = dict()
|
|
259
|
-
use_masks = dict()
|
|
260
|
-
bin_ulim_bools = dict()
|
|
261
|
-
|
|
262
|
-
for lum_ext in [self.flux_key_ext, self.mag_key_ext]:
|
|
263
|
-
f = lightcurve[f"{b}{lum_ext}"]
|
|
264
|
-
e = lightcurve[f"{b}{lum_ext}{self.error_key_ext}"]
|
|
265
|
-
|
|
266
|
-
# we will flag outliers based on the flux only
|
|
267
|
-
remove_outliers = lum_ext == self.flux_key_ext and self.clean_outliers_when_binning
|
|
268
|
-
outlier_mask = outlier_masks.get(self.flux_key_ext, None)
|
|
269
|
-
|
|
270
|
-
mean, u, bin_ulim_bool, outlier_mask, use_mask, n_points = self.calculate_epochs(
|
|
271
|
-
f, e, visit_map,
|
|
272
|
-
counts,
|
|
273
|
-
remove_outliers=remove_outliers,
|
|
274
|
-
outlier_mask=outlier_mask
|
|
275
|
-
)
|
|
276
|
-
n_outliers = np.sum(outlier_mask)
|
|
277
|
-
|
|
278
|
-
if (n_outliers > 0):
|
|
279
|
-
logger.debug(f"removed {n_outliers} outliers by brightness for {b} {lum_ext}")
|
|
280
|
-
|
|
281
|
-
binned_data[f'{b}{self.mean_key}{lum_ext}'] = mean
|
|
282
|
-
binned_data[f'{b}{lum_ext}{self.rms_key}'] = u
|
|
283
|
-
binned_data[f'{b}{lum_ext}{self.upper_limit_key}'] = bin_ulim_bool
|
|
284
|
-
binned_data[f'{b}{lum_ext}{self.Npoints_key}'] = n_points
|
|
285
|
-
|
|
286
|
-
outlier_masks[lum_ext] = outlier_mask
|
|
287
|
-
use_masks[lum_ext] = use_mask
|
|
288
|
-
bin_ulim_bools[lum_ext] = bin_ulim_bool
|
|
289
|
-
|
|
290
|
-
# ------- calculate the zeropoints per exposure ------- #
|
|
291
|
-
# this might look wrong since we use the flux mask on the magnitudes but it s right
|
|
292
|
-
# for each flux measurement we need the corresponding magnitude to get the zeropoint
|
|
293
|
-
mags = lightcurve[f'{b}{self.mag_key_ext}']
|
|
294
|
-
inst_fluxes = lightcurve[f'{b}{self.flux_key_ext}']
|
|
295
|
-
pos_m = inst_fluxes > 0 # select only positive fluxes, i.e. detections
|
|
296
|
-
zp_mask = pos_m & use_masks[self.flux_key_ext]
|
|
297
|
-
|
|
298
|
-
# calculate zero points
|
|
299
|
-
zps = np.zeros_like(inst_fluxes)
|
|
300
|
-
zps[zp_mask] = mags[zp_mask] + 2.5 * np.log10(inst_fluxes[zp_mask])
|
|
301
|
-
# find visits with no zeropoints
|
|
302
|
-
n_valid_zps = np.bincount(visit_map, weights=zp_mask)
|
|
303
|
-
at_least_one_valid_zp = n_valid_zps > 0
|
|
304
|
-
# calculate the median zeropoint for each visit
|
|
305
|
-
zps_median = np.zeros_like(n_valid_zps, dtype=float)
|
|
306
|
-
zps_median[n_valid_zps > 0] = np.array([
|
|
307
|
-
np.median(zps[(visit_map == i) & zp_mask])
|
|
308
|
-
for i in np.unique(visit_map[at_least_one_valid_zp[visit_map]])
|
|
309
|
-
])
|
|
310
|
-
# if there are only non-detections then fall back to default zeropoint
|
|
311
|
-
zps_median[n_valid_zps == 0] = self.magnitude_zeropoints['Mag'][b]
|
|
312
|
-
# if the visit only has upper limits then use the fall-back zeropoint
|
|
313
|
-
zps_median[bin_ulim_bools[self.flux_key_ext]] = self.magnitude_zeropoints['Mag'][b]
|
|
314
|
-
|
|
315
|
-
# --------------- calculate flux density from instrument flux ---------------- #
|
|
316
|
-
# get the instrument flux [digital numbers], i.e. source count
|
|
317
|
-
inst_fluxes_e = lightcurve[f'{b}{self.flux_key_ext}{self.error_key_ext}']
|
|
318
|
-
|
|
319
|
-
# calculate the proportionality constant between flux density and source count
|
|
320
|
-
mag_zp = self.magnitude_zeropoints['F_nu'][b].to('mJy').value
|
|
321
|
-
flux_dens_const = mag_zp * 10 ** (-zps_median / 2.5)
|
|
322
|
-
|
|
323
|
-
# calculate flux densities from instrument counts
|
|
324
|
-
flux_densities = inst_fluxes * flux_dens_const[visit_map]
|
|
325
|
-
flux_densities_e = inst_fluxes_e * flux_dens_const[visit_map]
|
|
326
|
-
|
|
327
|
-
# bin flux densities
|
|
328
|
-
mean_fd, u_fd, ul_fd, outlier_mask_fd, use_mask_fd, n_points_fd = self.calculate_epochs(
|
|
329
|
-
flux_densities,
|
|
330
|
-
flux_densities_e,
|
|
331
|
-
visit_map, counts,
|
|
332
|
-
remove_outliers=False,
|
|
333
|
-
outlier_mask=outlier_masks[self.flux_key_ext]
|
|
334
|
-
)
|
|
335
|
-
binned_data[f'{b}{self.mean_key}{self.flux_density_key_ext}'] = mean_fd
|
|
336
|
-
binned_data[f'{b}{self.flux_density_key_ext}{self.rms_key}'] = u_fd
|
|
337
|
-
binned_data[f'{b}{self.flux_density_key_ext}{self.upper_limit_key}'] = ul_fd
|
|
338
|
-
binned_data[f'{b}{self.flux_density_key_ext}{self.Npoints_key}'] = n_points_fd
|
|
339
|
-
|
|
340
|
-
return pd.DataFrame(binned_data)
|
|
341
|
-
|
|
342
|
-
def calculate_metadata_single(self, lc):
|
|
343
|
-
"""
|
|
344
|
-
Calculates some metadata, describing the variability of the lightcurves.
|
|
345
|
-
|
|
346
|
-
- `max_dif`: maximum difference in magnitude between any two datapoints
|
|
347
|
-
- `min_rms`: the minimum errorbar of all datapoints
|
|
348
|
-
- `N_datapoints`: The number of datapoints
|
|
349
|
-
- `max_deltat`: the maximum time difference between any two datapoints
|
|
350
|
-
- `mean_weighted_ppb`: the weighted average brightness where the weights are the points per bin
|
|
351
|
-
|
|
352
|
-
:param lc: the lightcurves
|
|
353
|
-
:type lc: dict
|
|
354
|
-
:return: the metadata
|
|
355
|
-
:rtype: dict
|
|
356
|
-
"""
|
|
357
|
-
|
|
358
|
-
metadata = dict()
|
|
359
|
-
|
|
360
|
-
for band in self.bands:
|
|
361
|
-
for lum_key in [self.mag_key_ext, self.flux_key_ext, self.flux_density_key_ext]:
|
|
362
|
-
llumkey = f"{band}{self.mean_key}{lum_key}"
|
|
363
|
-
errkey = f"{band}{lum_key}{self.rms_key}"
|
|
364
|
-
ul_key = f'{band}{lum_key}{self.upper_limit_key}'
|
|
365
|
-
ppb_key = f'{band}{lum_key}{self.Npoints_key}'
|
|
366
|
-
|
|
367
|
-
difk = f"{band}_max_dif{lum_key}"
|
|
368
|
-
rmsk = f"{band}_min_rms{lum_key}"
|
|
369
|
-
Nk = f"{band}_N_datapoints{lum_key}"
|
|
370
|
-
dtk = f"{band}_max_deltat{lum_key}"
|
|
371
|
-
medk = f"{band}_median{lum_key}"
|
|
372
|
-
chi2tmk = f"{band}_chi2_to_med{lum_key}"
|
|
373
|
-
mean_weighted_ppb_key = f"{band}_mean_weighted_ppb{lum_key}"
|
|
374
|
-
excess_variance_key = f"{band}_excess_variance_{lum_key}"
|
|
375
|
-
excess_variance_err_key = f"{band}_excess_variance_err_{lum_key}"
|
|
376
|
-
mck = f"{band}_coverage_of_median{lum_key}"
|
|
377
|
-
|
|
378
|
-
ilc = lc[~np.array(lc[ul_key]).astype(bool)] if ul_key in lc else dict()
|
|
379
|
-
metadata[Nk] = len(ilc)
|
|
380
|
-
|
|
381
|
-
if len(ilc) > 0:
|
|
382
|
-
# check if ppb sum to more than zero
|
|
383
|
-
if sum(ilc[ppb_key]) > 0:
|
|
384
|
-
metadata[mean_weighted_ppb_key] = np.average(ilc[llumkey], weights=ilc[ppb_key])
|
|
385
|
-
metadata[excess_variance_key], metadata[excess_variance_err_key] = get_excess_variance(
|
|
386
|
-
np.array(ilc[llumkey]),
|
|
387
|
-
np.array(ilc[errkey]),
|
|
388
|
-
np.array(metadata[mean_weighted_ppb_key])
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
# if not we can not calculate the excess variance
|
|
392
|
-
else:
|
|
393
|
-
for k in [mean_weighted_ppb_key, excess_variance_key, excess_variance_err_key]:
|
|
394
|
-
metadata[k] = np.nan
|
|
395
|
-
|
|
396
|
-
imin = ilc[llumkey].min()
|
|
397
|
-
imax = ilc[llumkey].max()
|
|
398
|
-
imin_rms_ind = ilc[errkey].argmin()
|
|
399
|
-
imin_rms = ilc[errkey].iloc[imin_rms_ind]
|
|
400
|
-
|
|
401
|
-
imed = np.median(ilc[llumkey])
|
|
402
|
-
ichi2_to_med = sum(((ilc[llumkey] - imed) / ilc[errkey]) ** 2)
|
|
403
|
-
imc = np.sum(abs(ilc[llumkey] - imed) < ilc[errkey]) / len(ilc[llumkey])
|
|
404
|
-
|
|
405
|
-
metadata[difk] = imax - imin
|
|
406
|
-
metadata[rmsk] = imin_rms
|
|
407
|
-
metadata[medk] = imed
|
|
408
|
-
metadata[chi2tmk] = ichi2_to_med
|
|
409
|
-
metadata[mck] = imc
|
|
410
|
-
|
|
411
|
-
if len(ilc) == 1:
|
|
412
|
-
metadata[dtk] = 0
|
|
413
|
-
else:
|
|
414
|
-
mjds = np.array(ilc.mean_mjd).astype(float)
|
|
415
|
-
dt = mjds[1:] - mjds[:-1]
|
|
416
|
-
metadata[dtk] = max(dt)
|
|
417
|
-
|
|
418
|
-
else:
|
|
419
|
-
for k in [
|
|
420
|
-
difk,
|
|
421
|
-
dtk,
|
|
422
|
-
mean_weighted_ppb_key,
|
|
423
|
-
excess_variance_key,
|
|
424
|
-
rmsk,
|
|
425
|
-
medk,
|
|
426
|
-
chi2tmk
|
|
427
|
-
|
|
428
|
-
]:
|
|
429
|
-
metadata[k] = np.nan
|
|
430
|
-
|
|
431
|
-
return metadata
|
|
432
|
-
|
|
433
|
-
def plot_diagnostic_binning(
|
|
434
|
-
self,
|
|
435
|
-
service,
|
|
436
|
-
ind,
|
|
437
|
-
lum_key="mag",
|
|
438
|
-
interactive=False,
|
|
439
|
-
fn=None,
|
|
440
|
-
save=True,
|
|
441
|
-
which="panstarrs",
|
|
442
|
-
arcsec=20
|
|
443
|
-
):
|
|
444
|
-
"""
|
|
445
|
-
Show a skymap of the single detections and which bin they belong to next to the binned lightcurve
|
|
446
|
-
|
|
447
|
-
:param service: service used to download data, either of 'tap' or 'gator'
|
|
448
|
-
:type service: str
|
|
449
|
-
:param ind: index of the object in the parent sample
|
|
450
|
-
:type ind: str, int
|
|
451
|
-
:param lum_key: the key of the brightness unit, either of `flux` (instrument flux in counts) or `mag`
|
|
452
|
-
:type lum_key: str
|
|
453
|
-
:param interactive: if function is used interactively, return mpl.Figure and mpl.axes if True
|
|
454
|
-
:type interactive: bool
|
|
455
|
-
:param fn: filename for saving
|
|
456
|
-
:type fn: str
|
|
457
|
-
:param save: saves figure if True
|
|
458
|
-
:type save: bool
|
|
459
|
-
:param which: survey to get the cutout from, either of 'sdss' or 'panstarrs'
|
|
460
|
-
:type which: str
|
|
461
|
-
:param arcsec: size of cutout
|
|
462
|
-
:type arcsec: float
|
|
463
|
-
:returns: Figure and axes if `interactive=True`
|
|
464
|
-
:rtype: mpl.Figure, mpl.Axes
|
|
465
|
-
"""
|
|
466
|
-
|
|
467
|
-
logger.info(f"making binning diagnostic plot")
|
|
468
|
-
# get position
|
|
469
|
-
pos = self.parent_sample.df.loc[
|
|
470
|
-
ind,
|
|
471
|
-
[self.parent_sample.default_keymap["ra"], self.parent_sample.default_keymap["dec"]]
|
|
472
|
-
]
|
|
473
|
-
# get chunk number to be able to load the unstacked lightcurve
|
|
474
|
-
chunk_number = self._get_chunk_number(parent_sample_index=ind)
|
|
475
|
-
|
|
476
|
-
# load the unstacked lightcurves
|
|
477
|
-
if service == "tap":
|
|
478
|
-
unbinned_lcs = self.get_unbinned_lightcurves(chunk_number=chunk_number)
|
|
479
|
-
else:
|
|
480
|
-
unbinned_lcs = self._get_unbinned_lightcurves_gator(chunk_number=chunk_number)
|
|
481
|
-
|
|
482
|
-
# select the datapoints corresponding to our object
|
|
483
|
-
lightcurve = unbinned_lcs[unbinned_lcs[self._tap_orig_id_key] == ind]
|
|
484
|
-
|
|
485
|
-
# get visit map
|
|
486
|
-
visit_map = self.get_visit_map(lightcurve)
|
|
487
|
-
counts = np.bincount(visit_map)
|
|
488
|
-
|
|
489
|
-
# get the masks indicating outliers per visit based on brightness
|
|
490
|
-
outlier_masks = dict()
|
|
491
|
-
for b in self.bands:
|
|
492
|
-
f = lightcurve[f"{b}{self.flux_key_ext}"]
|
|
493
|
-
e = lightcurve[f"{b}{self.flux_key_ext}{self.error_key_ext}"]
|
|
494
|
-
|
|
495
|
-
mean, u, bin_ulim_bool, outlier_mask, use_mask, n_points = self.calculate_epochs(
|
|
496
|
-
f, e, visit_map,
|
|
497
|
-
counts,
|
|
498
|
-
remove_outliers=True
|
|
499
|
-
)
|
|
500
|
-
if np.any(outlier_mask):
|
|
501
|
-
outlier_masks[b] = outlier_mask
|
|
502
|
-
|
|
503
|
-
# get a mask indicating outliers based on position
|
|
504
|
-
ra, dec = pos.astype(float)
|
|
505
|
-
bad_indices_position, cluster_res, data_mask, allwise_mask = self.calculate_position_mask(
|
|
506
|
-
lightcurve, ra=ra, dec=dec, return_all=True, whitelist_region=self.whitelist_region.to("arcsec").value
|
|
507
|
-
)
|
|
508
|
-
position_mask = (
|
|
509
|
-
~lightcurve.index.isin(bad_indices_position)
|
|
510
|
-
if len(bad_indices_position) > 0
|
|
511
|
-
else np.array([True] * len(lightcurve))
|
|
512
|
-
)
|
|
513
|
-
|
|
514
|
-
# calculate the stacked lightcurve
|
|
515
|
-
binned_lightcurve = self.bin_lightcurve(lightcurve[position_mask])
|
|
516
|
-
|
|
517
|
-
# ----------------- create the plot ----------------- #
|
|
518
|
-
|
|
519
|
-
fig, axs = plt.subplots(nrows=2, gridspec_kw={"height_ratios": [3, 2]}, figsize=(5, 8))
|
|
520
|
-
|
|
521
|
-
# plot a panstarrs cutout
|
|
522
|
-
kwargs = {"plot_color_image": True} if which == "panstarrs" else dict()
|
|
523
|
-
self.parent_sample.plot_cutout(ind=ind, ax=axs[0], which=which, arcsec=arcsec, **kwargs)
|
|
524
|
-
|
|
525
|
-
# plot the lightcurve
|
|
526
|
-
if len(binned_lightcurve) > 0:
|
|
527
|
-
self._plot_lc(
|
|
528
|
-
lightcurve=binned_lightcurve,
|
|
529
|
-
unbinned_lc=lightcurve[position_mask],
|
|
530
|
-
lum_key=lum_key,
|
|
531
|
-
ax=axs[-1],
|
|
532
|
-
save=False
|
|
533
|
-
)
|
|
534
|
-
self._plot_lc(
|
|
535
|
-
unbinned_lc=lightcurve[~position_mask],
|
|
536
|
-
lum_key=lum_key,
|
|
537
|
-
ax=axs[-1],
|
|
538
|
-
save=False,
|
|
539
|
-
colors={"W1": "gray", "W2": "lightgray"}
|
|
540
|
-
)
|
|
541
|
-
|
|
542
|
-
# set markers for visits
|
|
543
|
-
markers_strings = list(Line2D.filled_markers) + ["$1$", "$2$", "$3$", "$4$", "$5$", "$6$", "$7$", "$8$", "$9$"]
|
|
544
|
-
markers_straight = [MarkerStyle(im) for im in markers_strings]
|
|
545
|
-
rot = Affine2D().rotate_deg(180)
|
|
546
|
-
markers_rotated = [MarkerStyle(im, transform=rot) for im in markers_strings]
|
|
547
|
-
markers = markers_straight + markers_rotated
|
|
548
|
-
|
|
549
|
-
# calculate ra and dec relative to center of cutout
|
|
550
|
-
ra = (lightcurve.ra - pos[self.parent_sample.default_keymap["ra"]]) * 3600
|
|
551
|
-
dec = (lightcurve.dec - pos[self.parent_sample.default_keymap["dec"]]) * 3600
|
|
552
|
-
|
|
553
|
-
# for each visit plot the datapoints on the cutout
|
|
554
|
-
for visit in np.unique(visit_map):
|
|
555
|
-
m = visit_map == visit
|
|
556
|
-
label = str(visit)
|
|
557
|
-
axs[0].plot([], [], marker=markers[visit], label=label, mec="k", mew=1, mfc="none", ls="")
|
|
558
|
-
|
|
559
|
-
for im, mec, zorder in zip(
|
|
560
|
-
[position_mask, ~position_mask],
|
|
561
|
-
["k", "none"],
|
|
562
|
-
[1, 0]
|
|
563
|
-
):
|
|
564
|
-
mask = m & im
|
|
565
|
-
|
|
566
|
-
for i_data_mask, i_data in zip([data_mask, ~data_mask], ["data", "other_allwise"]):
|
|
567
|
-
datapoints = lightcurve[mask & i_data_mask]
|
|
568
|
-
cluster_labels = (
|
|
569
|
-
cluster_res.labels_[mask[i_data_mask]] if i_data == "data"
|
|
570
|
-
else np.array([-1] * len(datapoints))
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
for cluster_label in np.unique(cluster_labels):
|
|
574
|
-
cluster_label_mask = cluster_labels == cluster_label
|
|
575
|
-
datapoints_cluster = datapoints[cluster_label_mask]
|
|
576
|
-
# make a marker for the visit and colored by cluster
|
|
577
|
-
color = f"C{cluster_label}" if cluster_label != -1 else "grey"
|
|
578
|
-
|
|
579
|
-
if ("sigra" in datapoints_cluster.columns) and ("sigdec" in datapoints_cluster.columns):
|
|
580
|
-
has_sig = ~datapoints_cluster.sigra.isna() & ~datapoints_cluster.sigdec.isna()
|
|
581
|
-
_ra = ra[mask & i_data_mask][cluster_label_mask]
|
|
582
|
-
_dec = dec[mask & i_data_mask][cluster_label_mask]
|
|
583
|
-
|
|
584
|
-
axs[0].errorbar(
|
|
585
|
-
_ra[has_sig],
|
|
586
|
-
_dec[has_sig],
|
|
587
|
-
xerr=datapoints_cluster.sigra[has_sig] / 3600,
|
|
588
|
-
yerr=datapoints_cluster.sigdec[has_sig] / 3600,
|
|
589
|
-
marker=markers[visit],
|
|
590
|
-
ls="",
|
|
591
|
-
color=color,
|
|
592
|
-
zorder=zorder,
|
|
593
|
-
ms=10,
|
|
594
|
-
mec=mec,
|
|
595
|
-
mew=0.1
|
|
596
|
-
)
|
|
597
|
-
axs[0].scatter(
|
|
598
|
-
_ra[~has_sig],
|
|
599
|
-
_dec[~has_sig],
|
|
600
|
-
marker=markers[visit],
|
|
601
|
-
color=color,
|
|
602
|
-
zorder=zorder,
|
|
603
|
-
edgecolors=mec,
|
|
604
|
-
linewidths=0.1,
|
|
605
|
-
)
|
|
606
|
-
else:
|
|
607
|
-
axs[0].scatter(
|
|
608
|
-
ra[mask], dec[mask],
|
|
609
|
-
marker=markers[visit],
|
|
610
|
-
color=color,
|
|
611
|
-
zorder=zorder,
|
|
612
|
-
edgecolors=mec,
|
|
613
|
-
linewidths=0.1,
|
|
614
|
-
)
|
|
615
|
-
|
|
616
|
-
# for each band indicate the outliers based on brightness with circles
|
|
617
|
-
for b, outlier_mask in outlier_masks.items():
|
|
618
|
-
brightness_outlier_scatter_style = {
|
|
619
|
-
"marker": '$\u25cc$',
|
|
620
|
-
"facecolors": self.band_plot_colors[b],
|
|
621
|
-
"edgecolors": self.band_plot_colors[b],
|
|
622
|
-
"s": 60,
|
|
623
|
-
"lw": 2
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
axs[0].scatter(
|
|
627
|
-
ra[outlier_mask],
|
|
628
|
-
dec[outlier_mask],
|
|
629
|
-
label=f"{b} outlier by brightness",
|
|
630
|
-
**brightness_outlier_scatter_style
|
|
631
|
-
|
|
632
|
-
)
|
|
633
|
-
axs[1].scatter(
|
|
634
|
-
lightcurve.mjd[outlier_mask],
|
|
635
|
-
lightcurve[f"{b}_{lum_key}"][outlier_mask],
|
|
636
|
-
**brightness_outlier_scatter_style
|
|
637
|
-
)
|
|
638
|
-
|
|
639
|
-
# indicate the query region with a circle
|
|
640
|
-
circle = plt.Circle((0, 0), self.min_sep.to("arcsec").value, color='r', fill=False, ls="-", lw=3, zorder=0)
|
|
641
|
-
axs[0].add_artist(circle)
|
|
642
|
-
# indicate the whitelist region of 1 arcsec with a circle
|
|
643
|
-
circle = plt.Circle(
|
|
644
|
-
(0, 0),
|
|
645
|
-
self.whitelist_region.to("arcsec").value,
|
|
646
|
-
color='g',
|
|
647
|
-
fill=False,
|
|
648
|
-
ls="-",
|
|
649
|
-
lw=3,
|
|
650
|
-
zorder=0
|
|
651
|
-
)
|
|
652
|
-
axs[0].add_artist(circle)
|
|
653
|
-
|
|
654
|
-
# formatting
|
|
655
|
-
title = axs[0].get_title()
|
|
656
|
-
axs[-1].set_ylabel("Apparent Vega Magnitude")
|
|
657
|
-
axs[-1].grid(ls=":", alpha=0.5)
|
|
658
|
-
axs[0].set_title("")
|
|
659
|
-
axs[0].legend(ncol=5, bbox_to_anchor=(0, 1, 1, 0), loc="lower left", mode="expand", title=title)
|
|
660
|
-
axs[0].set_aspect(1, adjustable="box")
|
|
661
|
-
fig.tight_layout()
|
|
662
|
-
|
|
663
|
-
if save:
|
|
664
|
-
if fn is None:
|
|
665
|
-
fn = self.plots_dir / f"{ind}_binning_diag_{which}cutout.pdf"
|
|
666
|
-
logger.debug(f"saving under {fn}")
|
|
667
|
-
fig.savefig(fn)
|
|
668
|
-
|
|
669
|
-
if interactive:
|
|
670
|
-
return fig, axs
|
|
671
|
-
else:
|
|
672
|
-
plt.close()
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
F_nu f_c(W1) f_c(W2) f_c(W3) f_c(W4) [W1 - W2] [W2 - W3] [W3 - W4]
|
|
2
|
-
nu^3 1.0283 1.0206 1.1344 1.0142 -0.4040 -0.9624 -0.8684
|
|
3
|
-
nu^2 1.0084 1.0066 1.0088 1.0013 -0.0538 -0.0748 -0.0519
|
|
4
|
-
nu^1 0.9961 0.9976 0.9393 0.9934 0.2939 0.8575 0.7200
|
|
5
|
-
nu^0 0.9907 0.9935 0.9169 0.9905 0.6393 1.8357 1.4458
|
|
6
|
-
nu^-1 0.9921 0.9943 0.9373 0.9926 0.9828 2.8586 2.1272
|
|
7
|
-
nu^-2 1.0000 1.0000 1.0000 1.0000 1.3246 3.9225 2.7680
|
|
8
|
-
nu^-3 1.0142 1.0107 1.1081 1.0130 1.6649 5.0223 3.3734
|
|
9
|
-
nu^-4 1.0347 1.0265 1.2687 1.0319 2.0041 6.1524 3.9495
|
|
10
|
-
B_nu(100) 17.2062 3.9096 2.6588 1.0032 10.6511 18.9307 4.6367
|
|
11
|
-
B_nu(141) 4.0882 1.9739 1.4002 0.9852 7.7894 13.0371 3.4496
|
|
12
|
-
B_nu(200) 2.0577 1.3448 1.0006 0.9833 5.4702 8.8172 2.4949
|
|
13
|
-
B_nu(283) 1.3917 1.1124 0.8791 0.9865 3.8329 5.8986 1.7552
|
|
14
|
-
B_nu(400) 1.1316 1.0229 0.8622 0.9903 2.6588 3.8930 1.2014
|
|
15
|
-
B_nu(566) 1.0263 0.9919 0.8833 0.9935 1.8069 2.5293 0.8041
|
|
16
|
-
B_nu(800) 0.9884 0.9853 0.9125 0.9958 1.1996 1.6282 0.5311
|
|
17
|
-
B_nu(1131) 0.9801 0.9877 0.9386 0.9975 0.7774 1.0421 0.3463
|
|
18
|
-
K2V 1.0038 1.0512 1.0030 1.0013 -0.0963 0.1225 -0.0201
|
|
19
|
-
G2V 1.0049 1.0193 1.0024 1.0012 -0.0268 0.0397 -0.0217
|