Skip to content

Commit dfe5dba

Browse files
hombitclaude
andauthored
Bump light-curve-feature to 0.15.0 (#701)
* Bump light-curve-feature to 0.15.0 * Add phase_features to Periodogram, Chi2Pvar and LaflerKinmanStringLength Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a7e20ae commit dfe5dba

6 files changed

Lines changed: 120 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
--
12+
- `Periodogram` now accepts a `phase_features` argument: features extracted from the light curve
13+
phase-folded at the best period
14+
([#701](https://github.com/light-curve/light-curve-python/pull/701)).
15+
- New feature `Chi2Pvar`: probability of variability from the chi-squared test
16+
([#701](https://github.com/light-curve/light-curve-python/pull/701)).
17+
- New feature `LaflerKinmanStringLength`: smoothness of the light curve
18+
([#701](https://github.com/light-curve/light-curve-python/pull/701)).
1319

1420
### Changed
1521

16-
--
22+
- **Breaking** Underlying `nuts-rs` crate has updated, so specific non-linear optimization results may change https://github.com/light-curve/light-curve-python/pull/701
1723

1824
### Deprecated
1925

light-curve/Cargo.lock

Lines changed: 63 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

light-curve/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ arrow-schema = "58"
7575
comfy-table = "7"
7676

7777
[dependencies.light-curve-feature]
78-
version = "0.14.0"
78+
version = "0.15.0"
7979
default-features = false
8080

8181
[dependencies.light-curve-dmdt]

light-curve/src/features.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,8 @@ transform : None
16111611
}
16121612
}
16131613

1614+
evaluator!(Chi2Pvar, lcf::Chi2Pvar, StockTransformer::Identity);
1615+
16141616
evaluator!(Cusum, lcf::Cusum, StockTransformer::Identity);
16151617

16161618
evaluator!(Eta, lcf::Eta, StockTransformer::Identity);
@@ -1673,6 +1675,12 @@ quantile : positive float
16731675

16741676
evaluator!(Kurtosis, lcf::Kurtosis, StockTransformer::Arcsinh);
16751677

1678+
evaluator!(
1679+
LaflerKinmanStringLength,
1680+
lcf::LaflerKinmanStringLength,
1681+
StockTransformer::Identity
1682+
);
1683+
16761684
evaluator!(LinearFit, lcf::LinearFit, StockTransformer::Identity);
16771685

16781686
evaluator!(LinearTrend, lcf::LinearTrend, StockTransformer::Identity);
@@ -1932,6 +1940,7 @@ impl Periodogram {
19321940
freqs: Option<Bound<PyAny>>,
19331941
fast: Option<bool>,
19341942
features: Option<Bound<PyAny>>,
1943+
phase_features: Option<Bound<PyAny>>,
19351944
normalization: PeriodogramNormalization,
19361945
) -> PyResult<(LcfPeriodogram<f32>, LcfPeriodogram<f64>)> {
19371946
let mut eval_f32 = match peaks {
@@ -2065,8 +2074,16 @@ impl Periodogram {
20652074
if let Some(features) = features {
20662075
for x in features.try_iter()? {
20672076
let py_feature = x?.cast::<PyFeatureEvaluator>()?.borrow();
2068-
eval_f32.add_feature(py_feature.feature_evaluator_f32.clone());
2069-
eval_f64.add_feature(py_feature.feature_evaluator_f64.clone());
2077+
eval_f32.add_spectrum_feature(py_feature.feature_evaluator_f32.clone());
2078+
eval_f64.add_spectrum_feature(py_feature.feature_evaluator_f64.clone());
2079+
}
2080+
}
2081+
2082+
if let Some(phase_features) = phase_features {
2083+
for x in phase_features.try_iter()? {
2084+
let py_feature = x?.cast::<PyFeatureEvaluator>()?.borrow();
2085+
eval_f32.add_phase_feature(py_feature.feature_evaluator_f32.clone());
2086+
eval_f64.add_phase_feature(py_feature.feature_evaluator_f64.clone());
20702087
}
20712088
}
20722089

@@ -2127,6 +2144,7 @@ impl Periodogram {
21272144
freqs = None,
21282145
fast = true,
21292146
features = None,
2147+
phase_features = None,
21302148
normalization = "psd",
21312149
transform = None,
21322150
))]
@@ -2138,6 +2156,7 @@ impl Periodogram {
21382156
freqs: Option<Bound<PyAny>>,
21392157
fast: Option<bool>,
21402158
features: Option<Bound<PyAny>>,
2159+
phase_features: Option<Bound<PyAny>>,
21412160
normalization: &str,
21422161
transform: Option<Bound<PyAny>>,
21432162
) -> PyResult<(Self, PyFeatureEvaluator)> {
@@ -2155,6 +2174,7 @@ impl Periodogram {
21552174
freqs,
21562175
fast,
21572176
features,
2177+
phase_features,
21582178
normalization,
21592179
)?;
21602180
Ok((
@@ -2238,9 +2258,14 @@ fast : bool or None, optional
22382258
Use "Fast" (approximate and FFT-based) or direct periodogram algorithm,
22392259
default is {default_fast}
22402260
features : iterable or None, optional
2241-
Features to extract from periodogram considering it as a time-series,
2242-
default is None which means no additional features
2243-
Features to extract from periodogram considering it as a time-series
2261+
Features extracted from the periodogram power spectrum, treating it as a
2262+
time-series (frequency as time, power as magnitude).
2263+
Default is None which means no additional spectrum features.
2264+
phase_features : iterable or None, optional
2265+
Features to extract from the light curve phase-folded at the best period.
2266+
Phase runs from 0 to 1 with phase 0 at the magnitude minimum.
2267+
Feature names are prefixed with `period_folded_`.
2268+
Default is None which means no phase features.
22442269
normalization : str, optional
22452270
Normalization of the periodogram power. Affects `power()`,
22462271
`freq_power()`, and feature extraction via `__call__()`.

light-curve/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fn light_curve(py: Python, m: Bound<PyModule>) -> PyResult<()> {
5858
m.add_class::<f::BazinFit>()?;
5959
m.add_class::<f::BeyondNStd>()?;
6060
m.add_class::<f::Bins>()?;
61+
m.add_class::<f::Chi2Pvar>()?;
6162
m.add_class::<f::Cusum>()?;
6263
m.add_class::<f::Duration>()?;
6364
m.add_class::<f::Eta>()?;
@@ -66,6 +67,7 @@ fn light_curve(py: Python, m: Bound<PyModule>) -> PyResult<()> {
6667
m.add_class::<f::JsonDeserializedFeature>()?;
6768
m.add_class::<f::InterPercentileRange>()?;
6869
m.add_class::<f::Kurtosis>()?;
70+
m.add_class::<f::LaflerKinmanStringLength>()?;
6971
m.add_class::<f::LinearFit>()?;
7072
m.add_class::<f::LinearTrend>()?;
7173
m.add_class::<f::LinexpFit>()?;

light-curve/tests/light_curve_ext/test_periodogram.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from numpy.testing import assert_allclose
44
from scipy.signal import lombscargle
55

6-
from light_curve.light_curve_ext import Periodogram
6+
from light_curve.light_curve_ext import Duration, Periodogram
77

88

99
def test_vs_lombscargle():
@@ -121,6 +121,21 @@ def test_normalization_default_is_psd():
121121
assert_allclose(power_default, power_psd)
122122

123123

124+
def test_phase_features_duration():
125+
"""Duration of phase-folded dense light curve must be close to 1."""
126+
period = 2.0
127+
# Dense, regular sampling over many periods so the phase folds uniformly
128+
t = np.linspace(0, 100 * period, 10_000)
129+
m = np.sin(2 * np.pi * t / period)
130+
131+
fe = Periodogram(peaks=1, fast=True, phase_features=[Duration()])
132+
result = fe(t, m, sorted=True)
133+
134+
# result layout: period_0, snr_0, period_folded_duration
135+
period_folded_duration = result[-1]
136+
assert_allclose(period_folded_duration, 1.0, atol=1e-3)
137+
138+
124139
def test_normalization_invalid():
125140
"""Invalid normalization string should raise ValueError."""
126141
with pytest.raises(ValueError, match="normalization must be one of"):

0 commit comments

Comments
 (0)