docs: def merge_bins(self, bin_ranges, axis=0):
          """
          Merge bins in bin ranges
  
          Parameters
          ----------
  
          bin_ranges : list of tuples
              A list of tuples of bin indices for each bin range to be merged
              into one bin.
  
          axis : int (default=1)
              The integer identifying the axis to merge bins along.
  
          Returns
          -------
  
          hist : TH1
              The rebinned histogram.
  
          Examples
          --------
  
          Merge the overflow bins into the first and last real bins::
  
              newhist = hist.merge_bins([(0, 1), (-2, -1)])
  
          """
          ndim = self.GetDimension()
          if axis > ndim - 1:
              raise ValueError(
                  "axis is out of range")
          axis_bins = self.nbins(axis=axis, overflow=True)
  
          # collect the indices along this axis to be merged
          # support negative indices via slicing
          windows = []
          for window in bin_ranges:
              if len(window) != 2:
                  raise ValueError(
                      "bin range tuples must contain "
                      "two elements: {0!r}".format(window))
              l, r = window
              if l == r:
                  raise ValueError(
                      "bin indices must not be equal "
                      "in a merging window: {0!r}".format(window))
              if (l < 0 and r >= 0) or (l > 0 and r > 0 and l > r):
                  raise ValueError(
                      "invalid bin range: {0!r}".format(window))
              if r == -1:
                  r = axis_bins
              else:
                  r += 1
              bin_idx = range(*slice(l, r).indices(axis_bins))
              if bin_idx: # skip []
                  windows.append(list(bin_idx))
  
          if not windows:
              # no merging will take place so return a clone
              return self.Clone()
  
          # check that windows do not overlap
          if len(windows) > 1:
              flattened = [idx for window in windows for idx in window]
              if len(flattened) != len(set(flattened)):
                  raise ValueError(
                      "bin index windows overlap: {0!r}".format(bin_ranges))
  
          # construct a mapping from old to new bin index along this axis
          windows.sort()
          mapping = {}
          left_idx = {}
          offset = 0
          for window in windows:
              # put underflow in first bin
              new_idx = window[0] - offset or 1
              left_idx[window[0] or 1] = None
              for idx in window:
                  mapping[idx] = new_idx
              offset += len(window) - 1
              if window[0] == 0:
                  offset -= 1
  
          new_axis_bins = axis_bins - offset
  
          # construct new bin edges
          new_edges = []
          for i, edge in enumerate(self._edges(axis)):
              if (i != axis_bins - 2 and i + 1 in mapping
                  and i + 1 not in left_idx):
                  continue
              new_edges.append(edge)
  
          # construct new histogram and fill
          new_hist = self.empty_clone(binning=new_edges, axis=axis)
  
          this_axis = self.axis(axis)
          new_axis = new_hist.axis(axis)
  
          def translate(idx):
              if idx in mapping:
                  return mapping[idx]
              if idx == 0:
                  return 0
              # use TH1.FindBin to determine where the bins should be merged
              return new_axis.FindBin(this_axis.GetBinCenter(idx))
  
          for bin in self.bins(overflow=True):
              xyz = bin.xyz
              new_xyz = list(xyz)
              new_xyz[axis] = translate(int(xyz[axis]))
  
              x, y, z = new_xyz
  
              new_v = new_hist.GetBinContent(x, y, z)
              new_hist.SetBinContent(x, y, z, new_v + bin.value)
  
              sum_w2 = self.get_sum_w2(*xyz)
              new_sum_w2 = new_hist.get_sum_w2(x, y, z)
              new_hist.set_sum_w2(sum_w2 + new_sum_w2, x, y, z)
  
          # transfer stats info
          stat_array = array('d', [0.] * 10)
          self.GetStats(stat_array)
          new_hist.PutStats(stat_array)
          entries = self.GetEntries()
          new_hist.SetEntries(entries)
          return new_hist