go-hep.org/x/hep@v0.38.1/hbook/p1d.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 ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "math" 12 "strings" 13 ) 14 15 // P1D is a 1-dim profile histogram. 16 type P1D struct { 17 bng binningP1D 18 ann Annotation 19 } 20 21 // NewP1D returns a 1-dim profile histogram with n bins between xmin and xmax. 22 func NewP1D(n int, xmin, xmax float64) *P1D { 23 return &P1D{ 24 bng: newBinningP1D(n, xmin, xmax), 25 ann: make(Annotation), 26 } 27 } 28 29 /* 30 // FIXME(sbinet): need support of variable-size bins 31 // 32 // NewP1DFromS2D creates a 1-dim profile histogram from a 2-dim scatter's binning. 33 func NewP1DFromH1D(s*S2D) *P1D { 34 return &P1D{ 35 bng: newBinningP1D(len(h.Binning().Bins()), h.XMin(), h.XMax()), 36 ann: make(Annotation), 37 } 38 } 39 */ 40 41 // NewP1DFromH1D creates a 1-dim profile histogram from a 1-dim histogram's binning. 42 func NewP1DFromH1D(h *H1D) *P1D { 43 return &P1D{ 44 bng: newBinningP1D(len(h.Binning.Bins), h.XMin(), h.XMax()), 45 ann: make(Annotation), 46 } 47 } 48 49 // Name returns the name of this profile histogram, if any 50 func (p *P1D) Name() string { 51 v, ok := p.ann["name"] 52 if !ok { 53 return "" 54 } 55 n, ok := v.(string) 56 if !ok { 57 return "" 58 } 59 return n 60 } 61 62 // Annotation returns the annotations attached to this profile histogram 63 func (p *P1D) Annotation() Annotation { 64 return p.ann 65 } 66 67 // Rank returns the number of dimensions for this profile histogram 68 func (p *P1D) Rank() int { 69 return 1 70 } 71 72 // Entries returns the number of entries in this profile histogram 73 func (p *P1D) Entries() int64 { 74 return p.bng.entries() 75 } 76 77 // EffEntries returns the number of effective entries in this profile histogram 78 func (p *P1D) EffEntries() float64 { 79 return p.bng.effEntries() 80 } 81 82 // Binning returns the binning of this profile histogram 83 func (p *P1D) Binning() *binningP1D { 84 return &p.bng 85 } 86 87 // SumW returns the sum of weights in this profile histogram. 88 // Overflows are included in the computation. 89 func (p *P1D) SumW() float64 { 90 return p.bng.dist.SumW() 91 } 92 93 // SumW2 returns the sum of squared weights in this profile histogram. 94 // Overflows are included in the computation. 95 func (p *P1D) SumW2() float64 { 96 return p.bng.dist.SumW2() 97 } 98 99 // XMean returns the mean X. 100 // Overflows are included in the computation. 101 func (p *P1D) XMean() float64 { 102 return p.bng.dist.xMean() 103 } 104 105 // XVariance returns the variance in X. 106 // Overflows are included in the computation. 107 func (p *P1D) XVariance() float64 { 108 return p.bng.dist.xVariance() 109 } 110 111 // XStdDev returns the standard deviation in X. 112 // Overflows are included in the computation. 113 func (p *P1D) XStdDev() float64 { 114 return p.bng.dist.xStdDev() 115 } 116 117 // XStdErr returns the standard error in X. 118 // Overflows are included in the computation. 119 func (p *P1D) XStdErr() float64 { 120 return p.bng.dist.xStdErr() 121 } 122 123 // XRMS returns the XRMS in X. 124 // Overflows are included in the computation. 125 func (p *P1D) XRMS() float64 { 126 return p.bng.dist.xRMS() 127 } 128 129 // Fill fills this histogram with x,y and weight w. 130 func (p *P1D) Fill(x, y, w float64) { 131 p.bng.fill(x, y, w) 132 } 133 134 // XMin returns the low edge of the X-axis of this profile histogram. 135 func (p *P1D) XMin() float64 { 136 return p.bng.xMin() 137 } 138 139 // XMax returns the high edge of the X-axis of this profile histogram. 140 func (p *P1D) XMax() float64 { 141 return p.bng.xMax() 142 } 143 144 // Scale scales the content of each bin by the given factor. 145 func (p *P1D) Scale(factor float64) { 146 p.bng.scaleW(factor) 147 } 148 149 // check various interfaces 150 var _ Object = (*P1D)(nil) 151 var _ Histogram = (*P1D)(nil) 152 153 // annToYODA creates a new Annotation with fields compatible with YODA 154 func (p *P1D) annToYODA() Annotation { 155 ann := make(Annotation, len(p.ann)) 156 ann["Type"] = "Profile1D" 157 ann["Path"] = "/" + p.Name() 158 ann["Title"] = "" 159 for k, v := range p.ann { 160 if k == "name" { 161 continue 162 } 163 if k == "title" { 164 ann["Title"] = v 165 continue 166 } 167 ann[k] = v 168 } 169 return ann 170 } 171 172 // annFromYODA creates a new Annotation from YODA compatible fields 173 func (p *P1D) annFromYODA(ann Annotation) { 174 if len(p.ann) == 0 { 175 p.ann = make(Annotation, len(ann)) 176 } 177 for k, v := range ann { 178 switch k { 179 case "Type": 180 // noop 181 case "Path": 182 name := v.(string) 183 name = strings.TrimPrefix(name, "/") 184 p.ann["name"] = name 185 case "Title": 186 p.ann["title"] = v 187 default: 188 p.ann[k] = v 189 } 190 } 191 } 192 193 // MarshalYODA implements the YODAMarshaler interface. 194 func (p *P1D) MarshalYODA() ([]byte, error) { 195 return p.marshalYODAv2() 196 } 197 198 func (p *P1D) marshalYODAv1() ([]byte, error) { 199 buf := new(bytes.Buffer) 200 ann := p.annToYODA() 201 fmt.Fprintf(buf, "BEGIN YODA_PROFILE1D %s\n", ann["Path"]) 202 data, err := ann.marshalYODAv1() 203 if err != nil { 204 return nil, err 205 } 206 buf.Write(data) 207 208 fmt.Fprintf(buf, "# ID\t ID\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t numEntries\n") 209 d := p.bng.dist 210 fmt.Fprintf( 211 buf, 212 "Total \tTotal \t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 213 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), d.Entries(), 214 ) 215 216 d = p.bng.outflows[0] 217 fmt.Fprintf( 218 buf, 219 "Underflow\tUnderflow\t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 220 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), d.Entries(), 221 ) 222 223 d = p.bng.outflows[1] 224 fmt.Fprintf( 225 buf, 226 "Overflow\tOverflow\t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 227 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), d.Entries(), 228 ) 229 230 // bins 231 fmt.Fprintf(buf, "# xlow\t xhigh\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t numEntries\n") 232 for _, bin := range p.bng.bins { 233 d := bin.dist 234 fmt.Fprintf( 235 buf, 236 "%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 237 bin.xrange.Min, bin.xrange.Max, 238 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), d.Entries(), 239 ) 240 } 241 fmt.Fprintf(buf, "END YODA_PROFILE1D\n\n") 242 return buf.Bytes(), err 243 } 244 245 func (p *P1D) marshalYODAv2() ([]byte, error) { 246 buf := new(bytes.Buffer) 247 ann := p.annToYODA() 248 fmt.Fprintf(buf, "BEGIN YODA_PROFILE1D_V2 %s\n", ann["Path"]) 249 data, err := ann.marshalYODAv2() 250 if err != nil { 251 return nil, err 252 } 253 buf.Write(data) 254 buf.Write([]byte("---\n")) 255 256 fmt.Fprintf(buf, "# ID\t ID\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t numEntries\n") 257 d := p.bng.dist 258 fmt.Fprintf( 259 buf, 260 "Total \tTotal \t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 261 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), float64(d.Entries()), 262 ) 263 264 d = p.bng.outflows[0] 265 fmt.Fprintf( 266 buf, 267 "Underflow\tUnderflow\t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 268 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), float64(d.Entries()), 269 ) 270 271 d = p.bng.outflows[1] 272 fmt.Fprintf( 273 buf, 274 "Overflow\tOverflow\t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 275 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), float64(d.Entries()), 276 ) 277 278 // bins 279 fmt.Fprintf(buf, "# xlow\t xhigh\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t numEntries\n") 280 for _, bin := range p.bng.bins { 281 d := bin.dist 282 fmt.Fprintf( 283 buf, 284 "%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 285 bin.xrange.Min, bin.xrange.Max, 286 d.SumW(), d.SumW2(), d.SumWX(), d.SumWX2(), d.SumWY(), d.SumWY2(), float64(d.Entries()), 287 ) 288 } 289 fmt.Fprintf(buf, "END YODA_PROFILE1D_V2\n\n") 290 return buf.Bytes(), err 291 } 292 293 // UnmarshalYODA implements the YODAUnmarshaler interface. 294 func (p *P1D) UnmarshalYODA(data []byte) error { 295 r := newRBuffer(data) 296 _, vers, err := readYODAHeader(r, "BEGIN YODA_PROFILE1D") 297 if err != nil { 298 return err 299 } 300 switch vers { 301 case 1: 302 return p.unmarshalYODAv1(r) 303 case 2: 304 return p.unmarshalYODAv2(r) 305 default: 306 return fmt.Errorf("hbook: invalid YODA version %v", vers) 307 } 308 } 309 310 func (p *P1D) unmarshalYODAv1(r *rbuffer) error { 311 ann := make(Annotation) 312 313 // pos of end of annotations 314 pos := bytes.Index(r.Bytes(), []byte("\n# ID\t ID\t")) 315 if pos < 0 { 316 return fmt.Errorf("hbook: invalid P1D-YODA data") 317 } 318 err := ann.unmarshalYODAv1(r.Bytes()[:pos+1]) 319 if err != nil { 320 return fmt.Errorf("hbook: %q\nhbook: %w", string(r.Bytes()[:pos+1]), err) 321 } 322 p.annFromYODA(ann) 323 r.next(pos) 324 325 var ctx struct { 326 total bool 327 under bool 328 over bool 329 bins bool 330 } 331 332 // sets of xlow values, to infer number of bins in X. 333 xset := make(map[float64]int) 334 335 var ( 336 dist Dist2D 337 oflows [2]Dist2D 338 bins []BinP1D 339 xmin = math.Inf(+1) 340 xmax = math.Inf(-1) 341 ) 342 s := bufio.NewScanner(r) 343 scanLoop: 344 for s.Scan() { 345 buf := s.Bytes() 346 if len(buf) == 0 || buf[0] == '#' { 347 continue 348 } 349 rbuf := bytes.NewReader(buf) 350 switch { 351 case bytes.HasPrefix(buf, []byte("END YODA_PROFILE1D")): 352 break scanLoop 353 case !ctx.total && bytes.HasPrefix(buf, []byte("Total \t")): 354 ctx.total = true 355 d := &dist 356 _, err = fmt.Fscanf( 357 rbuf, 358 "Total \tTotal \t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 359 &d.X.Dist.SumW, &d.X.Dist.SumW2, 360 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 361 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 362 &d.X.Dist.N, 363 ) 364 if err != nil { 365 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 366 } 367 d.Y.Dist.N = d.X.Dist.N 368 case !ctx.under && bytes.HasPrefix(buf, []byte("Underflow\t")): 369 ctx.under = true 370 d := &oflows[0] 371 _, err = fmt.Fscanf( 372 rbuf, 373 "Underflow\tUnderflow\t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 374 &d.X.Dist.SumW, &d.X.Dist.SumW2, 375 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 376 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 377 &d.X.Dist.N, 378 ) 379 if err != nil { 380 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 381 } 382 d.Y.Dist.N = d.X.Dist.N 383 case !ctx.over && bytes.HasPrefix(buf, []byte("Overflow\t")): 384 ctx.over = true 385 d := &oflows[1] 386 _, err = fmt.Fscanf( 387 rbuf, 388 "Overflow\tOverflow\t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 389 &d.X.Dist.SumW, &d.X.Dist.SumW2, 390 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 391 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 392 &d.X.Dist.N, 393 ) 394 if err != nil { 395 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 396 } 397 d.Y.Dist.N = d.X.Dist.N 398 ctx.bins = true 399 case ctx.bins: 400 var bin BinP1D 401 d := &bin.dist 402 _, err = fmt.Fscanf( 403 rbuf, 404 "%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\t%d\n", 405 &bin.xrange.Min, &bin.xrange.Max, 406 &d.X.Dist.SumW, &d.X.Dist.SumW2, 407 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 408 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 409 &d.X.Dist.N, 410 ) 411 if err != nil { 412 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 413 } 414 d.Y.Dist.N = d.X.Dist.N 415 xset[bin.xrange.Min] = 1 416 xmin = math.Min(xmin, bin.xrange.Min) 417 xmax = math.Max(xmax, bin.xrange.Max) 418 bins = append(bins, bin) 419 420 default: 421 return fmt.Errorf("hbook: invalid P1D-YODA data: %q", string(buf)) 422 } 423 } 424 p.bng = newBinningP1D(len(xset), xmin, xmax) 425 p.bng.dist = dist 426 p.bng.bins = bins 427 p.bng.outflows = oflows 428 return err 429 } 430 431 func (p *P1D) unmarshalYODAv2(r *rbuffer) error { 432 ann := make(Annotation) 433 434 // pos of end of annotations 435 pos := bytes.Index(r.Bytes(), []byte("\n# ID\t ID\t")) 436 if pos < 0 { 437 return fmt.Errorf("hbook: invalid P1D-YODA data") 438 } 439 err := ann.unmarshalYODAv2(r.Bytes()[:pos+1]) 440 if err != nil { 441 return fmt.Errorf("hbook: %q\nhbook: %w", string(r.Bytes()[:pos+1]), err) 442 } 443 p.annFromYODA(ann) 444 r.next(pos) 445 446 var ctx struct { 447 total bool 448 under bool 449 over bool 450 bins bool 451 } 452 453 // sets of xlow values, to infer number of bins in X. 454 xset := make(map[float64]int) 455 456 var ( 457 dist Dist2D 458 oflows [2]Dist2D 459 bins []BinP1D 460 xmin = math.Inf(+1) 461 xmax = math.Inf(-1) 462 ) 463 s := bufio.NewScanner(r) 464 scanLoop: 465 for s.Scan() { 466 buf := s.Bytes() 467 if len(buf) == 0 || buf[0] == '#' { 468 continue 469 } 470 rbuf := bytes.NewReader(buf) 471 switch { 472 case bytes.HasPrefix(buf, []byte("END YODA_PROFILE1D_V2")): 473 break scanLoop 474 case !ctx.total && bytes.HasPrefix(buf, []byte("Total \t")): 475 ctx.total = true 476 d := &dist 477 var n float64 478 _, err = fmt.Fscanf( 479 rbuf, 480 "Total \tTotal \t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 481 &d.X.Dist.SumW, &d.X.Dist.SumW2, 482 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 483 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 484 &n, 485 ) 486 if err != nil { 487 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 488 } 489 d.X.Dist.N = int64(n) 490 d.Y.Dist.N = d.X.Dist.N 491 case !ctx.under && bytes.HasPrefix(buf, []byte("Underflow\t")): 492 ctx.under = true 493 d := &oflows[0] 494 var n float64 495 _, err = fmt.Fscanf( 496 rbuf, 497 "Underflow\tUnderflow\t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 498 &d.X.Dist.SumW, &d.X.Dist.SumW2, 499 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 500 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 501 &n, 502 ) 503 if err != nil { 504 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 505 } 506 d.X.Dist.N = int64(n) 507 d.Y.Dist.N = d.X.Dist.N 508 case !ctx.over && bytes.HasPrefix(buf, []byte("Overflow\t")): 509 ctx.over = true 510 d := &oflows[1] 511 var n float64 512 _, err = fmt.Fscanf( 513 rbuf, 514 "Overflow\tOverflow\t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 515 &d.X.Dist.SumW, &d.X.Dist.SumW2, 516 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 517 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 518 &n, 519 ) 520 if err != nil { 521 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 522 } 523 d.X.Dist.N = int64(n) 524 d.Y.Dist.N = d.X.Dist.N 525 ctx.bins = true 526 case ctx.bins: 527 var bin BinP1D 528 d := &bin.dist 529 var n float64 530 _, err = fmt.Fscanf( 531 rbuf, 532 "%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\n", 533 &bin.xrange.Min, &bin.xrange.Max, 534 &d.X.Dist.SumW, &d.X.Dist.SumW2, 535 &d.X.Stats.SumWX, &d.X.Stats.SumWX2, 536 &d.Y.Stats.SumWX, &d.Y.Stats.SumWX2, 537 &n, 538 ) 539 if err != nil { 540 return fmt.Errorf("hbook: %q\nhbook: %w", string(buf), err) 541 } 542 d.X.Dist.N = int64(n) 543 d.Y.Dist.N = d.X.Dist.N 544 xset[bin.xrange.Min] = 1 545 xmin = math.Min(xmin, bin.xrange.Min) 546 xmax = math.Max(xmax, bin.xrange.Max) 547 bins = append(bins, bin) 548 549 default: 550 return fmt.Errorf("hbook: invalid P1D-YODA data: %q", string(buf)) 551 } 552 } 553 p.bng = newBinningP1D(len(xset), xmin, xmax) 554 p.bng.dist = dist 555 p.bng.bins = bins 556 p.bng.outflows = oflows 557 return err 558 } 559 560 // binningP1D is a 1-dim binning for 1-dim profile histograms. 561 type binningP1D struct { 562 bins []BinP1D 563 dist Dist2D 564 outflows [2]Dist2D 565 xrange Range 566 xstep float64 567 } 568 569 func newBinningP1D(n int, xmin, xmax float64) binningP1D { 570 if xmin >= xmax { 571 panic("hbook: invalid X-axis limits") 572 } 573 if n <= 0 { 574 panic("hbook: X-axis with zero bins") 575 } 576 bng := binningP1D{ 577 bins: make([]BinP1D, n), 578 xrange: Range{Min: xmin, Max: xmax}, 579 } 580 bng.xstep = float64(n) / bng.xrange.Width() 581 width := bng.xrange.Width() / float64(n) 582 for i := range bng.bins { 583 bin := &bng.bins[i] 584 bin.xrange.Min = xmin + float64(i)*width 585 bin.xrange.Max = xmin + float64(i+1)*width 586 } 587 588 return bng 589 } 590 591 func (bng *binningP1D) entries() int64 { 592 return bng.dist.Entries() 593 } 594 595 func (bng *binningP1D) effEntries() float64 { 596 return bng.dist.EffEntries() 597 } 598 599 // xMin returns the low edge of the X-axis 600 func (bng *binningP1D) xMin() float64 { 601 return bng.xrange.Min 602 } 603 604 // xMax returns the high edge of the X-axis 605 func (bng *binningP1D) xMax() float64 { 606 return bng.xrange.Max 607 } 608 609 func (bng *binningP1D) fill(x, y, w float64) { 610 idx := bng.coordToIndex(x) 611 bng.dist.fill(x, y, w) 612 if idx < 0 { 613 bng.outflows[-idx-1].fill(x, y, w) 614 return 615 } 616 bng.bins[idx].fill(x, y, w) 617 } 618 619 // coordToIndex returns the bin index corresponding to the coordinate x. 620 func (bng *binningP1D) coordToIndex(x float64) int { 621 switch { 622 default: 623 i := int((x - bng.xrange.Min) * bng.xstep) 624 return i 625 case x < bng.xrange.Min: 626 return UnderflowBin1D 627 case x >= bng.xrange.Max: 628 return OverflowBin1D 629 } 630 } 631 632 func (bng *binningP1D) scaleW(f float64) { 633 bng.dist.scaleW(f) 634 bng.outflows[0].scaleW(f) 635 bng.outflows[1].scaleW(f) 636 for i := range bng.bins { 637 bin := &bng.bins[i] 638 bin.scaleW(f) 639 } 640 } 641 642 // Bins returns the slice of bins for this binning. 643 func (bng *binningP1D) Bins() []BinP1D { 644 return bng.bins 645 } 646 647 // BinP1D models a bin in a 1-dim space. 648 type BinP1D struct { 649 xrange Range 650 dist Dist2D 651 } 652 653 // Rank returns the number of dimensions for this bin. 654 func (BinP1D) Rank() int { return 1 } 655 656 func (b *BinP1D) scaleW(f float64) { 657 b.dist.scaleW(f) 658 } 659 660 func (b *BinP1D) fill(x, y, w float64) { 661 b.dist.fill(x, y, w) 662 } 663 664 // Entries returns the number of entries in this bin. 665 func (b *BinP1D) Entries() int64 { 666 return b.dist.Entries() 667 } 668 669 // EffEntries returns the effective number of entries \f$ = (\sum w)^2 / \sum w^2 \f$ 670 func (b *BinP1D) EffEntries() float64 { 671 return b.dist.EffEntries() 672 } 673 674 // SumW returns the sum of weights in this bin. 675 func (b *BinP1D) SumW() float64 { 676 return b.dist.SumW() 677 } 678 679 // SumW2 returns the sum of squared weights in this bin. 680 func (b *BinP1D) SumW2() float64 { 681 return b.dist.SumW2() 682 } 683 684 // XEdges returns the [low,high] edges of this bin. 685 func (b *BinP1D) XEdges() Range { 686 return b.xrange 687 } 688 689 // XMin returns the lower limit of the bin (inclusive). 690 func (b *BinP1D) XMin() float64 { 691 return b.xrange.Min 692 } 693 694 // XMax returns the upper limit of the bin (exclusive). 695 func (b *BinP1D) XMax() float64 { 696 return b.xrange.Max 697 } 698 699 // XMid returns the geometric center of the bin. 700 // i.e.: 0.5*(high+low) 701 func (b *BinP1D) XMid() float64 { 702 return 0.5 * (b.xrange.Min + b.xrange.Max) 703 } 704 705 // XWidth returns the (signed) width of the bin 706 func (b *BinP1D) XWidth() float64 { 707 return b.xrange.Max - b.xrange.Min 708 } 709 710 // XFocus returns the mean position in the bin, or the midpoint (if the 711 // sum of weights for this bin is 0). 712 func (b *BinP1D) XFocus() float64 { 713 if b.SumW() == 0 { 714 return b.XMid() 715 } 716 return b.XMean() 717 } 718 719 // XMean returns the mean X. 720 func (b *BinP1D) XMean() float64 { 721 return b.dist.xMean() 722 } 723 724 // XVariance returns the variance in X. 725 func (b *BinP1D) XVariance() float64 { 726 return b.dist.xVariance() 727 } 728 729 // XStdDev returns the standard deviation in X. 730 func (b *BinP1D) XStdDev() float64 { 731 return b.dist.xStdDev() 732 } 733 734 // XStdErr returns the standard error in X. 735 func (b *BinP1D) XStdErr() float64 { 736 return b.dist.xStdErr() 737 } 738 739 // XRMS returns the RMS in X. 740 func (b *BinP1D) XRMS() float64 { 741 return b.dist.xRMS() 742 }