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

     1  // Copyright ©2015 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  	"errors"
     9  	"sort"
    10  )
    11  
    12  // Indices for the under- and over-flow 1-dim bins.
    13  const (
    14  	UnderflowBin1D = -1
    15  	OverflowBin1D  = -2
    16  )
    17  
    18  var (
    19  	errInvalidXAxis   = errors.New("hbook: invalid X-axis limits")
    20  	errEmptyXAxis     = errors.New("hbook: X-axis with zero bins")
    21  	errShortXAxis     = errors.New("hbook: too few 1-dim X-bins")
    22  	errOverlapXAxis   = errors.New("hbook: invalid X-binning (overlap)")
    23  	errNotSortedXAxis = errors.New("hbook: X-edges slice not sorted")
    24  	errDupEdgesXAxis  = errors.New("hbook: duplicates in X-edge values")
    25  
    26  	errInvalidYAxis   = errors.New("hbook: invalid Y-axis limits")
    27  	errEmptyYAxis     = errors.New("hbook: Y-axis with zero bins")
    28  	errShortYAxis     = errors.New("hbook: too few 1-dim Y-bins")
    29  	errNotSortedYAxis = errors.New("hbook: Y-edges slice not sorted")
    30  	errDupEdgesYAxis  = errors.New("hbook: duplicates in Y-edge values")
    31  )
    32  
    33  // Binning1D is a 1-dim binning of the x-axis.
    34  type Binning1D struct {
    35  	Bins     []Bin1D
    36  	Dist     Dist1D
    37  	Outflows [2]Dist1D
    38  	XRange   Range
    39  }
    40  
    41  func newBinning1D(n int, xmin, xmax float64) Binning1D {
    42  	if xmin >= xmax {
    43  		panic(errInvalidXAxis)
    44  	}
    45  	if n <= 0 {
    46  		panic(errEmptyXAxis)
    47  	}
    48  	bng := Binning1D{
    49  		Bins:   make([]Bin1D, n),
    50  		XRange: Range{Min: xmin, Max: xmax},
    51  	}
    52  	width := bng.XRange.Width() / float64(n)
    53  	for i := range bng.Bins {
    54  		bin := &bng.Bins[i]
    55  		bin.Range.Min = xmin + float64(i)*width
    56  		bin.Range.Max = xmin + float64(i+1)*width
    57  	}
    58  	return bng
    59  }
    60  
    61  func newBinning1DFromBins(xbins []Range) Binning1D {
    62  	if len(xbins) < 1 {
    63  		panic(errShortXAxis)
    64  	}
    65  	n := len(xbins)
    66  	bng := Binning1D{
    67  		Bins: make([]Bin1D, n),
    68  	}
    69  	for i, xbin := range xbins {
    70  		bin := &bng.Bins[i]
    71  		bin.Range = xbin
    72  	}
    73  	sort.Sort(Bin1Ds(bng.Bins))
    74  	for i := range len(bng.Bins) - 1 {
    75  		b0 := bng.Bins[i]
    76  		b1 := bng.Bins[i+1]
    77  		if b0.Range.Max > b1.Range.Min {
    78  			panic(errOverlapXAxis)
    79  		}
    80  	}
    81  	bng.XRange = Range{Min: bng.Bins[0].XMin(), Max: bng.Bins[n-1].XMax()}
    82  	return bng
    83  }
    84  
    85  func newBinning1DFromEdges(edges []float64) Binning1D {
    86  	if len(edges) <= 1 {
    87  		panic(errShortXAxis)
    88  	}
    89  	if !sort.IsSorted(sort.Float64Slice(edges)) {
    90  		panic(errNotSortedXAxis)
    91  	}
    92  	n := len(edges) - 1
    93  	bng := Binning1D{
    94  		Bins:   make([]Bin1D, n),
    95  		XRange: Range{Min: edges[0], Max: edges[n]},
    96  	}
    97  	for i := range bng.Bins {
    98  		bin := &bng.Bins[i]
    99  		xmin := edges[i]
   100  		xmax := edges[i+1]
   101  		if xmin == xmax {
   102  			panic(errDupEdgesXAxis)
   103  		}
   104  		bin.Range.Min = xmin
   105  		bin.Range.Max = xmax
   106  	}
   107  	return bng
   108  }
   109  
   110  func (bng *Binning1D) clone() Binning1D {
   111  	o := Binning1D{
   112  		Bins: make([]Bin1D, len(bng.Bins)),
   113  		Dist: bng.Dist.clone(),
   114  		Outflows: [2]Dist1D{
   115  			bng.Outflows[0].clone(),
   116  			bng.Outflows[1].clone(),
   117  		},
   118  		XRange: bng.XRange.clone(),
   119  	}
   120  
   121  	for i, bin := range bng.Bins {
   122  		o.Bins[i] = bin.clone()
   123  	}
   124  
   125  	return o
   126  }
   127  
   128  func (bng *Binning1D) entries() int64 {
   129  	return bng.Dist.Entries()
   130  }
   131  
   132  func (bng *Binning1D) effEntries() float64 {
   133  	return bng.Dist.EffEntries()
   134  }
   135  
   136  // xMin returns the low edge of the X-axis
   137  func (bng *Binning1D) xMin() float64 {
   138  	return bng.XRange.Min
   139  }
   140  
   141  // xMax returns the high edge of the X-axis
   142  func (bng *Binning1D) xMax() float64 {
   143  	return bng.XRange.Max
   144  }
   145  
   146  func (bng *Binning1D) fill(x, w float64) {
   147  	idx := bng.coordToIndex(x)
   148  	bng.Dist.fill(x, w)
   149  	if idx < 0 {
   150  		bng.Outflows[-idx-1].fill(x, w)
   151  		return
   152  	}
   153  	if idx == len(bng.Bins) {
   154  		// gap bin.
   155  		return
   156  	}
   157  	bng.Bins[idx].fill(x, w)
   158  }
   159  
   160  // coordToIndex returns the bin index corresponding to the coordinate x.
   161  func (bng *Binning1D) coordToIndex(x float64) int {
   162  	switch {
   163  	case x < bng.XRange.Min:
   164  		return UnderflowBin1D
   165  	case x >= bng.XRange.Max:
   166  		return OverflowBin1D
   167  	}
   168  	return Bin1Ds(bng.Bins).IndexOf(x)
   169  }
   170  
   171  func (bng *Binning1D) scaleW(f float64) {
   172  	bng.Dist.scaleW(f)
   173  	bng.Outflows[0].scaleW(f)
   174  	bng.Outflows[1].scaleW(f)
   175  	for i := range bng.Bins {
   176  		bin := &bng.Bins[i]
   177  		bin.scaleW(f)
   178  	}
   179  }
   180  
   181  func (bng *Binning1D) Underflow() *Dist1D {
   182  	return &bng.Outflows[0]
   183  }
   184  
   185  func (bng *Binning1D) Overflow() *Dist1D {
   186  	return &bng.Outflows[1]
   187  }