asteroid_spinprops 0.2.32__py3-none-any.whl → 1.0.1__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.
- asteroid_spinprops/ssolib/dataprep.py +0 -346
- asteroid_spinprops/ssolib/modelfit.py +143 -252
- asteroid_spinprops/ssolib/periodest.py +126 -158
- asteroid_spinprops/ssolib/utils.py +164 -211
- asteroid_spinprops-1.0.1.dist-info/METADATA +186 -0
- asteroid_spinprops-1.0.1.dist-info/RECORD +10 -0
- asteroid_spinprops/ssolib/.ruff_cache/.gitignore +0 -2
- asteroid_spinprops/ssolib/.ruff_cache/0.13.2/1980339045096230685 +0 -0
- asteroid_spinprops/ssolib/.ruff_cache/CACHEDIR.TAG +0 -1
- asteroid_spinprops/ssolib/pipetools.py +0 -167
- asteroid_spinprops/ssolib/testing/atlas_x_ztf_testing/test_pqfile_1.parquet +0 -0
- asteroid_spinprops/ssolib/testing/atlas_x_ztf_testing/test_pqfile_2.parquet +0 -0
- asteroid_spinprops/ssolib/testing/ephemeris_testing/2000 WL152 +0 -1702
- asteroid_spinprops/ssolib/testing/ephemeris_testing/2001 PC +0 -94
- asteroid_spinprops/ssolib/testing/ephemeris_testing/2001 SG276 +0 -111
- asteroid_spinprops/ssolib/testing/ephemeris_testing/2008 GX32 +0 -93
- asteroid_spinprops/ssolib/testing/ephemeris_testing/2009 BE185 +0 -130
- asteroid_spinprops/ssolib/testing/ephemeris_testing/2011 EY17 +0 -101
- asteroid_spinprops/ssolib/testing/ephemeris_testing/2134 T-1 +0 -352
- asteroid_spinprops/ssolib/testing/ephemeris_testing/Bellmore +0 -2657
- asteroid_spinprops/ssolib/testing/ephemeris_testing/Dermott +0 -2971
- asteroid_spinprops/ssolib/testing/ephemeris_testing/Duke +0 -2026
- asteroid_spinprops/ssolib/testing/ephemeris_testing/Izenberg +0 -2440
- asteroid_spinprops/ssolib/testing/ephemeris_testing/Lermontov +0 -2760
- asteroid_spinprops/ssolib/testing/ephemeris_testing/Poullain +0 -1272
- asteroid_spinprops/ssolib/testing/ephemeris_testing/Sonneberga +0 -2756
- asteroid_spinprops/ssolib/testing/testing_ssoname_keys.pkl +0 -0
- asteroid_spinprops-0.2.32.dist-info/METADATA +0 -77
- asteroid_spinprops-0.2.32.dist-info/RECORD +0 -31
- {asteroid_spinprops-0.2.32.dist-info → asteroid_spinprops-1.0.1.dist-info}/WHEEL +0 -0
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import pandas as pd
|
|
3
|
-
import rocks
|
|
4
|
-
import matplotlib.pyplot as plt
|
|
5
|
-
|
|
6
3
|
|
|
7
4
|
from astropy.timeseries import LombScargleMultiband, LombScargle
|
|
8
5
|
from scipy.signal import find_peaks
|
|
@@ -11,21 +8,59 @@ from scipy.stats import f as ftest
|
|
|
11
8
|
|
|
12
9
|
|
|
13
10
|
def alias_func(x, i, j, p_feat):
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
"""
|
|
12
|
+
Compute the aliasing relation for a periodic signal.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
x : float
|
|
17
|
+
Input period.
|
|
18
|
+
i : int
|
|
19
|
+
Positive integer representing the mode number.
|
|
20
|
+
j : int
|
|
21
|
+
Alias index.
|
|
22
|
+
p_feat : float
|
|
23
|
+
Characteristic frequency of the feature (e.g. 1 day).
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
float
|
|
28
|
+
Value of the aliasing relation for the given inputs.
|
|
24
29
|
"""
|
|
25
30
|
return (i + 1) * p_feat * x / np.abs(p_feat - j * x)
|
|
26
31
|
|
|
27
32
|
|
|
28
33
|
def get_period_estimate(residuals_dataframe, p_min=0.03, p_max=2):
|
|
34
|
+
"""
|
|
35
|
+
Estimate significant periods in a time series of residuals using Lomb-Scargle periodograms.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
residuals_dataframe : pandas.DataFrame
|
|
40
|
+
DataFrame containing at least the following columns:
|
|
41
|
+
- 'jd' : observation times
|
|
42
|
+
- 'residuals' : residuals (observed - SHG1G2 modeled magnitudes)
|
|
43
|
+
- 'filters' : filter IDs (int)
|
|
44
|
+
p_min : float, optional
|
|
45
|
+
Minimum period to search (in days). Default is 0.03.
|
|
46
|
+
p_max : float, optional
|
|
47
|
+
Maximum period to search (in days). Default is 2.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
tuple
|
|
52
|
+
- single_band_results : list
|
|
53
|
+
[frequencies, power, top signal peak frequencies, corresponding powers]
|
|
54
|
+
- window_function_results : list
|
|
55
|
+
[frequencies, power, top window peak frequencies, corresponding powers]
|
|
56
|
+
- noise_level : float
|
|
57
|
+
Estimated noise level in the periodogram (mean + 3*std of power)
|
|
58
|
+
|
|
59
|
+
Notes
|
|
60
|
+
-----
|
|
61
|
+
- Uses `LombScargle` for the window function and `LombScargleMultiband` from the `nifty-ls` implementations
|
|
62
|
+
- The five strongest peaks are returned.
|
|
63
|
+
"""
|
|
29
64
|
period_min, period_max = p_min, p_max
|
|
30
65
|
period_range = (period_min, period_max)
|
|
31
66
|
|
|
@@ -90,6 +125,50 @@ def get_period_estimate(residuals_dataframe, p_min=0.03, p_max=2):
|
|
|
90
125
|
def get_multiband_period_estimate(
|
|
91
126
|
residuals_dataframe, p_min=0.03, p_max=2, k_free=True, k_val=None
|
|
92
127
|
):
|
|
128
|
+
"""
|
|
129
|
+
Estimate the period of a multiband time series using a multiband Lomb-Scargle model.
|
|
130
|
+
|
|
131
|
+
Fits a multiband Lomb-Scargle model to the SHG1G2 residuals across different filters,
|
|
132
|
+
optionally testing multiple base-term complexities (`k`) and selecting the simplest model
|
|
133
|
+
that adequately describes the data.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
residuals_dataframe : pandas.DataFrame
|
|
138
|
+
DataFrame containing:
|
|
139
|
+
- 'jd' : observation times
|
|
140
|
+
- 'residuals' : residual magnitudes (observed - model)
|
|
141
|
+
- 'filters' : filter IDs
|
|
142
|
+
- 'sigma' : observational uncertainties
|
|
143
|
+
p_min : float, optional
|
|
144
|
+
Minimum period to search (days). Default is 0.03.
|
|
145
|
+
p_max : float, optional
|
|
146
|
+
Maximum period to search (days). Default is 2.
|
|
147
|
+
k_free : bool, optional
|
|
148
|
+
If True, automatically scan multiple base-term complexities to choose optimal model. Default True.
|
|
149
|
+
k_val : int, optional
|
|
150
|
+
Fixed number of base terms to use if `k_free=False`. Default None.
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
tuple
|
|
155
|
+
- period_in : float
|
|
156
|
+
Estimated dominant SSO period (2 / f_best) in the time series.
|
|
157
|
+
- k_val : int
|
|
158
|
+
Number of base terms used in the final multiband model.
|
|
159
|
+
- p_rms : float
|
|
160
|
+
RMS of residuals for the chosen model (NaN if k_free=False).
|
|
161
|
+
- signal_peaks : array_like
|
|
162
|
+
Frequencies of the top five peaks in the final multiband periodogram.
|
|
163
|
+
- window_peaks : array_like
|
|
164
|
+
Frequencies of the top five peaks in the single-band window function periodogram.
|
|
165
|
+
|
|
166
|
+
Notes
|
|
167
|
+
-----
|
|
168
|
+
- Uses `LombScargle` for the window function and `LombScargleMultiband` from the `nifty-ls` implementations
|
|
169
|
+
- If `k_free=True`, performs F-test comparisons to select the simplest adequate model.
|
|
170
|
+
"""
|
|
171
|
+
|
|
93
172
|
period_min, period_max = p_min, p_max
|
|
94
173
|
period_range = (period_min, period_max)
|
|
95
174
|
results = []
|
|
@@ -245,152 +324,41 @@ def get_multiband_period_estimate(
|
|
|
245
324
|
return period_in, k_val, p_rms, signal_peaks, window_peaks
|
|
246
325
|
|
|
247
326
|
|
|
248
|
-
def plot_periodograms(signal, window, name=None, axis="frequency"):
|
|
249
|
-
if name is not None:
|
|
250
|
-
r = rocks.Rock(name, datacloud="spins")
|
|
251
|
-
bib_estimates = r.spins["period"].values[pd.notna(r.spins["period"].values)]
|
|
252
|
-
|
|
253
|
-
fig, ax = plt.subplots(2, 1, figsize=(12, 6))
|
|
254
|
-
if axis == "frequency":
|
|
255
|
-
ax[0].plot(signal[0], signal[1], c="red", linewidth=2)
|
|
256
|
-
ax[1].plot(window[0], window[1], c="black", alpha=1, linewidth=2)
|
|
257
|
-
|
|
258
|
-
for i, n in enumerate(signal[2]):
|
|
259
|
-
ax[0].scatter(
|
|
260
|
-
n - 0.2,
|
|
261
|
-
signal[3][i],
|
|
262
|
-
marker="${}$".format(i + 1),
|
|
263
|
-
zorder=1000,
|
|
264
|
-
s=60,
|
|
265
|
-
c="black",
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
for i in [0, 1]:
|
|
269
|
-
for j in range(1, 9):
|
|
270
|
-
ax[i].axvline(x=24 / (24 / j), linestyle="-.", c="purple", linewidth=1)
|
|
271
|
-
ax[i].set_xlim(
|
|
272
|
-
1 / (2 + 0.5),
|
|
273
|
-
)
|
|
274
|
-
ax[i].set_ylim(
|
|
275
|
-
0,
|
|
276
|
-
)
|
|
277
|
-
if name is not None:
|
|
278
|
-
for p in bib_estimates:
|
|
279
|
-
ax[0].axvline(x=24 / (p / 2), linestyle="-", c="green", linewidth=1.5)
|
|
280
|
-
|
|
281
|
-
for k in [
|
|
282
|
-
int(24 * 2),
|
|
283
|
-
int(12 * 2),
|
|
284
|
-
int(8 * 2),
|
|
285
|
-
int(6 * 2),
|
|
286
|
-
]:
|
|
287
|
-
ax[0].text(
|
|
288
|
-
x=24 / (k / 2) - 0.1,
|
|
289
|
-
y=max(signal[1]) + 0.05,
|
|
290
|
-
s="{}h".format(k),
|
|
291
|
-
rotation=90,
|
|
292
|
-
fontsize=11,
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
ax[0].text(
|
|
296
|
-
x=0.95,
|
|
297
|
-
y=0.95,
|
|
298
|
-
s="Signal",
|
|
299
|
-
fontsize=15,
|
|
300
|
-
transform=ax[0].transAxes,
|
|
301
|
-
ha="right",
|
|
302
|
-
va="top",
|
|
303
|
-
)
|
|
304
|
-
ax[1].text(
|
|
305
|
-
x=0.95,
|
|
306
|
-
y=0.95,
|
|
307
|
-
s="Window",
|
|
308
|
-
fontsize=15,
|
|
309
|
-
transform=ax[1].transAxes,
|
|
310
|
-
ha="right",
|
|
311
|
-
va="top",
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
ax[1].set_xlabel(r"Frequency /day$^{-1}$")
|
|
315
|
-
|
|
316
|
-
ax[0].set_ylabel("Power")
|
|
317
|
-
ax[1].set_ylabel("Power")
|
|
318
|
-
|
|
319
|
-
ax[0].set_xticks([])
|
|
320
|
-
|
|
321
|
-
plt.tight_layout()
|
|
322
|
-
|
|
323
|
-
if axis == "period":
|
|
324
|
-
ax[0].plot(2 / signal[0], signal[1], c="red", linewidth=2)
|
|
325
|
-
ax[1].plot(2 / window[0], window[1], c="black", alpha=1, linewidth=2)
|
|
326
|
-
|
|
327
|
-
for i, n in enumerate(2 / signal[2]):
|
|
328
|
-
ax[0].scatter(
|
|
329
|
-
2 / (n - 0.2),
|
|
330
|
-
signal[3][i],
|
|
331
|
-
marker="${}$".format(i + 1),
|
|
332
|
-
zorder=1000,
|
|
333
|
-
s=60,
|
|
334
|
-
c="black",
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
# for i in [0, 1]:
|
|
338
|
-
# for j in range(1, 9):
|
|
339
|
-
# ax[i].axvline(x=24 / (24 / j), linestyle="-.", c="purple", linewidth=1)
|
|
340
|
-
# ax[i].set_xlim(
|
|
341
|
-
# 1 / (2 + 0.5),
|
|
342
|
-
# )
|
|
343
|
-
# ax[i].set_ylim(
|
|
344
|
-
# 0,
|
|
345
|
-
# )
|
|
346
|
-
if name is not None:
|
|
347
|
-
for p in bib_estimates:
|
|
348
|
-
ax[0].axvline(x=p / 24, linestyle="-", c="green", linewidth=1.5)
|
|
349
|
-
|
|
350
|
-
for k in [
|
|
351
|
-
int(24 * 2),
|
|
352
|
-
int(12 * 2),
|
|
353
|
-
int(8 * 2),
|
|
354
|
-
int(6 * 2),
|
|
355
|
-
]:
|
|
356
|
-
ax[0].text(
|
|
357
|
-
x=(k - 0.1) / 24,
|
|
358
|
-
y=max(signal[1]) + 0.05,
|
|
359
|
-
s="{}h".format(k),
|
|
360
|
-
rotation=90,
|
|
361
|
-
fontsize=11,
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
ax[0].text(
|
|
365
|
-
x=0.95,
|
|
366
|
-
y=0.95,
|
|
367
|
-
s="Signal",
|
|
368
|
-
fontsize=15,
|
|
369
|
-
transform=ax[0].transAxes,
|
|
370
|
-
ha="right",
|
|
371
|
-
va="top",
|
|
372
|
-
)
|
|
373
|
-
ax[1].text(
|
|
374
|
-
x=0.95,
|
|
375
|
-
y=0.95,
|
|
376
|
-
s="Window",
|
|
377
|
-
fontsize=15,
|
|
378
|
-
transform=ax[1].transAxes,
|
|
379
|
-
ha="right",
|
|
380
|
-
va="top",
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
ax[1].set_xlabel(r"Period /day")
|
|
384
|
-
|
|
385
|
-
ax[0].set_ylabel("Power")
|
|
386
|
-
ax[1].set_ylabel("Power")
|
|
387
|
-
|
|
388
|
-
ax[0].set_xticks([])
|
|
389
|
-
|
|
390
|
-
plt.tight_layout()
|
|
391
|
-
|
|
392
|
-
|
|
393
327
|
def perform_residual_resampling(resid_df, p_min, p_max, k=1):
|
|
328
|
+
"""
|
|
329
|
+
Estimate the robustness of a period measurement via bootstrap resampling of residuals.
|
|
330
|
+
|
|
331
|
+
This function resamples the residuals DataFrame multiple times with replacement,
|
|
332
|
+
recomputes the period for each bootstrap sample, and counts how many of the
|
|
333
|
+
resampled periods are within 1% of the original period. It supports both single-band
|
|
334
|
+
(k=1) and multiband (k>1) period estimation.
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
resid_df : pandas.DataFrame
|
|
339
|
+
DataFrame of residuals containing columns required for `get_period_estimate`
|
|
340
|
+
or `get_multiband_period_estimate`.
|
|
341
|
+
p_min : float
|
|
342
|
+
Minimum period to search (days).
|
|
343
|
+
p_max : float
|
|
344
|
+
Maximum period to search (days).
|
|
345
|
+
k : int, optional
|
|
346
|
+
Number of base terms in the model; k=1 for single-band, k>1 for multiband. Default is 1.
|
|
347
|
+
|
|
348
|
+
Returns
|
|
349
|
+
-------
|
|
350
|
+
tuple
|
|
351
|
+
- BS_df : pandas.DataFrame
|
|
352
|
+
The last bootstrap-resampled residuals DataFrame.
|
|
353
|
+
- Nbs : int
|
|
354
|
+
Number of bootstrap samples whose estimated period is within 1% of the original period.
|
|
355
|
+
|
|
356
|
+
Notes
|
|
357
|
+
-----
|
|
358
|
+
- Performs 25 bootstrap resamples by default.
|
|
359
|
+
- For k=1, uses `get_period_estimate`; for k>1, uses `get_multiband_period_estimate`.
|
|
360
|
+
"""
|
|
361
|
+
|
|
394
362
|
if k == 1:
|
|
395
363
|
sg, w, _ = get_period_estimate(resid_df)
|
|
396
364
|
Pog = 48 / sg[2][0] # in hours
|