go-hep.org/x/hep@v0.38.1/hbook/binning2d.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 "sort"
     8  
     9  // indices for the 2D-binning overflows
    10  const (
    11  	BngNW int = 1 + iota
    12  	BngN
    13  	BngNE
    14  	BngE
    15  	BngSE
    16  	BngS
    17  	BngSW
    18  	BngW
    19  )
    20  
    21  type Binning2D struct {
    22  	Bins     []Bin2D
    23  	Dist     Dist2D
    24  	Outflows [8]Dist2D
    25  	XRange   Range
    26  	YRange   Range
    27  	Nx       int
    28  	Ny       int
    29  	XEdges   []Bin1D
    30  	YEdges   []Bin1D
    31  }
    32  
    33  func newBinning2D(nx int, xlow, xhigh float64, ny int, ylow, yhigh float64) Binning2D {
    34  	if xlow >= xhigh {
    35  		panic(errInvalidXAxis)
    36  	}
    37  	if ylow >= yhigh {
    38  		panic(errInvalidYAxis)
    39  	}
    40  	if nx <= 0 {
    41  		panic(errEmptyXAxis)
    42  	}
    43  	if ny <= 0 {
    44  		panic(errEmptyYAxis)
    45  	}
    46  	bng := Binning2D{
    47  		Bins:   make([]Bin2D, nx*ny),
    48  		XRange: Range{Min: xlow, Max: xhigh},
    49  		YRange: Range{Min: ylow, Max: yhigh},
    50  		Nx:     nx,
    51  		Ny:     ny,
    52  		XEdges: make([]Bin1D, nx),
    53  		YEdges: make([]Bin1D, ny),
    54  	}
    55  	xwidth := bng.XRange.Width() / float64(bng.Nx)
    56  	ywidth := bng.YRange.Width() / float64(bng.Ny)
    57  	xmin := bng.XRange.Min
    58  	ymin := bng.YRange.Min
    59  	for ix := range bng.XEdges {
    60  		xbin := &bng.XEdges[ix]
    61  		xbin.Range.Min = xmin + float64(ix)*xwidth
    62  		xbin.Range.Max = xmin + float64(ix+1)*xwidth
    63  		for iy := range bng.YEdges {
    64  			ybin := &bng.YEdges[iy]
    65  			ybin.Range.Min = ymin + float64(iy)*ywidth
    66  			ybin.Range.Max = ymin + float64(iy+1)*ywidth
    67  			i := iy*nx + ix
    68  			bin := &bng.Bins[i]
    69  			bin.XRange.Min = xbin.Range.Min
    70  			bin.XRange.Max = xbin.Range.Max
    71  			bin.YRange.Min = ybin.Range.Min
    72  			bin.YRange.Max = ybin.Range.Max
    73  		}
    74  	}
    75  	return bng
    76  }
    77  
    78  func newBinning2DFromEdges(xedges, yedges []float64) Binning2D {
    79  	if len(xedges) <= 1 {
    80  		panic(errShortXAxis)
    81  	}
    82  	if !sort.IsSorted(sort.Float64Slice(xedges)) {
    83  		panic(errNotSortedXAxis)
    84  	}
    85  	if len(yedges) <= 1 {
    86  		panic(errShortYAxis)
    87  	}
    88  	if !sort.IsSorted(sort.Float64Slice(yedges)) {
    89  		panic(errNotSortedYAxis)
    90  	}
    91  	var (
    92  		nx    = len(xedges) - 1
    93  		ny    = len(yedges) - 1
    94  		xlow  = xedges[0]
    95  		xhigh = xedges[nx]
    96  		ylow  = yedges[0]
    97  		yhigh = yedges[ny]
    98  	)
    99  	bng := Binning2D{
   100  		Bins:   make([]Bin2D, nx*ny),
   101  		XRange: Range{Min: xlow, Max: xhigh},
   102  		YRange: Range{Min: ylow, Max: yhigh},
   103  		Nx:     nx,
   104  		Ny:     ny,
   105  		XEdges: make([]Bin1D, nx),
   106  		YEdges: make([]Bin1D, ny),
   107  	}
   108  	for ix, xmin := range xedges[:nx] {
   109  		xmax := xedges[ix+1]
   110  		if xmin == xmax {
   111  			panic(errDupEdgesXAxis)
   112  		}
   113  		bng.XEdges[ix].Range.Min = xmin
   114  		bng.XEdges[ix].Range.Max = xmax
   115  		for iy, ymin := range yedges[:ny] {
   116  			ymax := yedges[iy+1]
   117  			if ymin == ymax {
   118  				panic(errDupEdgesYAxis)
   119  			}
   120  			i := iy*nx + ix
   121  			bin := &bng.Bins[i]
   122  			bin.XRange.Min = xmin
   123  			bin.XRange.Max = xmax
   124  			bin.YRange.Min = ymin
   125  			bin.YRange.Max = ymax
   126  		}
   127  	}
   128  	for iy, ymin := range yedges[:ny] {
   129  		ymax := yedges[iy+1]
   130  		bng.YEdges[iy].Range.Min = ymin
   131  		bng.YEdges[iy].Range.Max = ymax
   132  	}
   133  	return bng
   134  }
   135  
   136  func (bng *Binning2D) entries() int64 {
   137  	return bng.Dist.Entries()
   138  }
   139  
   140  func (bng *Binning2D) effEntries() float64 {
   141  	return bng.Dist.EffEntries()
   142  }
   143  
   144  // xMin returns the low edge of the X-axis
   145  func (bng *Binning2D) xMin() float64 {
   146  	return bng.XRange.Min
   147  }
   148  
   149  // xMax returns the high edge of the X-axis
   150  func (bng *Binning2D) xMax() float64 {
   151  	return bng.XRange.Max
   152  }
   153  
   154  // yMin returns the low edge of the Y-axis
   155  func (bng *Binning2D) yMin() float64 {
   156  	return bng.YRange.Min
   157  }
   158  
   159  // yMax returns the high edge of the Y-axis
   160  func (bng *Binning2D) yMax() float64 {
   161  	return bng.YRange.Max
   162  }
   163  
   164  func (bng *Binning2D) fill(x, y, w float64) {
   165  	idx := bng.coordToIndex(x, y)
   166  	bng.Dist.fill(x, y, w)
   167  	if idx == len(bng.Bins) {
   168  		// GAP bin
   169  		return
   170  	}
   171  	if idx < 0 {
   172  		bng.Outflows[-idx-1].fill(x, y, w)
   173  		return
   174  	}
   175  	bng.Bins[idx].fill(x, y, w)
   176  }
   177  
   178  func (bng *Binning2D) coordToIndex(x, y float64) int {
   179  	ix := Bin1Ds(bng.XEdges).IndexOf(x)
   180  	iy := Bin1Ds(bng.YEdges).IndexOf(y)
   181  
   182  	switch {
   183  	case ix == bng.Nx && iy == bng.Ny: // GAP
   184  		return len(bng.Bins)
   185  	case ix == OverflowBin1D && iy == OverflowBin1D:
   186  		return -BngNE
   187  	case ix == OverflowBin1D && iy == UnderflowBin1D:
   188  		return -BngSE
   189  	case ix == UnderflowBin1D && iy == UnderflowBin1D:
   190  		return -BngSW
   191  	case ix == UnderflowBin1D && iy == OverflowBin1D:
   192  		return -BngNW
   193  	case ix == OverflowBin1D:
   194  		return -BngE
   195  	case ix == UnderflowBin1D:
   196  		return -BngW
   197  	case iy == OverflowBin1D:
   198  		return -BngN
   199  	case iy == UnderflowBin1D:
   200  		return -BngS
   201  	}
   202  	return iy*bng.Nx + ix
   203  }