1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU Lesser General Public License as published
7 //  by the Free Software Foundation; either version 3.0 of the License, or
8 //  any later version.
9 //
10 //  The GPSTk is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU Lesser General Public License for more details.
14 //
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with GPSTk; if not, write to the Free Software Foundation,
17 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 //  This software was developed by Applied Research Laboratories at the
20 //  University of Texas at Austin.
21 //  Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 //  This software was developed by Applied Research Laboratories at the
28 //  University of Texas at Austin, under contract to an agency or agencies
29 //  within the U.S. Department of Defense. The U.S. Government retains all
30 //  rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 //  Pursuant to DoD Directive 523024
33 //
34 //  DISTRIBUTION STATEMENT A: This software has been approved for public
35 //                            release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 /// @file Stats.hpp
40 /// Conventional, sequential and weighted one-sample, and two-sample statistics
41 
42 #ifndef INCLUDE_GPSTK_STATS_INCLUDE
43 #define INCLUDE_GPSTK_STATS_INCLUDE
44 
45 #include <string>
46 #include <sstream>
47 #include <vector>
48 #include <algorithm>
49 #include "Exception.hpp"
50 #include "MiscMath.hpp"
51 #include "Vector.hpp"
52 
53 namespace gpstk
54 {
55    /** @addtogroup math */
56    //@{
57 
58    /// Compute the median of a gpstk::Vector
median(const Vector<T> & v)59    template <class T> inline T median(const Vector<T>& v)
60    {
61       const int n(v.size());
62       if(n==0) return T();
63       if(n==1) return v(0);
64       if(n==2) return (v(0)+v(1))/T(2);
65       // insert sort
66       int i,j;
67       T x;
68       Vector<T> w(v);
69       for(i=0; i<n; i++) {
70          x = w[i] = v(i);
71          j = i-1;
72          while(j>=0 && x<w[j]) {
73             w[j+1] = w[j];
74             j--;
75          }
76          w[j+1] = x;
77       }
78 
79       if(n % 2) return (w[(n+1)/2-1]);
80       return ((w[n/2-1]+w[n/2])/T(2));
81 
82    }  // end median(Vector)
83 
84    /// median absolute deviation of a gpstk::Vector
mad(const gpstk::Vector<T> & v)85    template <class T> inline T mad(const gpstk::Vector<T>& v)
86    {
87       if (v.size() < 2) return T();
88 
89       double med = gpstk::median(v);
90       gpstk::Vector<T> w(v);
91       for(size_t i=0; i < w.size(); i++)
92          w[i] = std::abs(w[i]- med);
93 
94       return gpstk::median(w);
95    }  // end mad(Vector)
96 
97    /// Compute the median of a std::vector
median(const std::vector<T> & v)98    template <class T> inline T median(const std::vector<T>& v)
99    {
100       const unsigned int n(v.size());
101       if(n==0) return T();
102 
103       std::vector<T> w(v);
104       std::sort(w.begin(), w.end());
105 
106       if (n % 2) return w[(n+1)/2-1];
107       return ((w[n/2-1]+w[n/2])/T(2));
108    }  // end median(vector)
109 
110    /// median absolute deviation of a std::vector
mad(const std::vector<T> & v)111    template <class T> inline T mad(const std::vector<T>& v)
112    {
113       if (v.size() < 2) return T();
114 
115       double med = gpstk::median(v);
116       std::vector<T> w(v);
117       for(size_t i=0; i < w.size(); i++)
118          w[i] = std::abs(w[i]- med);
119 
120       return gpstk::median(w);
121    }  // end mad(vector)
122 
123    //---------------------------------------------------------------------------
124    //---------------------------------------------------------------------------
125    // forward declaration
126    template <class T> class TwoSampleStats;
127 
128    //---------------------------------------------------------------------------
129    /// Conventional statistics for one sample, with scaling to improve numerical
130    /// error in cases of very large numbers. Constructor does the same as Reset();
131    /// use it when starting a new series of input samples.
132    /// Results are available at any time by calling N(), Minimum(), Maximum(),
133    /// Average(), Variance() and StdDev(). Also the scale is available in Scale().
134    /// NB. Variance is normalized with 1/(N-1) and StdDev is sqrt(Variance).
135    /// NB. This class is not intended to be used with non-floating types,
136    ///     for which it may yield incorrect results.
137    template <class T> class Stats
138    {
139    public:
140       friend class TwoSampleStats<T>;
141 
142       /// constructor
Stats()143       Stats() { Reset(); }
144 
145       /// reset, i.e. ignore earlier data and restart sampling
Reset(void)146       inline void Reset(void)
147       {
148          n = 0;
149          setScale = false;
150          scale = T(1);
151          sum = sum2 = min = max = T();
152       }
153 
154       // add and subtract ---------------------------------------------------
155 
156       /// add a single sample to the computation of statistics
157       /// NB this is the fundamental Add routine; all other Add's call this
Add(const T & x)158       void Add(const T& x)          // Stats
159       {
160          if(n == 0) {
161             sum = sum2 = T();
162             min = max = x;
163             scale = T(1);
164             setScale = false;
165          }
166          if(!setScale && x!=T()) { scale=::fabs(x); setScale = true; }
167          sum += x/scale;
168          sum2 += (x/scale)*(x/scale);
169          if(x < min) min=x;
170          if(x > max) max=x;
171          n++;
172       }
173 
174       /// remove a sample from the computation of statistics.
175       /// NB this is the fundamental Subtract routine; all others call this
176       /// NB. Assumes that this sample was previously added.
177       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(const T & x)178       void Subtract(const T& x)           // Stats
179       {
180          if(n < 1) return;       // TD throw
181          if(n == 1) { n = 0; return; }
182          T sx(x/scale);
183          sum -= sx;
184          sum2 -= sx*sx;
185          n--;
186       }
187 
188       /// Add gpstk::Vector<T> of data to the statistics
Add(const Vector<T> & X)189       void Add(const Vector<T>& X)                 // Stats
190       {
191          for(size_t i=0; i < X.size(); i++) Add(X(i));
192       }
193 
194       /// add a std::vector<T> of samples to the computation of statistics
Add(std::vector<T> & X)195       inline void Add(std::vector<T>& X)           // Stats
196       {
197          for(size_t i=0; i<X.size(); i++) Add(X[i]);
198       }
199 
200       /// Subtract gpstk::Vector<T> of data to the statistics
201       /// NB. Assumes that these samples were previously added.
202       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(const Vector<T> & X)203       void Subtract(const Vector<T>& X)            // Stats
204       {
205          for(size_t i=0; i < X.size(); i++) Subtract(X(i));
206       }
207 
208       /// remove a std::vector<T> of samples to the computation of statistics
209       /// NB. Assumes that these samples were previously added.
210       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(std::vector<T> & X)211       inline void Subtract(std::vector<T>& X)       // Stats
212       {
213          for(size_t i=0; i<X.size(); i++) Subtract(X[i]);
214       }
215 
216       // combine two Stats objects -------------------------------------
217 
218       /// combine two Stats (assumed taken from the same or equivalent ensembles)
operator +=(const Stats<T> & S)219       Stats<T>& operator+=(const Stats<T>& S)
220       {
221          if(S.n == 0)
222             return *this;
223          if(!setScale) { setScale=true; scale = S.scale; }
224          // TD what if both have !setScale?
225          if((n == 0) || (S.min < min)) min=S.min;
226          if((n == 0) || (S.max > max)) max=S.max;
227          sum += S.scale*S.sum/scale;
228          sum2 += (S.scale/scale)*(S.scale/scale)*S.sum2;
229          n += S.n;
230          return *this;
231       }
232 
233       /// remove one Stats from another, assumed to be taken from the same or
234       /// equivalent ensembles.
235       /// NB. Assumes that these samples were previously added.
236       /// NB. Minimum() and Maximum() may no longer be valid.
operator -=(const Stats<T> & S)237       Stats<T>& operator-=(const Stats<T>& S)
238       {
239          if(n <= S.n) { Reset(); return *this; }
240          sum -= S.scale*S.sum/scale;
241          sum2 -= (S.scale/scale)*(S.scale/scale)*S.sum2;
242          n -= S.n;
243          return *this;
244       }
245 
246       // dump and reload -----------------------------------------------
247 
248       /// Dump Stats private members directly;
249       /// useful in saving an object (e.g. to a file); reload with Load().
250       /// @param vuint output vector<unsigned int> of length 2, input of Load()
251       /// @param vT output vector<T> of length 5, input of Load()
Dump(std::vector<unsigned int> & vuint,std::vector<T> & vT)252       void Dump(std::vector<unsigned int>& vuint, std::vector<T>& vT)
253       {
254          vuint.clear();
255          vuint.push_back(n); vuint.push_back(setScale ? 1:0);
256 
257          vT.clear();
258          vT.push_back(setScale ? scale : T(0));
259          vT.push_back(min); vT.push_back(max);
260          vT.push_back(sum); vT.push_back(sum2);
261       }
262 
263       /// Define Stats private members directly; useful in continuing
264       /// with an object that was earlier saved (e.g. to a file) using Dump().
265       /// @param vuint input vector<unsigned int> of length 2, output of Dump()
266       /// @param vT input vector<T> of length 5, output of Dump()
267       /// NB no checking at all - caller has burden of validity
268       /// NB zero-fill rather than throwing
Load(const std::vector<unsigned int> & vuint,const std::vector<T> & vT)269       void Load(const std::vector<unsigned int>& vuint, const std::vector<T>& vT)
270       {
271          if(vuint.size() < 2 || vT.size() < 4) {
272             n = 0;
273             setScale = false;
274             scale = T(0);
275             min = T(0); max = T(0);
276             sum = T(0); sum2 = T(0);
277          }
278          else {
279             n = vuint[0];
280             setScale = (vuint[1] != 0);
281             scale = vT[0];
282             min = vT[1]; max = vT[2];
283             sum = vT[3]; sum2 = vT[4];
284          }
285       }
286 
287       // output ---------------------------------------------------
288 
289       /// Write Stats to a single-line string
asString(std::string msg=std::string (),int w=7,int p=4) const290       std::string asString(std::string msg=std::string(), int w=7, int p=4) const
291       {
292          std::ostringstream oss;
293          //static const int p=4, w=7;
294          oss << "stats(con):" << (msg.empty() ? "" : " "+msg)
295             << " N " << std::setw(w) << N() << std::fixed << std::setprecision(p)
296             << "  Ave " << std::setw(w) << Average()
297             << "  Std " << std::setw(w) << StdDev()
298             << "  Var " << std::setw(w) << Variance()
299             << "  Min " << std::setw(w) << Minimum()
300             << "  Max " << std::setw(w) << Maximum()
301             << "  P2P " << std::setw(w) << Maximum()-Minimum();
302          return oss.str();
303       }
304 
305       /// Write Stats N,ave,sig to a short single-line string
asShortString(std::string msg=std::string (),int w=0,int p=3) const306       std::string asShortString(std::string msg=std::string(), int w=0, int p=3) const
307       {
308          std::ostringstream oss;
309          oss << msg << std::fixed << std::setprecision(p)
310             << " N " << std::setw(w) << N()
311             << "  Ave " << std::setw(w) << Average()
312             << "  Std " << std::setw(w) << StdDev();
313          return oss.str();
314       }
315 
316       // the rest are just accessors --------------------------------------
317       /// return the sample size
N(void) const318       inline unsigned int N(void) const { return n; }
319 
320       /// return minimum value
Minimum(void) const321       inline T Minimum(void) const
322          { if(n) return min; else return T(); }
323 
324       /// return maximum value
Maximum(void) const325       inline T Maximum(void) const
326          { if(n) return max; else return T(); }
327 
328       /// return the average
Average(void) const329       inline T Average(void) const           // Stats
330       {
331          if(n>0)
332             return (scale*sum/T(n));
333          else
334             return T();
335       }
336 
337       /// return computed variance
Variance(void) const338       inline T Variance(void) const          // Stats
339       {
340          if(n>1)
341             return scale*scale*(sum2-sum*sum/T(n))/T(n-1);
342          else
343             return T();
344       }
345 
346       /// return computed standard deviation
StdDev(void) const347       inline T StdDev(void) const
348          { if(n <= 1) return T(); return SQRT(Variance()); }
349 
350       /// return the scale
Scale(void) const351       inline T Scale(void) const
352          { if(n) return scale; else return T(); }
353 
354    protected:
355       unsigned int n;   ///< number of samples added to the statistics
356       bool setScale;    ///< scale has been set to non-zero
357       T scale;          ///< scale
358       T min;            ///< Minimum value
359       T max;            ///< Maximum value
360       T sum;            ///< sum of x
361       T sum2;           ///< sum of x squared
362 
363    }; // end class Stats
364 
365    /// Output operator for Stats class
operator <<(std::ostream & s,const Stats<T> & ST)366    template <class T> std::ostream& operator<<(std::ostream& s, const Stats<T>& ST)
367    {
368       std::ofstream savefmt;
369       savefmt.copyfmt(s);
370       s << "       N = " << ST.N() << " (1-sample statistics)\n";
371       s << " Minimum = "; s.copyfmt(savefmt); s << ST.Minimum();
372       s << " Maximum = "; s.copyfmt(savefmt); s << ST.Maximum() << "\n";
373       s << " Average = "; s.copyfmt(savefmt); s << ST.Average();
374       s << " Std Dev = "; s.copyfmt(savefmt); s << ST.StdDev();
375       s << " Variance = "; s.copyfmt(savefmt); s << ST.Variance(); // temp
376       return s;
377    }
378 
379    //---------------------------------------------------------------------------
380    //---------------------------------------------------------------------------
381    /// Sequential conventional statistics for one sample; gives results identical
382    /// to class Stats except there is no scaling. This class maintains a current
383    /// average and variance with each Add(); thus it is more efficient when results
384    /// at each step are accessed - use this class when stats are to be accessed often,
385    /// e.g. after each Add().
386    /// Class Stats holds sum(x) and sum(x squared) and computes ave, sig etc on call.
387    /// NB. class WtdStats (weighted stats) derives from this class.
388    template <class T> class SeqStats
389    {
390    public:
391       /// constructor
SeqStats()392       SeqStats() : n(0) { }
393 
394       /// reset, i.e. ignore earlier data and restart sampling
Reset(void)395       inline void Reset(void) { n=0; }
396 
397       /// constructor given a gpstk::Vector<T> of data
SeqStats(Vector<T> & X)398       SeqStats(Vector<T>& X) { n = 0; Add(X); }
399 
400       // add and subtract ---------------------------------------------------
401 
402       /// add a single sample to the computation of statistics)
403       /// NB this is the fundamental Add routine; all other Add's call this
Add(const T & x)404       void Add(const T& x)                // SeqStats
405       {
406          if(n == 0) {
407             min = max = ave = x;
408             var = T();
409          }
410          else {
411             if(x < min) min=x;
412             if(x > max) max=x;
413          }
414 
415          ave += (x-ave)/T(n + 1);
416          if(n > 0) var = T(n)*var/T(n + 1) + (x-ave)*(x-ave)/T(n);
417 
418          n++;
419       }
420 
421       /// remove a sample from the computation of statistics
422       /// NB this is the fundamental Subtract routine; all others call this
423       /// NB. Assumes that this sample was previously added.
424       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(const T x)425       void Subtract(const T x)                  // SeqStats
426       {
427          if(n == 0) return;
428          if(n > 1)
429             var = (var - (x-ave)*(x-ave)/T(n-1))*T(n)/(T(n)-T(1));
430          else
431             var = T();
432          ave = T(n)*(ave - x/T(n))/(T(n)-T(1));
433          n--;
434       }
435 
436       /// add a gpstk::Vector<T> of samples to the computation of statistics
Add(Vector<T> & X)437       inline void Add(Vector<T>& X)                // SeqStats
438       {
439          for(size_t i=0; i<X.size(); i++) Add(X(i));
440       }
441 
442       /// add a std::vector<T> of samples to the computation of statistics
Add(std::vector<T> & X)443       inline void Add(std::vector<T>& X)                 // SeqStats
444       {
445          for(size_t i=0; i<X.size(); i++) Add(X[i]);
446       }
447 
448       /// remove a gpstk::Vector<T> of samples to the computation of statistics
449       /// NB. Assumes that these samples were previously added.
450       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(Vector<T> & X)451       inline void Subtract(Vector<T>& X)                 // SeqStats
452       {
453          for(size_t i=0; i<X.size(); i++) Subtract(X(i));
454       }
455 
456       /// remove a std::vector<T> of samples to the computation of statistics
457       /// NB. Assumes that these samples were previously added.
458       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(std::vector<T> & X)459       inline void Subtract(std::vector<T>& X)                  // SeqStats
460       {
461          for(size_t i=0; i<X.size(); i++) Subtract(X[i]);
462       }
463 
464       // combine two Stats objects -------------------------------------
465 
466       /// combine two SeqStats (assumed taken from the same or equivalent ensembles);
operator +=(const SeqStats<T> & S)467       SeqStats<T>& operator+=(const SeqStats<T>& S)
468       {
469          if(S.n == 0) return *this;
470          if(n==0) { *this = S; return *this; }
471 
472          if(S.min < min) min=S.min;
473          if(S.max > max) max=S.max;
474 
475          T newave = T(n)*ave + T(S.n)*S.ave;
476          T newvar = T(n)*var + T(S.n)*S.var + T(n)*ave*ave + T(S.n)*S.ave*S.ave;
477          ave = newave/T(n+S.n);
478          var = newvar/T(n+S.n) - ave*ave;
479          n += S.n;
480 
481          return *this;
482       }  // end SeqStats operator+=
483 
484       /// remove one SeqStats from another, assumed to be taken from the same or
485       /// equivalent ensembles.
486       /// NB. Assumes that these samples were previously added.
487       /// NB. Minimum() and Maximum() may no longer be valid.
operator -=(const SeqStats<T> & S)488       SeqStats<T>& operator-=(const SeqStats<T>& S)
489       {
490          if(n <= S.n) { n=0; return *this; }
491 
492          T newave = T(n)*ave - T(S.n)*S.ave;
493          T newvar = T(n)*var - T(S.n)*S.var + T(n)*ave*ave - T(S.n)*S.ave*S.ave;
494          ave = newave/T(n-S.n);
495          var = newvar/T(n-S.n) - ave*ave;
496          n -= S.n;
497 
498          return *this;
499       }  // end SeqStats operator-=
500 
501       // dump and reload -----------------------------------------------
502 
503       /// Dump SeqStats private members directly;
504       /// useful in saving an object (e.g. to a file); reload with Load().
505       /// @param vuint output vector<unsigned int> of length 1, input of Load()
506       /// @param vT output vector<T> of length 4, input of Load()
Dump(std::vector<unsigned int> & vuint,std::vector<T> & vT)507       void Dump(std::vector<unsigned int>& vuint, std::vector<T>& vT)
508       {
509          vuint.clear();
510          vuint.push_back(n);
511 
512          vT.clear();
513          vT.push_back(min); vT.push_back(max);
514          vT.push_back(ave); vT.push_back(var);
515       }
516 
517       /// Define SeqStats private members directly; useful in continuing
518       /// with an object that was earlier saved (e.g. to a file) using Dump().
519       /// @param vuint input vector<unsigned int> of length 1, output of Dump()
520       /// @param vT input vector<T> of length 4, output of Dump()
521       /// NB no checking at all - caller has burden of validity
522       /// NB zero-fill rather than throwing
Load(const std::vector<unsigned int> & vuint,const std::vector<T> & vT)523       void Load(const std::vector<unsigned int>& vuint, const std::vector<T>& vT)
524       {
525          if(vuint.size() < 1 || vT.size() < 4) {
526             n = 0;
527             min = T(0); max = T(0);
528             ave = T(0); var = T(0);
529          }
530          else {
531             n = vuint[0];
532             min = vT[0]; max = vT[1];
533             ave = vT[2]; var = vT[3];
534          }
535       }
536 
537       // output ---------------------------------------------------
538 
539       /// Write SeqStats to a single-line string
asString(std::string msg=std::string (),int w=7,int p=4) const540       std::string asString(std::string msg=std::string(), int w=7, int p=4) const
541       {
542          std::ostringstream oss;
543          //static const int p=4, w=7;
544          oss << "stats(seq):" << (msg.empty() ? "" : " "+msg)
545             << " N " << std::setw(w) << N() << std::fixed << std::setprecision(p)
546             << "  Ave " << std::setw(w) << Average()
547             << "  Std " << std::setw(w) << StdDev()
548             << "  Var " << std::setw(w) << Variance()
549             << "  Min " << std::setw(w) << Minimum()
550             << "  Max " << std::setw(w) << Maximum()
551             << "  P2P " << std::setw(w) << Maximum()-Minimum();
552          return oss.str();
553       }
554 
555       /// Write SeqStats N,ave,sig to a short single-line string
asShortString(std::string msg=std::string (),int w=0,int p=3) const556       std::string asShortString(std::string msg=std::string(), int w=0, int p=3) const
557       {
558          std::ostringstream oss;
559          oss << msg << std::fixed << std::setprecision(p)
560             << " N " << std::setw(w) << N()
561             << "  Ave " << std::setw(w) << Average()
562             << "  Std " << std::setw(w) << StdDev();
563          return oss.str();
564       }
565 
566       // accessors -------------------------------------------------------
567       /// return the sample size
N(void) const568       inline unsigned int N(void) const { return n; }
569 
570       /// return minimum value
Minimum(void) const571       inline T Minimum(void) const { if(n) return min; else return T(); }
572 
573       /// return maximum value
Maximum(void) const574       inline T Maximum(void) const { if(n) return max; else return T(); }
575 
576       /// return computed average
Average(void) const577       inline T Average(void) const { if(n == 0) return T(); return ave; }
578 
579       /// return computed variance
Variance(void) const580       inline T Variance(void) const       // SeqStats
581       {
582          if(n <= 1) return T();
583          return (T(n)*var/T(n-1));
584       }
585 
586       /// return computed standard deviation
StdDev(void) const587       inline T StdDev(void) const         // SeqStats
588       {
589          if(n <= 1) return T();
590          return SQRT(Variance());
591       }
592 
593    protected:
594       unsigned int n;   ///< number of samples added to the statistics
595       T min;            ///< Minimum value
596       T max;            ///< Maximum value
597       T ave;            ///< Average value
598       T var;            ///< Variance (square of the standard deviation)
599 
600    }; // end class SeqStats
601 
602    /// Output operator for SeqStats class
operator <<(std::ostream & s,const SeqStats<T> & ST)603    template <class T> std::ostream& operator<<(std::ostream& s, const SeqStats<T>& ST)
604    {
605       std::ofstream savefmt;
606       savefmt.copyfmt(s);
607       s << "       N = " << ST.N() << " (1-sample stats, seq.impl.)\n";
608       s << " Minimum = "; s.copyfmt(savefmt); s << ST.Minimum();
609       s << " Maximum = "; s.copyfmt(savefmt); s << ST.Maximum() << "\n";
610       s << " Average = "; s.copyfmt(savefmt); s << ST.Average();
611       s << " Std Dev = "; s.copyfmt(savefmt); s << ST.StdDev();
612       s << " Variance = "; s.copyfmt(savefmt); s << ST.Variance(); // temp
613       return s;
614    }
615 
616    //---------------------------------------------------------------------------
617    //---------------------------------------------------------------------------
618    /// Weighted conventional statistics for one sample. Derived from SeqStats<T>
619    /// Weights must not be zero; zero weight causes the sample to be ignored.
620    template <class T> class WtdStats : public SeqStats<T>
621    {
622    public:
623       /// constructor
WtdStats()624       WtdStats() { }
625 
626       /// constructor given a gpstk::Vector<T> of data, with weights
WtdStats(Vector<T> & X,Vector<T> & W)627       WtdStats(Vector<T>& X, Vector<T>& W)
628       {
629          SeqStats<T>::n = 0;
630          Add(X,W);
631       }
632 
633       // add and subtract -------------------------------------
634 
635       /// add a single sample to the computation of statistics
636       /// NB this is the fundamental Add routine; all other Add's call this
637       /// NB input of zero weight causes the sample x to be ignored.
Add(const T & x,const T & wt_in)638       void Add(const T& x, const T& wt_in)      // WtdStats
639       {
640          T wt(::fabs(wt_in));
641          if(wt == T()) return;      // Don't add with zero weight
642 
643          if(SeqStats<T>::n == 0) {
644             SeqStats<T>::min = SeqStats<T>::max = SeqStats<T>::ave = x;
645             SeqStats<T>::var = T();
646             WtNorm = T();
647          }
648          else {
649             if(x < SeqStats<T>::min) SeqStats<T>::min=x;
650             if(x > SeqStats<T>::max) SeqStats<T>::max=x;
651          }
652 
653          SeqStats<T>::ave += (x-SeqStats<T>::ave)*(wt/(WtNorm+wt));
654          if(SeqStats<T>::n > 0)
655             SeqStats<T>::var = (WtNorm/(WtNorm+wt))*SeqStats<T>::var
656                            + (x-SeqStats<T>::ave)*(x-SeqStats<T>::ave)*(wt/WtNorm);
657          WtNorm += wt;
658 
659          SeqStats<T>::n++;
660       }
661 
662       /// remove a sample from the computation of statistics
663       /// NB input of zero weight causes the sample x to be ignored.
664       /// NB this is the fundamental Subtract routine; all others call this
665       /// NB. Assumes that this sample was previously added.
666       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(const T & x,const T & wt_in)667       void Subtract(const T& x, const T& wt_in)    // WtdStats
668       {
669          if(SeqStats<T>::n == 0) return;
670          T wt(::fabs(wt_in));
671          if(wt == T()) return;      // Don't add with zero weight
672 
673          T newave = WtNorm*SeqStats<T>::ave - wt*x;
674          T newvar = WtNorm*SeqStats<T>::var
675                         + WtNorm * SeqStats<T>::ave * SeqStats<T>::ave - wt*x*x;
676          WtNorm -= wt;
677          SeqStats<T>::ave = newave/WtNorm;
678          SeqStats<T>::var = newvar/WtNorm - SeqStats<T>::ave * SeqStats<T>::ave;
679          SeqStats<T>::n--;
680       }
681 
682       /// add a gpstk::Vector<T> of samples, with weights
Add(Vector<T> & X,Vector<T> & W)683       inline void Add(Vector<T>& X, Vector<T>& W)     // WtdStats
684       {
685          for(size_t i=0; i<(X.size()>W.size() ? W.size():X.size()); i++)
686             Add(X(i),W(i));
687       }
688 
689       /// add a std::vector<T> of samples, with weights
Add(std::vector<T> & X,std::vector<T> & W)690       inline void Add(std::vector<T>& X, std::vector<T>& W)    // WtdStats
691       {
692          for(size_t i=0; i<(X.size()>W.size() ? W.size():X.size()); i++)
693             Add(X(i),W(i));
694       }
695 
696       /// remove a gpstk::Vector<T> of samples, with weights
697       /// NB input of zero weight causes the sample x to be ignored.
698       /// NB. Assumes that this sample was previously added.
699       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(Vector<T> & X,Vector<T> & W)700       inline void Subtract(Vector<T>& X, Vector<T>& W)      // WtdStats
701       {
702          size_t i, nn(X.size() > W.size() ? W.size() : X.size());
703          for(i=0; i<nn; i++) Subtract(X(i),W(i));
704       }
705 
706       /// remove a std::vector<T> of samples, with weights
707       /// NB input of zero weight causes the sample x to be ignored.
708       /// NB. Assumes that this sample was previously added.
709       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(std::vector<T> & X,std::vector<T> & W)710       inline void Subtract(std::vector<T>& X, std::vector<T>& W)     // WtdStats
711       {
712          size_t i, nn(X.size() > W.size() ? W.size() : X.size());
713          for(i=0; i<nn; i++) Subtract(X(i),W(i));
714       }
715 
716       // combine two objects -------------------------------------------
717 
718       /// combine two WtdStats (assumed taken from the same or equivalent ensembles);
operator +=(const WtdStats<T> & S)719       WtdStats<T>& operator+=(const WtdStats<T>& S)
720       {
721          if(S.n == 0)
722             return *this;
723          if(SeqStats<T>::n == 0)
724             { *this = S; return *this; }
725 
726          //if(S.min < SeqStats<T>::min) SeqStats<T>::min = S.min; // gcc why!?!?
727          if(SeqStats<T>::min > S.min) SeqStats<T>::min = S.min;
728          if(S.max > SeqStats<T>::max) SeqStats<T>::max = S.max;
729 
730          T newave = WtNorm*SeqStats<T>::ave + S.WtNorm*S.ave;
731          T newvar = WtNorm*SeqStats<T>::var + S.WtNorm*S.var
732                     + WtNorm*SeqStats<T>::ave*SeqStats<T>::ave + S.WtNorm*S.ave*S.ave;
733          WtNorm += S.WtNorm;
734          SeqStats<T>::ave = newave/WtNorm;
735          SeqStats<T>::var = newvar/WtNorm - SeqStats<T>::ave*SeqStats<T>::ave;
736          SeqStats<T>::n += S.n;
737 
738          return *this;
739       }
740 
741       /// remove one WtdStats from another, assumed to be taken from the same or
742       /// equivalent ensembles.
743       /// NB. Assumes that this sample was previously added.
744       /// NB. Minimum() and Maximum() may no longer be valid.
operator -=(const WtdStats<T> & S)745       WtdStats<T>& operator-=(const WtdStats<T>& S)
746       {
747          if(SeqStats<T>::n <= S.n) { SeqStats<T>::n=0; return *this; }
748 
749          T newave = WtNorm*SeqStats<T>::ave - S.WtNorm*S.ave;
750          T newvar = WtNorm*SeqStats<T>::var-S.WtNorm*S.var
751                     + WtNorm*SeqStats<T>::ave*SeqStats<T>::ave - S.WtNorm*S.ave*S.ave;
752          WtNorm -= S.WtNorm;
753          SeqStats<T>::ave = newave/WtNorm;
754          SeqStats<T>::var = newvar/WtNorm - SeqStats<T>::ave*SeqStats<T>::ave;
755          SeqStats<T>::n -= S.n;
756 
757          return *this;
758 
759       }  // end WtdStats operator-=
760 
761       // dump and reload -----------------------------------------------
762 
763       /// Dump WtdStats private members directly;
764       /// useful in saving an object (e.g. to a file); reload with Load().
765       /// @param vuint output vector<unsigned int> of length 1, input of Load()
766       /// @param vT output vector<T> of length 5, input of Load()
Dump(std::vector<unsigned int> & vuint,std::vector<T> & vT)767       void Dump(std::vector<unsigned int>& vuint, std::vector<T>& vT)      // WtdStats
768       {
769          SeqStats<T>::Dump(vuint,vT);
770          vT.push_back(WtNorm);
771       }
772 
773       /// Define WtdStats private members directly; useful in continuing
774       /// with an object that was earlier saved (e.g. to a file) using Dump().
775       /// @param vuint input vector<unsigned int> of length 1, output of Dump()
776       /// @param vT input vector<T> of length 5, output of Dump()
777       /// NB no checking at all - caller has burden of validity
778       /// NB zero-fill rather than throwing
Load(std::vector<unsigned int> & vuint,std::vector<T> & vT)779       void Load(std::vector<unsigned int>& vuint, std::vector<T>& vT)      // WtdStats
780       {
781          if(vuint.size() < 1 || vT.size() < 5) {
782             vuint.clear();
783             vuint.push_back(0);
784             vT.clear();
785             for(size_t i=0; i<5; i++) vT.push_back(T(0));
786          }
787          SeqStats<T>::Load(vuint,vT);
788          WtNorm = vT[4];
789       }
790 
791       // output ----------------------------------------------------------
792 
793       /// Write WtdStats to a single-line string
asString(std::string msg=std::string (),int w=7,int p=4) const794       std::string asString(std::string msg=std::string(), int w=7, int p=4) const
795       {
796          std::ostringstream oss;
797          oss << "stats(wtd):" << (msg.empty() ? "" : " "+msg)
798             << " N " << std::setw(w) << SeqStats<T>::N()
799             << std::fixed << std::setprecision(p)
800             << "  Ave " << std::setw(w) << SeqStats<T>::Average()
801             << "  Std " << std::setw(w) << SeqStats<T>::StdDev()
802             << "  Var " << std::setw(w) << SeqStats<T>::Variance()
803             << "  Min " << std::setw(w) << SeqStats<T>::Minimum()
804             << "  Max " << std::setw(w) << SeqStats<T>::Maximum()
805             << "  P2P " << std::setw(w)
806             << SeqStats<T>::Maximum()-SeqStats<T>::Minimum()
807             << "  Wts " << std::setw(w) << WtsSum();
808          return oss.str();
809       }
810 
811       /// Write WtdStats N,ave,sig to a short single-line string
asShortString(std::string msg=std::string (),int w=0,int p=3) const812       std::string asShortString(std::string msg=std::string(), int w=0, int p=3) const
813       {
814          std::ostringstream oss;
815          oss << msg << std::fixed << std::setprecision(p)
816             << " N " << std::setw(w) << SeqStats<T>::N()
817             << "  Ave " << std::setw(w) << SeqStats<T>::Average()
818             << "  Std " << std::setw(w) << SeqStats<T>::StdDev();
819          return oss.str();
820       }
821 
822       /// return normalization = sum of weights
WtsSum(void) const823       inline T WtsSum(void) const { return WtNorm; }     // WtdStats
824 
825    protected:
826       T WtNorm;               ///< Normalization constant = sum weights
827 
828    }; // end class WtdStats
829 
830    /// Output operator for WtdStats class
operator <<(std::ostream & s,const WtdStats<T> & ST)831    template <class T> std::ostream& operator<<(std::ostream& s, const WtdStats<T>& ST)
832    {
833       std::ofstream savefmt;
834       savefmt.copyfmt(s);
835       s << "       N = " << ST.N() << " (weighted 1-sample stats)\n";
836       s << " Minimum = "; s.copyfmt(savefmt); s << ST.Minimum();
837       s << " Maximum = "; s.copyfmt(savefmt); s << ST.Maximum() << "\n";
838       s << " Average = "; s.copyfmt(savefmt); s << ST.Average();
839       s << " Std Dev = "; s.copyfmt(savefmt); s << ST.StdDev();
840       s << " Variance = "; s.copyfmt(savefmt); s << ST.Variance() << "\n";
841       s << " SumWts  = "; s.copyfmt(savefmt); s << ST.WtsSum();
842       return s;
843    }
844 
845    //---------------------------------------------------------------------------
846    //---------------------------------------------------------------------------
847    /// Conventional statistics for two samples. Also uses a pair of Stats<T> for
848    /// the each of the two samples.
849    template <class T> class TwoSampleStats
850    {
851    public:
852       /// constructor
TwoSampleStats()853       TwoSampleStats() : n(0), sumxy(T(0)) {}
854 
855       /// reset, i.e. ignore earlier data and restart sampling
Reset(void)856       inline void Reset(void) { n=0; SX.Reset(); SY.Reset(); sumxy = T(0); }
857 
858       /// constructor given two gpstk::Vector<T>s of data - must be parallel
TwoSampleStats(Vector<T> & X,Vector<T> & Y)859       TwoSampleStats(Vector<T>& X, Vector<T>& Y)
860       {
861          Reset();
862          Add(X,Y);
863       }
864 
865       /// Add data to the statistics
866       /// NB this is the fundamental Add routine; all other Add's call this
Add(const T & x,const T & y)867       void Add(const T& x, const T& y)    // TwoSampleStats
868       {
869          SX.Add(x);
870          SY.Add(y);
871          sumxy += (x/SX.scale)*(y/SY.scale);
872          n++;
873       }
874 
875       /// Subtract data from the statistics
876       /// NB this is the fundamental Subtract routine; all others call this
877       /// NB. Assumes that these samples were previously added.
878       /// NB. Minimum() and Maximum() may no longer be valid.
Subtract(const T & x,const T & y)879       void Subtract(const T& x, const T& y)     // TwoSampleStats
880       {
881          if(n < 1) return;       // TD throw
882          if(n == 1) { Reset(); return; }
883          SX.Subtract(x);
884          SY.Subtract(y);
885          sumxy -= (x/SX.scale)*(y/SY.scale);
886          n--;
887       }
888 
889       /// Add two gpstk::Vector<T>s of data to the statistics
Add(const Vector<T> & X,const Vector<T> & Y)890       void Add(const Vector<T>& X, const Vector<T>& Y)      // TwoSampleStats
891       {
892          for(size_t i=0; i<(X.size()<Y.size() ? X.size():Y.size()); i++)
893             Add(X(i),Y(i));
894       }
895 
896       /// Add two std::vectors of data to the statistics
Add(const std::vector<T> & X,const std::vector<T> & Y)897       void Add(const std::vector<T>& X, const std::vector<T>& Y)     // TwoSampleStats
898       {
899          for(size_t i=0; i<(X.size()<Y.size() ? X.size():Y.size()); i++)
900             Add(X[i],Y[i]);
901       }
902 
903       /// Subtract two gpstk::Vector<T>s of data from the statistics
Subtract(const Vector<T> & X,const Vector<T> & Y)904       void Subtract(const Vector<T>& X, const Vector<T>& Y)    // TwoSampleStats
905       {
906          for(size_t i=0; i<(X.size()<Y.size() ? X.size():Y.size()); i++)
907             Subtract(X(i),Y(i));
908       }
909 
910       /// Subtract two std::vectors of data from the statistics
Subtract(const std::vector<T> & X,const std::vector<T> & Y)911       void Subtract(const std::vector<T>& X, const std::vector<T>& Y)
912       {
913          for(size_t i=0; i<(X.size()<Y.size() ? X.size():Y.size()); i++)
914             Subtract(X[i],Y[i]);
915       }
916 
917       // combine two objects ---------------------------------------------
918 
919       /// combine two TwoSampleStats (assumed to be taken from the same or
920       /// equivalent ensembles)
operator +=(TwoSampleStats<T> & TSS)921       TwoSampleStats<T>& operator+=(TwoSampleStats<T>& TSS)
922       {
923          if(n + TSS.n == 0) return *this;
924          SX += TSS.SX;
925          SY += TSS.SY;
926          sumxy += (TSS.SX.scale/SX.scale)*(TSS.SY.scale/SY.scale)*TSS.sumxy;
927          n += TSS.n;
928          return *this;
929       }  // end TwoSampleStats operator+=
930 
931       /// remove one WtdStats from another, assumed to be taken from the same or
932       /// equivalent ensembles.
933       /// NB. Assumes that these samples were previously added.
934       /// NB. Minimum() and Maximum() may no longer be valid.
operator -=(TwoSampleStats<T> & TSS)935       TwoSampleStats<T>& operator-=(TwoSampleStats<T>& TSS)
936       {
937          if(n <= TSS.n) { Reset(); return *this; }
938          SX -= TSS.SX;
939          SY -= TSS.SY;
940          sumxy -= (TSS.SX.scale/SX.scale)*(TSS.SY.scale/SY.scale)*TSS.sumxy;
941          n -= TSS.n;
942          return *this;
943       }  // end TwoSampleStats operator-=
944 
945       // dump and reload --------------------------------------------
946 
947       /// Dump TwoSampleStats private members directly;
948       /// useful in saving an object (e.g. to a file); reload with Load().
949       /// @param vuint output vector<unsigned int> of length 5, input of Load()
950       /// @param vT output vector<T> of length 11, input of Load()
Dump(std::vector<unsigned int> & vuint,std::vector<T> & vT)951       void Dump(std::vector<unsigned int>& vuint, std::vector<T>& vT)   // TSS
952       {
953          size_t i;
954          std::vector<unsigned int> vi;
955          std::vector<T> vt;
956 
957          vuint.clear(); vT.clear();
958 
959          SX.Dump(vi,vt);
960          for(i=0; i<2; i++) vuint.push_back(vi[i]);
961          for(i=0; i<5; i++) vT.push_back(vt[i]);
962 
963          SY.Dump(vi,vt);                              // this will clear vi,vt first
964          for(i=0; i<2; i++) vuint.push_back(vi[i]);
965          for(i=0; i<5; i++) vT.push_back(vt[i]);
966 
967          vuint.push_back(n);
968          vT.push_back(sumxy);
969       }
970 
971       /// Define TwoSampleStats private members directly; useful in continuing
972       /// with an object that was earlier saved (e.g. to a file) using Dump().
973       /// @param vuint input vector<unsigned int> of length 5, output of Dump()
974       /// @param vT input vector<T> of length 11, output of Dump()
975       /// NB no checking at all - caller has burden of validity
976       /// NB zero-fill rather than throwing
Load(std::vector<unsigned int> & vuint,std::vector<T> & vT)977       void Load(std::vector<unsigned int>& vuint, std::vector<T>& vT)   // TSS
978       {
979          size_t i;
980          std::vector<unsigned int> vi2;
981          std::vector<double> vt2;
982          if(vuint.size() < 5 || vT.size() < 11) {
983             vuint.clear();
984             for(i=0; i<5; i++) vT.push_back(T(0));
985             vT.clear();
986             for(i=0; i<11; i++) vT.push_back(T(0));
987          }
988          SX.Load(vuint,vT);
989 
990          for(i=0; i<2; i++) vi2.push_back(vuint[i+2]);
991          for(i=0; i<5; i++) vt2.push_back(vT[i+5]);
992          SY.Load(vi2,vt2);
993 
994          n = vuint[4];
995          sumxy = vT[10];
996       }
997 
998       // output -----------------------------------------------------------
999 
1000       /// Write TwoSampleStats to a three-line string
asString(std::string msg=std::string (),int w=7,int p=4) const1001       std::string asString(std::string msg=std::string(), int w=7, int p=4) const
1002       {
1003          std::ostringstream oss;
1004          //static const int p=4, w=7;
1005          oss << SX.asString(msg,w,p) << " (X)" << std::endl
1006             << SY.asString(msg,w,p) << " (Y)" << std::endl
1007             << "stats(tss):" << (msg.empty() ? "" : " "+msg)
1008             << " N " << std::setw(w) << N() << std::fixed << std::setprecision(p)
1009             << "  Int " << std::setw(w) << Intercept()
1010             << "  Slp " << std::setw(w) << Slope()
1011             << " +- " << std::setw(w) << SigmaSlope()
1012             << "  CSig " << std::setw(w) << SigmaYX()
1013             << "  Corr " << std::setw(w) << Correlation();
1014          return oss.str();
1015       }
1016 
1017       /// Write TwoSampleStats as a short 1-line string
asShortString(std::string msg=std::string (),int w=0,int p=3) const1018       std::string asShortString(std::string msg=std::string(), int w=0, int p=3) const
1019       {
1020          std::ostringstream oss;
1021          oss << SX.asShortString(msg,w,p) << " (X);"
1022              << SY.asShortString(msg,w,p) << " (Y);"
1023              << msg << std::fixed << std::setprecision(p)
1024              << std::fixed << std::setprecision(p)
1025              << "  Int " << std::setw(w) << Intercept()
1026              << "  Slp " << std::setw(w) << Slope()
1027              << " +- " << std::setw(w) << SigmaSlope()
1028              << "  CSig " << std::setw(w) << SigmaYX()
1029              << "  Corr " << std::setw(w) << Correlation();
1030          return oss.str();
1031       }
1032 
1033       // accessors -------------------------------------------------------
1034 
1035       /// access the sample size
1036       /// NB. n should match SX.N() and SY.N() at all times
N(void) const1037       inline unsigned int N(void) const { return n; }
1038       /// return minimum X value
MinimumX(void) const1039       inline T MinimumX(void) const { return SX.Minimum(); }
1040       /// return maximum X value
MaximumX(void) const1041       inline T MaximumX(void) const { return SX.Maximum(); }
1042       /// return minimum Y value
MinimumY(void) const1043       inline T MinimumY(void) const { return SY.Minimum(); }
1044       /// return maximum Y value
MaximumY(void) const1045       inline T MaximumY(void) const { return SY.Maximum(); }
1046       /// return computed X average
AverageX(void) const1047       inline T AverageX(void) const { return SX.Average(); }
1048       /// return computed Y average
AverageY(void) const1049       inline T AverageY(void) const { return SY.Average(); }
1050       /// return computed X variance
VarianceX(void) const1051       inline T VarianceX(void) const { return SX.Variance(); }
1052       /// return computed Y variance
VarianceY(void) const1053       inline T VarianceY(void) const { return SY.Variance(); }
1054       /// return computed X standard deviation
StdDevX(void) const1055       inline T StdDevX(void) const { return SX.StdDev(); }
1056       /// return computed Y standard deviation
StdDevY(void) const1057       inline T StdDevY(void) const { return SY.StdDev(); }
1058 
1059       /// return slope of best-fit line Y=slope*X + intercept
Slope(void) const1060       inline T Slope(void) const
1061       {
1062          if(n > 0) {
1063             T den(SX.sum2-SX.sum*SX.sum/T(n));
1064             if(den == T()) return T();                   // throw?
1065             return ( (SY.scale/SX.scale) * (sumxy-SX.sum*SY.sum/T(n)) / den );
1066          }
1067          else
1068             return T();
1069       }
1070 
1071       /// return intercept of best-fit line Y=slope*X + intercept
Intercept(void) const1072       inline T Intercept(void) const
1073       {
1074          if(n > 0)
1075             return (AverageY()-Slope()*AverageX());
1076          else
1077             return T();
1078       }
1079 
1080       /// return uncertainty in slope
SigmaSlope(void) const1081       inline T SigmaSlope(void) const
1082       {
1083          if(n > 2) {
1084             T den(StdDevX()*SQRT(T(n-1)));
1085             if(den == T()) return T();                   // throw?
1086             return (SigmaYX()/den);
1087          }
1088          else
1089             return T();
1090       }
1091 
1092       /// return correlation
Correlation(void) const1093       inline T Correlation(void) const
1094       {
1095          if(n>1) {
1096             T den(StdDevX()*StdDevY()*T(n-1));
1097             if(den == T()) return T();                   // throw?
1098             return ( SX.scale*SY.scale*(sumxy-SX.sum*SY.sum/T(n)) / den);
1099          }
1100          else
1101             return T();
1102       }
1103 
1104       /// return conditional uncertainty = uncertainty y given x
SigmaYX(void) const1105       inline T SigmaYX(void) const
1106       {
1107          return SQRT(VarianceYX());
1108       }
1109 
1110       /// return conditional variance = (uncertainty y given x)^2
VarianceYX(void) const1111       inline T VarianceYX(void) const
1112       {
1113          if(n > 2) {
1114             return (VarianceY() * (T(n-1)/T(n-2))
1115                        * (T(1)-Correlation()*Correlation()));
1116          }
1117          else return T();
1118       }
1119 
1120       /// return the predicted Y at the given X, using Slope and Intercept
Evaluate(T x) const1121       inline T Evaluate(T x) const
1122       {
1123          return (Slope()*x + Intercept());
1124       }
1125 
1126    protected:
1127       Stats<T> SX;      ///< conventional 1-sample stats for first sample x
1128       Stats<T> SY;      ///< conventional 1-sample stats for second sample y
1129       unsigned int n;   ///< number of samples added to the statistics
1130       T sumxy;          ///< sum of x*y
1131 
1132    }; // end class TwoSampleStats
1133 
1134    /// Output operator for TwoSampleStats class
1135    template <class T>
operator <<(std::ostream & s,const TwoSampleStats<T> & TSS)1136    std::ostream& operator<<(std::ostream& s, const TwoSampleStats<T>& TSS)
1137    {
1138       std::ofstream savefmt;
1139       savefmt.copyfmt(s);
1140       s << " N       = " << TSS.N() << " (two-sample-statistics)\n";
1141       s << " Minimum:  X = "; s.copyfmt(savefmt); s << TSS.MinimumX();
1142       s << "  Y = "; s.copyfmt(savefmt); s << TSS.MinimumY() << "\n";
1143       s << " Maximum:  X = "; s.copyfmt(savefmt); s << TSS.MaximumX();
1144       s << "  Y = "; s.copyfmt(savefmt); s << TSS.MaximumY() << "\n";
1145       s << " Average:  X = "; s.copyfmt(savefmt); s << TSS.AverageX();
1146       s << "  Y = "; s.copyfmt(savefmt); s << TSS.AverageY() << "\n";
1147       s << " Std Dev:  X = "; s.copyfmt(savefmt); s << TSS.StdDevX();
1148       s << "  Y = "; s.copyfmt(savefmt); s << TSS.StdDevY() << "\n";
1149       s << " Variance: X = "; s.copyfmt(savefmt); s << TSS.VarianceX();
1150       s << "  Y = "; s.copyfmt(savefmt); s << TSS.VarianceY() << "\n";
1151 
1152       bool bad(TSS.VarianceYX() == T());
1153       std::string badmsg("undef");
1154       s << " Intercept = "; s.copyfmt(savefmt);
1155       if(bad) s << badmsg; else s << TSS.Intercept();
1156       s << "  Slope = "; s.copyfmt(savefmt);
1157       if(bad) s << badmsg; else s << TSS.Slope();
1158       s << " with uncertainty = "; s.copyfmt(savefmt);
1159       if(bad) s << badmsg; else s << TSS.SigmaSlope();
1160       s << "\n Conditional uncertainty (sigma Y given X) = "; s.copyfmt(savefmt);
1161       if(bad) s << badmsg; else s << TSS.SigmaYX();
1162       s << "  Correlation = "; s.copyfmt(savefmt);
1163       if(bad) s << badmsg; else s << TSS.Correlation();
1164 
1165       return s;
1166    }
1167 
1168    //@}
1169 
1170 }  // namespace
1171 
1172 #endif   // INCLUDE_GPSTK_STATS_INCLUDE
1173