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 }