@@ -13,6 +13,20 @@ use crate::periodogram::{self, NyquistFreq, PeriodogramPower};
1313use ndarray:: Array1 ;
1414use std:: fmt:: Debug ;
1515
16+ /// Normalisation of periodogram across passbands
17+ #[ derive( Clone , Debug , Serialize , Deserialize , JsonSchema ) ]
18+ pub enum MultiColorPeriodogramNormalisation {
19+ /// Weight individual periodograms by the number of observations in each passband.
20+ /// Useful if no weight is given to observations
21+ Count ,
22+ /// Weight individual periodograms by $\chi^2 = \sum \left(\frac{m_i - \bar{m}}{\delta_i}\right)^2$
23+ ///
24+ /// Be aware that if no weight are given to observations
25+ /// (i.e. via [TimeSeries::new_without_weight]) unity weights are assumed and this is NOT
26+ /// equivalent to [::Count], but weighting by magnitude variance.
27+ Chi2 ,
28+ }
29+
1630/// Multi-passband periodogram
1731#[ derive( Clone , Debug , Serialize , Deserialize , JsonSchema ) ]
1832#[ serde(
@@ -25,14 +39,26 @@ where
2539{
2640 // We use it to not reimplement some internals
2741 monochrome : Periodogram < T , F > ,
28- properties : Box < EvaluatorProperties > ,
42+ normalization : MultiColorPeriodogramNormalisation ,
2943}
3044
3145impl < T , F > MultiColorPeriodogram < T , F >
3246where
3347 T : Float ,
34- F : FeatureEvaluator < T > ,
48+ F : FeatureEvaluator < T > + From < PeriodogramPeaks > ,
3549{
50+ pub fn new ( peaks : usize , normalization : MultiColorPeriodogramNormalisation ) -> Self {
51+ let monochrome = Periodogram :: with_name_description (
52+ peaks,
53+ "multicolor_periodogram" ,
54+ "of multi-color periodogram (interpreting frequency as time, power as magnitude)" ,
55+ ) ;
56+ Self {
57+ monochrome,
58+ normalization,
59+ }
60+ }
61+
3662 #[ inline]
3763 pub fn default_peaks ( ) -> usize {
3864 PeriodogramPeaks :: default_peaks ( )
@@ -103,19 +129,53 @@ where
103129 ' a : ' mcts ,
104130 P : PassbandTrait ,
105131 {
106- let unnormed_power = mcts
107- . mapping_mut ( )
132+ let ts_weights = {
133+ let mut a: Array1 < _ > = match self . normalization {
134+ MultiColorPeriodogramNormalisation :: Count => {
135+ mcts. mapping_mut ( ) . values ( ) . map ( |ts| ts. lenf ( ) ) . collect ( )
136+ }
137+ MultiColorPeriodogramNormalisation :: Chi2 => mcts
138+ . mapping_mut ( )
139+ . values_mut ( )
140+ . map ( |ts| ts. get_m_chi2 ( ) )
141+ . collect ( ) ,
142+ } ;
143+ let norm = a. sum ( ) ;
144+ if norm. is_zero ( ) {
145+ match self . normalization {
146+ MultiColorPeriodogramNormalisation :: Count => {
147+ return Err ( MultiColorEvaluatorError :: all_time_series_short (
148+ mcts. mapping_mut ( ) ,
149+ self . min_ts_length ( ) ,
150+ ) ) ;
151+ }
152+ MultiColorPeriodogramNormalisation :: Chi2 => {
153+ return Err ( MultiColorEvaluatorError :: AllTimeSeriesAreFlat ) ;
154+ }
155+ }
156+ }
157+ a /= norm;
158+ a
159+ } ;
160+ mcts. mapping_mut ( )
108161 . values_mut ( )
109- . filter ( |ts| self . monochrome . check_ts_length ( ts) . is_ok ( ) )
110- . map ( |ts| p. power ( ts) * ts. lenf ( ) )
111- . reduce ( |acc, x| acc + x)
162+ . zip ( ts_weights. iter ( ) )
163+ . filter ( |( ts, _ts_weight) | self . monochrome . check_ts_length ( ts) . is_ok ( ) )
164+ . map ( |( ts, & ts_weight) | {
165+ let mut power = p. power ( ts) ;
166+ power *= ts_weight;
167+ power
168+ } )
169+ . reduce ( |mut acc, power| {
170+ acc += & power;
171+ acc
172+ } )
112173 . ok_or_else ( || {
113174 MultiColorEvaluatorError :: all_time_series_short (
114175 mcts. mapping_mut ( ) ,
115- self . monochrome . min_ts_length ( ) ,
176+ self . min_ts_length ( ) ,
116177 )
117- } ) ?;
118- Ok ( unnormed_power / mcts. total_lenf ( ) )
178+ } )
119179 }
120180
121181 pub fn power < ' slf , ' a , ' mcts , P > (
@@ -174,7 +234,7 @@ where
174234 <F as TryInto < PeriodogramPeaks > >:: Error : Debug ,
175235{
176236 fn get_info ( & self ) -> & EvaluatorInfo {
177- & self . properties . info
237+ self . monochrome . get_info ( )
178238 }
179239}
180240
@@ -185,15 +245,11 @@ where
185245 <F as TryInto < PeriodogramPeaks > >:: Error : Debug ,
186246{
187247 fn get_names ( & self ) -> Vec < & str > {
188- self . properties . names . iter ( ) . map ( String :: as_str ) . collect ( )
248+ self . monochrome . get_names ( )
189249 }
190250
191251 fn get_descriptions ( & self ) -> Vec < & str > {
192- self . properties
193- . descriptions
194- . iter ( )
195- . map ( String :: as_str)
196- . collect ( )
252+ self . monochrome . get_descriptions ( )
197253 }
198254}
199255
0 commit comments