@@ -125,3 +125,134 @@ void example1D() {
125125 tree0 -> SetAlias ("mDCAr_A_NTracks_median_All" ,("getNearest1D(time, " + std ::to_string (mapID ) + ")" ).data ());
126126 tree0 -> Draw ("mTSITSTPC.mDCAr_A_NTracks_median:mDCAr_A_NTracks_median_All" ,"indexType==1" ,"" ,10000 );
127127}
128+ /* ------------------------------------------------------------------
129+ Statistics extension (non‑breaking) -------------------------------
130+ Added without changing previous API.
131+
132+ New options:
133+ • Enum‑based interface for better ROOT compatibility
134+ enum StatKind { kMean=0, kMedian=1, kStd=2 };
135+ float getStat(double x,int mapID,StatKind kind,double dx);
136+
137+ • Convenience thin wrappers for ROOT aliases
138+ getMean1D , getMedian1D , getStd1D
139+
140+ • cacheStat unchanged (uses strings internally)
141+
142+ ------------------------------------------------------------------*/
143+
144+ #include <vector>
145+ #include <algorithm>
146+ #include <numeric>
147+
148+ // --- enum for faster numeric calls --------------------------------
149+ enum StatKind { kMean = 0 , kMedian = 1 , kStd = 2 };
150+
151+ // Cache: stat → mapID → dx → (x → value)
152+ static std ::map < int , std ::map < double , std ::map < double ,float >>> cacheMean ;
153+ static std ::map < int , std ::map < double , std ::map < double ,float >>> cacheMedian ;
154+ static std ::map < int , std ::map < double , std ::map < double ,float >>> cacheStd ;
155+
156+ static float _mean (const std ::vector < float > & v ){ return v .empty ()?NAN :std ::accumulate (v .begin (),v .end (),0.0f )/v .size (); }
157+ static float _median (std ::vector < float > v ){ if (v .empty ()) return NAN ; size_t n = v .size ()/2 ; std ::nth_element (v .begin (),v .begin ()+ n ,v .end ()); return v [n ]; }
158+ static float _std (const std ::vector < float > & v ){ if (v .size ()< 2 ) return NAN ; float m = _mean (v ); double s2 = 0 ; for (float e :v ){ double d = e - m ; s2 += d * d ;} return std ::sqrt (s2 /(v .size ()- 1 )); }
159+
160+ //--------------------------------------------------------------------
161+ static float _computeStat (double x ,int mapID ,double dx ,StatKind k ){
162+ const auto itM = registeredMaps .find (mapID );
163+ if (itM == registeredMaps .end ()|| itM -> second .empty ()) return NAN ;
164+ const auto & mp = itM -> second ;
165+ std ::vector < float > buf ;
166+ for (auto it = mp .lower_bound (x - dx ); it != mp .end ()&& it -> first <=x + dx ; ++ it ) buf .push_back (it -> second );
167+ if (buf .empty ()) return NAN ;
168+ switch (k ){
169+ case kMean : return _mean (buf );
170+ case kMedian : return _median (buf );
171+ case kStd : return _std (buf );
172+ }
173+ return NAN ;
174+ }
175+
176+ //--------------------------------------------------------------------
177+ /**
178+ * @brief Return a local statistic (mean / median / std) around a query point.
179+ *
180+ * This version is preferred inside **TTree::Draw** because it uses an enum
181+ * instead of a string literal.
182+ *
183+ * @param x Center of the window (same coordinate used in the cache)
184+ * @param mapID ID returned by registerMap1D / registerMap1DByName
185+ * @param kind kMean (0), kMedian (1) or kStd (2)
186+ * @param dx Half‑window size: the statistic is computed from all points
187+ * with X in [x − dx, x + dx]
188+ *
189+ * Internally the first request builds (and caches) a map x → stat(x)
190+ * for the given (mapID, dx, kind). Subsequent calls are O(log N).
191+ */
192+ // Fast numeric interface (enum) ------------------------------------
193+ float getStat (double x ,int mapID ,StatKind kind ,double dx ){
194+ auto * pcache = (kind == kMean ? & cacheMean : (kind == kMedian ? & cacheMedian : & cacheStd ));
195+ auto & byMap = (* pcache )[mapID ];
196+ auto & byDx = byMap [dx ];
197+ if (byDx .empty ()){
198+ // build lazily for this dx
199+ const auto itM = registeredMaps .find (mapID );
200+ if (itM == registeredMaps .end ()) return NAN ;
201+ for (const auto & kv : itM -> second ){ double cx = kv .first ; byDx [cx ]= _computeStat (cx ,mapID ,dx ,kind );} }
202+ const auto & statMap = byDx ;
203+ auto it = statMap .lower_bound (x );
204+ if (it == statMap .begin ()) return it -> second ;
205+ if (it == statMap .end ()) return std ::prev (it )-> second ;
206+ auto prev = std ::prev (it );
207+ return (fabs (prev -> first - x )< fabs (it -> first - x )?prev -> second :it -> second );
208+ }
209+
210+ // String interface kept for backward compat.
211+ float getStat (double x ,int mapID ,const char * st ,double dx ){
212+ std ::string s (st );
213+ if (s == "mean" ) return getStat (x ,mapID ,kMean ,dx );
214+ if (s == "median" ) return getStat (x ,mapID ,kMedian ,dx );
215+ if (s == "std" || s == "sigma" ) return getStat (x ,mapID ,kStd ,dx );
216+ std ::cerr <<"[getStat] Unknown statType=" <<s <<std ::endl ; return NAN ;
217+ }
218+
219+ //--------------------------------------------------------------------
220+ // Convenience wrappers for ROOT Draw / numeric helpers -----------------
221+ inline float getMean1D (double x ,int id ,double dx ){ return getStat (x ,id ,kMean ,dx );} // mean
222+ inline float getMedian1D (double x ,int id ,double dx ){ return getStat (x ,id ,kMedian ,dx );} // median
223+ inline float getStd1D (double x ,int id ,double dx ){ return getStat (x ,id ,kStd ,dx );} // stddev
224+
225+ // Integer overload for ROOT (fix #2) --------------------------------
226+ inline float getStat (double x ,int id ,int kind ,double dx ){
227+ return getStat (x ,id ,static_cast < StatKind > (kind ),dx );
228+ }
229+
230+ //--------------------------------------------------------------------
231+ // Pre‑cache requested stats (by enum) ------------------------------- (by enum) -------------------------------
232+ bool cacheStat (int mapID ,const std ::vector < std ::string > & stats ,double dx ){
233+ for (const std ::string & s :stats ){
234+ if (s == "mean ") getStat(0,mapID,kMean ,dx); // lazy build
235+ else if (s == "median" ) getStat (0 ,mapID ,kMedian ,dx );
236+ else if (s == "std" || s == "sigma" ) getStat (0 ,mapID ,kStd ,dx );
237+ }
238+ return true;
239+ }
240+
241+ //--------------------------------------------------------------------
242+ /// Example: statistics with enum wrappers
243+ void exampleStat1D (){
244+ TFile * f = TFile ::Open ("timeSeries10000_apass5.root" );
245+ TTree * t = (TTree * )f -> Get ("timeSeries" );
246+ int id = registerMap1DByName ("dcar_time_stat" ,"time" ,"mTSITSTPC.mDCAr_A_NTracks_median" ,t ,"subentry==127" );
247+
248+ // Pre‑cache mean & std for ±200 window
249+ cacheStat (id ,{"mean" ,"std" },200 );
250+
251+ // Use integer selector (0 = mean, 2 = std). This avoids any ROOT
252+ // overload ambiguity and works in TTree::Draw directly.
253+ t -> SetAlias ("dcar_mean" , Form ("getStat(time,%d,0,200)" , id )); // 0 → kMean
254+ t -> SetAlias ("dcar_sigma" , Form ("getStat(time,%d,2,200)" , id )); // 2 → kStd
255+
256+ t -> Draw ("mTSITSTPC.mDCAr_A_NTracks_median:dcar_mean" ,"indexType==1" ,"colz" ,10000 );
257+ t -> Draw ("getStat(time,591487517, 0 ,10000+0):getStat(time,591487517, 1 ,10000+0)" ,"indexType==1" ,"colz" ,100000 );
258+ }
0 commit comments