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.
Files changed (52) hide show
  1. timewise/__init__.py +1 -5
  2. timewise/backend/__init__.py +6 -0
  3. timewise/backend/base.py +36 -0
  4. timewise/backend/filesystem.py +80 -0
  5. timewise/chunking.py +50 -0
  6. timewise/cli.py +117 -11
  7. timewise/config.py +34 -0
  8. timewise/io/__init__.py +1 -0
  9. timewise/io/config.py +64 -0
  10. timewise/io/download.py +302 -0
  11. timewise/io/stable_tap.py +121 -0
  12. timewise/plot/__init__.py +3 -0
  13. timewise/plot/diagnostic.py +242 -0
  14. timewise/plot/lightcurve.py +112 -0
  15. timewise/plot/panstarrs.py +260 -0
  16. timewise/plot/sdss.py +109 -0
  17. timewise/process/__init__.py +2 -0
  18. timewise/process/config.py +30 -0
  19. timewise/process/interface.py +143 -0
  20. timewise/process/keys.py +10 -0
  21. timewise/process/stacking.py +310 -0
  22. timewise/process/template.yml +49 -0
  23. timewise/query/__init__.py +6 -0
  24. timewise/query/base.py +45 -0
  25. timewise/query/positional.py +40 -0
  26. timewise/tables/__init__.py +10 -0
  27. timewise/tables/allwise_p3as_mep.py +22 -0
  28. timewise/tables/base.py +9 -0
  29. timewise/tables/neowiser_p1bs_psd.py +22 -0
  30. timewise/types.py +30 -0
  31. timewise/util/backoff.py +12 -0
  32. timewise/util/csv_utils.py +12 -0
  33. timewise/util/error_threading.py +70 -0
  34. timewise/util/visits.py +33 -0
  35. timewise-1.0.0a1.dist-info/METADATA +205 -0
  36. timewise-1.0.0a1.dist-info/RECORD +39 -0
  37. timewise-1.0.0a1.dist-info/entry_points.txt +3 -0
  38. timewise/big_parent_sample.py +0 -106
  39. timewise/config_loader.py +0 -157
  40. timewise/general.py +0 -52
  41. timewise/parent_sample_base.py +0 -89
  42. timewise/point_source_utils.py +0 -68
  43. timewise/utils.py +0 -558
  44. timewise/wise_bigdata_desy_cluster.py +0 -1407
  45. timewise/wise_data_base.py +0 -2027
  46. timewise/wise_data_by_visit.py +0 -672
  47. timewise/wise_flux_conversion_correction.dat +0 -19
  48. timewise-0.5.4.dist-info/METADATA +0 -56
  49. timewise-0.5.4.dist-info/RECORD +0 -17
  50. timewise-0.5.4.dist-info/entry_points.txt +0 -3
  51. {timewise-0.5.4.dist-info → timewise-1.0.0a1.dist-info}/WHEEL +0 -0
  52. {timewise-0.5.4.dist-info → timewise-1.0.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -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