nodes: 0, module; 1, function_definition; 2, function_name:isordinal; 3, parameters; 4, block; 5, identifier:x; 6, expression_statement; 7, import_statement; 8, if_statement; 9, if_statement; 10, return_statement; 11, comment:"""Checks if a list or array contains ordinal data.
  
      Warning:
      --------
      This is not a reliable check for a variable being
      ordinal.
  
      The following criteria are used
  
      - There are more observations than unique values.
          Why? Ordinal means discrete or countable and
          I just assume that an ordinal sample should
          have some recurring (countable) values.
      - Values are integers or strings.
          Why? Ordinal scale data are usually labels
          (e.g. strings) or are encoded labels (e.g.
          as integers).
      - Ordinal scale data is sortable
          Integers imply that the Analyst encoded the
          labels according to the ordinal data's "natural
          order". Strings would imply that the alphabetic
          order would be the natual order (what is usually
          not the case)
  
      Usage:
      ------
          obs = 10
          np.random.seed(42)
          x1 = np.random.randint(1,50, (obs,))
          x2 = np.random.randint(0,3, (obs,))
          x3 = np.random.uniform(0,3, (obs,))
  
          flag, msg = isordinal(x1)
          if not flag:
              warnings.warn(msg)
      """; 12, aliased_import; 13, comparison_operator:len(x) == len(np.unique(x)); 14, block; 15, not_operator; 16, block; 17, expression_list; 18, dotted_name; 19, identifier:np; 20, call; 21, call; 22, return_statement; 23, call; 24, if_statement; 25, True; 26, string:"is ordinal"; 27, identifier:numpy; 28, identifier:len; 29, argument_list; 30, identifier:len; 31, argument_list; 32, expression_list; 33, identifier:isinstance; 34, argument_list; 35, not_operator; 36, block; 37, identifier:x; 38, call; 39, False; 40, parenthesized_expression; 41, subscript; 42, identifier:str; 43, call; 44, return_statement; 45, attribute; 46, argument_list; 47, concatenated_string; 48, identifier:x; 49, integer:0; 50, attribute; 51, argument_list; 52, expression_list; 53, identifier:np; 54, identifier:unique; 55, identifier:x; 56, string:"number of observations equals the "; 57, string:"number of unique values."; 58, identifier:np; 59, identifier:all; 60, call; 61, False; 62, string:"elements are not integer or strings."; 63, attribute; 64, argument_list; 65, identifier:np; 66, identifier:equal; 67, call; 68, integer:0; 69, attribute; 70, argument_list; 71, identifier:np; 72, identifier:mod; 73, identifier:x; 74, integer:1
code: def isordinal(x):
      """Checks if a list or array contains ordinal data.
  
      Warning:
      --------
      This is not a reliable check for a variable being
      ordinal.
  
      The following criteria are used
  
      - There are more observations than unique values.
          Why? Ordinal means discrete or countable and
          I just assume that an ordinal sample should
          have some recurring (countable) values.
      - Values are integers or strings.
          Why? Ordinal scale data are usually labels
          (e.g. strings) or are encoded labels (e.g.
          as integers).
      - Ordinal scale data is sortable
          Integers imply that the Analyst encoded the
          labels according to the ordinal data's "natural
          order". Strings would imply that the alphabetic
          order would be the natual order (what is usually
          not the case)
  
      Usage:
      ------
          obs = 10
          np.random.seed(42)
          x1 = np.random.randint(1,50, (obs,))
          x2 = np.random.randint(0,3, (obs,))
          x3 = np.random.uniform(0,3, (obs,))
  
          flag, msg = isordinal(x1)
          if not flag:
              warnings.warn(msg)
      """
      import numpy as np
  
      if len(x) == len(np.unique(x)):
          return False, ("number of observations equals the "
                         "number of unique values.")
  
      if not isinstance(x[0], str):
          if not np.all(np.equal(np.mod(x, 1), 0)):
              return False, "elements are not integer or strings."
  
      return True, "is ordinal"