go-hep.org/x/hep@v0.38.1/hbook/dist.go (about)

     1  // Copyright ©2016 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package hbook
     6  
     7  import "math"
     8  
     9  // Dist0D is a 0-dim distribution.
    10  type Dist0D struct {
    11  	N     int64   // number of entries
    12  	SumW  float64 // sum of weights
    13  	SumW2 float64 // sum of squared weights
    14  }
    15  
    16  func (d Dist0D) clone() Dist0D {
    17  	return d
    18  }
    19  
    20  // Rank returns the number of dimensions of the distribution.
    21  func (*Dist0D) Rank() int {
    22  	return 1
    23  }
    24  
    25  // Entries returns the number of entries in the distribution.
    26  func (d *Dist0D) Entries() int64 {
    27  	return d.N
    28  }
    29  
    30  // EffEntries returns the number of weighted entries, such as:
    31  //
    32  //	(\sum w)^2 / \sum w^2
    33  func (d *Dist0D) EffEntries() float64 {
    34  	if d.SumW2 == 0 {
    35  		return 0
    36  	}
    37  	return d.SumW * d.SumW / d.SumW2
    38  }
    39  
    40  // errW returns the absolute error on sumW()
    41  func (d *Dist0D) errW() float64 {
    42  	return math.Sqrt(d.SumW2)
    43  }
    44  
    45  // // relErrW returns the relative error on sumW()
    46  // func (d *Dist0D) relErrW() float64 {
    47  // 	// FIXME(sbinet) check for low stats ?
    48  // 	return d.errW() / d.SumW
    49  // }
    50  
    51  func (d *Dist0D) fill(w float64) {
    52  	d.N++
    53  	d.SumW += w
    54  	d.SumW2 += w * w
    55  }
    56  
    57  func (d *Dist0D) addScaled(a, a2 float64, o Dist0D) {
    58  	d.N += o.N
    59  	d.SumW += a * o.SumW
    60  	d.SumW2 += a2 * o.SumW2
    61  }
    62  
    63  func (d *Dist0D) scaleW(f float64) {
    64  	d.SumW *= f
    65  	d.SumW2 *= f * f
    66  }
    67  
    68  // Dist1D is a 1-dim distribution.
    69  type Dist1D struct {
    70  	Dist  Dist0D // weight moments
    71  	Stats struct {
    72  		SumWX  float64 // 1st order weighted x moment
    73  		SumWX2 float64 // 2nd order weighted x moment
    74  	}
    75  }
    76  
    77  func (d Dist1D) clone() Dist1D {
    78  	return Dist1D{
    79  		Dist:  d.Dist.clone(),
    80  		Stats: d.Stats,
    81  	}
    82  }
    83  
    84  // Rank returns the number of dimensions of the distribution.
    85  func (*Dist1D) Rank() int {
    86  	return 1
    87  }
    88  
    89  // Entries returns the number of entries in the distribution.
    90  func (d *Dist1D) Entries() int64 {
    91  	return d.Dist.Entries()
    92  }
    93  
    94  // EffEntries returns the effective number of entries in the distribution.
    95  func (d *Dist1D) EffEntries() float64 {
    96  	return d.Dist.EffEntries()
    97  }
    98  
    99  // SumW returns the sum of weights of the distribution.
   100  func (d *Dist1D) SumW() float64 {
   101  	return d.Dist.SumW
   102  }
   103  
   104  // SumW2 returns the sum of squared weights of the distribution.
   105  func (d *Dist1D) SumW2() float64 {
   106  	return d.Dist.SumW2
   107  }
   108  
   109  // SumWX returns the 1st order weighted x moment.
   110  func (d *Dist1D) SumWX() float64 {
   111  	return d.Stats.SumWX
   112  }
   113  
   114  // SumWX2 returns the 2nd order weighted x moment.
   115  func (d *Dist1D) SumWX2() float64 {
   116  	return d.Stats.SumWX2
   117  }
   118  
   119  // errW returns the absolute error on sumW()
   120  func (d *Dist1D) errW() float64 {
   121  	return d.Dist.errW()
   122  }
   123  
   124  // // relErrW returns the relative error on sumW()
   125  // func (d *Dist1D) relErrW() float64 {
   126  // 	return d.Dist.relErrW()
   127  // }
   128  
   129  // mean returns the weighted mean of the distribution
   130  func (d *Dist1D) mean() float64 {
   131  	// FIXME(sbinet): check for low stats?
   132  	return d.SumWX() / d.SumW()
   133  }
   134  
   135  // variance returns the weighted variance of the distribution, defined as:
   136  //
   137  //	sig2 = ( \sum(wx^2) * \sum(w) - \sum(wx)^2 ) / ( \sum(w)^2 - \sum(w^2) )
   138  //
   139  // see: https://en.wikipedia.org/wiki/Weighted_arithmetic_mean
   140  func (d *Dist1D) variance() float64 {
   141  	// FIXME(sbinet): check for low stats?
   142  	sumw := d.SumW()
   143  	num := d.SumWX2()*sumw - d.SumWX()*d.SumWX()
   144  	den := sumw*sumw - d.SumW2()
   145  	v := num / den
   146  	return math.Abs(v)
   147  }
   148  
   149  // stdDev returns the weighted standard deviation of the distribution
   150  func (d *Dist1D) stdDev() float64 {
   151  	return math.Sqrt(d.variance())
   152  }
   153  
   154  // stdErr returns the weighted standard error of the distribution
   155  func (d *Dist1D) stdErr() float64 {
   156  	// FIXME(sbinet): check for low stats?
   157  	// TODO(sbinet): unbiased should check that Neff>1 and divide by N-1?
   158  	return math.Sqrt(d.variance() / d.EffEntries())
   159  }
   160  
   161  // rms returns the weighted RMS of the distribution, defined as:
   162  //
   163  //	rms = \sqrt{\sum{w . x^2} / \sum{w}}
   164  func (d *Dist1D) rms() float64 {
   165  	// FIXME(sbinet): check for low stats?
   166  	meansq := d.SumWX2() / d.SumW()
   167  	return math.Sqrt(meansq)
   168  }
   169  
   170  func (d *Dist1D) fill(x, w float64) {
   171  	d.Dist.fill(w)
   172  	d.Stats.SumWX += w * x
   173  	d.Stats.SumWX2 += w * x * x
   174  }
   175  
   176  func (d *Dist1D) addScaled(a, a2 float64, o Dist1D) {
   177  	d.Dist.addScaled(a, a2, o.Dist)
   178  	d.Stats.SumWX += a * o.Stats.SumWX
   179  	d.Stats.SumWX2 += a * o.Stats.SumWX2
   180  }
   181  
   182  func (d *Dist1D) scaleW(f float64) {
   183  	d.Dist.scaleW(f)
   184  	d.Stats.SumWX *= f
   185  	d.Stats.SumWX2 *= f
   186  }
   187  
   188  // Dist2D is a 2-dim distribution.
   189  type Dist2D struct {
   190  	X     Dist1D // x moments
   191  	Y     Dist1D // y moments
   192  	Stats struct {
   193  		SumWXY float64 // 2nd-order cross-term
   194  	}
   195  }
   196  
   197  // Rank returns the number of dimensions of the distribution.
   198  func (*Dist2D) Rank() int {
   199  	return 2
   200  }
   201  
   202  // Entries returns the number of entries in the distribution.
   203  func (d *Dist2D) Entries() int64 {
   204  	return d.X.Entries()
   205  }
   206  
   207  // EffEntries returns the effective number of entries in the distribution.
   208  func (d *Dist2D) EffEntries() float64 {
   209  	return d.X.EffEntries()
   210  }
   211  
   212  // SumW returns the sum of weights of the distribution.
   213  func (d *Dist2D) SumW() float64 {
   214  	return d.X.SumW()
   215  }
   216  
   217  // SumW2 returns the sum of squared weights of the distribution.
   218  func (d *Dist2D) SumW2() float64 {
   219  	return d.X.SumW2()
   220  }
   221  
   222  // SumWX returns the 1st order weighted x moment
   223  func (d *Dist2D) SumWX() float64 {
   224  	return d.X.SumWX()
   225  }
   226  
   227  // SumWX2 returns the 2nd order weighted x moment
   228  func (d *Dist2D) SumWX2() float64 {
   229  	return d.X.SumWX2()
   230  }
   231  
   232  // SumWY returns the 1st order weighted y moment
   233  func (d *Dist2D) SumWY() float64 {
   234  	return d.Y.SumWX()
   235  }
   236  
   237  // SumWY2 returns the 2nd order weighted y moment
   238  func (d *Dist2D) SumWY2() float64 {
   239  	return d.Y.SumWX2()
   240  }
   241  
   242  // SumWXY returns the 2nd-order cross-term.
   243  func (d *Dist2D) SumWXY() float64 {
   244  	return d.Stats.SumWXY
   245  }
   246  
   247  // // errW returns the absolute error on sumW()
   248  // func (d *Dist2D) errW() float64 {
   249  // 	return d.X.errW()
   250  // }
   251  //
   252  // // relErrW returns the relative error on sumW()
   253  // func (d *Dist2D) relErrW() float64 {
   254  // 	return d.X.relErrW()
   255  // }
   256  
   257  // xMean returns the weighted mean of the distribution
   258  func (d *Dist2D) xMean() float64 {
   259  	return d.X.mean()
   260  }
   261  
   262  // yMean returns the weighted mean of the distribution
   263  func (d *Dist2D) yMean() float64 {
   264  	return d.Y.mean()
   265  }
   266  
   267  // xVariance returns the weighted variance of the distribution
   268  func (d *Dist2D) xVariance() float64 {
   269  	return d.X.variance()
   270  }
   271  
   272  // yVariance returns the weighted variance of the distribution
   273  func (d *Dist2D) yVariance() float64 {
   274  	return d.Y.variance()
   275  }
   276  
   277  // xStdDev returns the weighted standard deviation of the distribution
   278  func (d *Dist2D) xStdDev() float64 {
   279  	return d.X.stdDev()
   280  }
   281  
   282  // yStdDev returns the weighted standard deviation of the distribution
   283  func (d *Dist2D) yStdDev() float64 {
   284  	return d.Y.stdDev()
   285  }
   286  
   287  // xStdErr returns the weighted standard error of the distribution
   288  func (d *Dist2D) xStdErr() float64 {
   289  	return d.X.stdErr()
   290  }
   291  
   292  // yStdErr returns the weighted standard error of the distribution
   293  func (d *Dist2D) yStdErr() float64 {
   294  	return d.Y.stdErr()
   295  }
   296  
   297  // xRMS returns the weighted RMS of the distribution
   298  func (d *Dist2D) xRMS() float64 {
   299  	return d.X.rms()
   300  }
   301  
   302  // yRMS returns the weighted RMS of the distribution
   303  func (d *Dist2D) yRMS() float64 {
   304  	return d.Y.rms()
   305  }
   306  
   307  func (d *Dist2D) fill(x, y, w float64) {
   308  	d.X.fill(x, w)
   309  	d.Y.fill(y, w)
   310  	d.Stats.SumWXY += w * x * y
   311  }
   312  
   313  func (d *Dist2D) scaleW(f float64) {
   314  	d.X.scaleW(f)
   315  	d.Y.scaleW(f)
   316  	d.Stats.SumWXY *= f
   317  }