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 }