nodes: 0, module; 1, function_definition; 2, function_name:bin; 3, parameters; 4, block; 5, identifier:x; 6, identifier:bins; 7, default_parameter; 8, default_parameter; 9, expression_statement; 10, if_statement; 11, if_statement; 12, if_statement; 13, comment:# digitize works on 1d array but not nd arrays.; 14, comment:# So I pass the flattened version of x and then reshape back into x's original shape; 15, return_statement; 16, identifier:maxX; 17, None; 18, identifier:minX; 19, None; 20, string; 21, comparison_operator:maxX is None; 22, block; 23, comparison_operator:minX is None; 24, block; 25, not_operator; 26, block; 27, expression_list; 28, string_content:bin signal x using 'binsN' bin. If minX, maxX are None, they default to the full
      range of the signal. If they are not None, everything above maxX gets assigned to
      binsN-1 and everything below minX gets assigned to 0, this is effectively the same
      as clipping x before passing it to 'bin'
  
      input:
      -----
          x:      signal to be binned, some sort of iterable
  
          bins:   int, number of bins
                  iterable, bin edges
  
          maxX:   clips data above maxX
  
          minX:   clips data below maxX
  
      output:
      ------
          binnedX:    x after being binned
  
          bins:       bins used for binning.
                      if input 'bins' is already an iterable it just returns the
                      same iterable
  
      example:
          # make 10 bins of equal length spanning from x.min() to x.max()
          bin(x, 10)
  
          # use predefined bins such that each bin has the same number of points (maximize
          entropy)
          binsN = 10
          percentiles = list(np.arange(0, 100.1, 100/binsN))
          bins = np.percentile(x, percentiles)
          bin(x, bins); 29, identifier:maxX; 30, None; 31, expression_statement; 32, identifier:minX; 33, None; 34, expression_statement; 35, call; 36, expression_statement; 37, call; 38, identifier:bins; 39, assignment; 40, assignment; 41, attribute; 42, argument_list; 43, assignment; 44, attribute; 45, argument_list; 46, identifier:maxX; 47, call; 48, identifier:minX; 49, call; 50, identifier:np; 51, identifier:iterable; 52, identifier:bins; 53, identifier:bins; 54, call; 55, call; 56, identifier:reshape; 57, attribute; 58, attribute; 59, argument_list; 60, attribute; 61, argument_list; 62, attribute; 63, argument_list; 64, attribute; 65, argument_list; 66, identifier:x; 67, identifier:shape; 68, identifier:x; 69, identifier:max; 70, identifier:x; 71, identifier:min; 72, identifier:np; 73, identifier:linspace; 74, identifier:minX; 75, binary_operator:maxX+1e-5; 76, binary_operator:bins+1; 77, identifier:np; 78, identifier:digitize; 79, call; 80, identifier:bins; 81, identifier:maxX; 82, float:1e-5; 83, identifier:bins; 84, integer:1; 85, attribute; 86, argument_list; 87, identifier:x; 88, identifier:ravel
code: def bin(x, bins, maxX=None, minX=None):
      '''
      bin signal x using 'binsN' bin. If minX, maxX are None, they default to the full
      range of the signal. If they are not None, everything above maxX gets assigned to
      binsN-1 and everything below minX gets assigned to 0, this is effectively the same
      as clipping x before passing it to 'bin'
  
      input:
      -----
          x:      signal to be binned, some sort of iterable
  
          bins:   int, number of bins
                  iterable, bin edges
  
          maxX:   clips data above maxX
  
          minX:   clips data below maxX
  
      output:
      ------
          binnedX:    x after being binned
  
          bins:       bins used for binning.
                      if input 'bins' is already an iterable it just returns the
                      same iterable
  
      example:
          # make 10 bins of equal length spanning from x.min() to x.max()
          bin(x, 10)
  
          # use predefined bins such that each bin has the same number of points (maximize
          entropy)
          binsN = 10
          percentiles = list(np.arange(0, 100.1, 100/binsN))
          bins = np.percentile(x, percentiles)
          bin(x, bins)
      '''
      if maxX is None:
          maxX = x.max()
  
      if minX is None:
          minX = x.min()
  
      if not np.iterable(bins):
          bins = np.linspace(minX, maxX+1e-5, bins+1)
  
      # digitize works on 1d array but not nd arrays.
      # So I pass the flattened version of x and then reshape back into x's original shape
      return np.digitize(x.ravel(), bins).reshape(x.shape), bins