code: def bin(values, bins, labels=None):
      
      bins = np.asarray(bins)
      if labels is None:
          labels = (bins[:-1] + np.diff(bins)/2.)
      else:
          labels = np.asarray(labels)
      dtype = 'float' if labels.dtype.kind == 'f' else 'O'
      binned = np.full_like(values, (np.nan if dtype == 'f' else None), dtype=dtype)
      for lower, upper, label in zip(bins[:-1], bins[1:], labels):
          condition = (values > lower) & (values <= upper)
          binned[np.where(condition)[0]] = label
      return binned
docs: Bins data into declared bins
  
      Bins data into declared bins. By default each bin is labelled
      with bin center values but an explicit list of bin labels may be
      defined.
  
      Args:
          values: Array of values to be binned
          bins: List or array containing the bin boundaries
          labels: List of labels to assign to each bin
              If the bins are length N the labels should be length N-1
  
      Returns:
          Array of binned values