code: def densitybin(x, weight=None, binwidth=None, bins=None, rangee=None):
      
      if all(pd.isnull(x)):
          return pd.DataFrame()
  
      if weight is None:
          weight = np.ones(len(x))
      weight = np.asarray(weight)
      weight[np.isnan(weight)] = 0
  
      if rangee is None:
          rangee = np.min(x), np.max(x)
      if bins is None:
          bins = 30
      if binwidth is None:
          binwidth = np.ptp(rangee) / bins
  
      # Sort weight and x, by x
      order = np.argsort(x)
      weight = weight[order]
      x = x[order]
  
      cbin = 0                # Current bin ID
      binn = [None] * len(x)  # The bin ID for each observation
      # End position of current bin (scan left to right)
      binend = -np.inf
  
      # Scan list and put dots in bins
      for i, value in enumerate(x):
          # If past end of bin, start a new bin at this point
          if value >= binend:
              binend = value + binwidth
              cbin = cbin + 1
          binn[i] = cbin
  
      def func(series):
          return (series.min()+series.max())/2
  
      results = pd.DataFrame({'x': x,
                              'bin': binn,
                              'binwidth': binwidth,
                              'weight': weight})
      # This is a plyr::ddply
      results['bincenter'] = results.groupby('bin')['x'].transform(func)
      return results
docs: Do density binning
  
      It does not collapse each bin with a count.
  
      Parameters
      ----------
      x : array-like
          Numbers to bin
      weight : array-like
          Weights
      binwidth : numeric
          Size of the bins
      rangee : tuple
          Range of x
  
      Returns
      -------
      data : DataFrame