nodes: 0, module; 1, function_definition; 2, function_name:_group_models; 3, parameters; 4, block; 5, identifier:self; 6, expression_statement; 7, expression_statement; 8, comment:# Add models to appropriate list. We only aggregate models which; 9, comment:# completed successfully and have a valid AIC and BMD.; 10, for_statement; 11, comment:# Sort each list by the number of parameters; 12, function_definition; 13, for_statement; 14, return_statement; 15, comment:"""
          If AIC and BMD are numeric and identical, then treat models as
          identical. Returns a list of lists. The outer list is a list of related
          models, the inner list contains each individual model, sorted by the
          number of parameters in ascending order.
  
          This is required because in some cases, a higher-order model may not use
          some parameters and can effectively collapse to a lower order model
          (for example, a 2nd order polynomial and power model may collapse to a
          linear model). In summary outputs, we may want to present all models in
          one row, since they are the same model effectively.
          """; 16, assignment; 17, pattern_list; 18, call; 19, block; 20, function_name:_get_num_params; 21, parameters; 22, block; 23, pattern_list; 24, call; 25, block; 26, call; 27, identifier:od; 28, call; 29, identifier:i; 30, identifier:model; 31, identifier:enumerate; 32, argument_list; 33, expression_statement; 34, if_statement; 35, identifier:model; 36, return_statement; 37, identifier:key; 38, identifier:_models; 39, attribute; 40, argument_list; 41, expression_statement; 42, identifier:list; 43, argument_list; 44, identifier:OrderedDict; 45, argument_list; 46, attribute; 47, assignment; 48, boolean_operator; 49, block; 50, else_clause; 51, parenthesized_expression; 52, identifier:od; 53, identifier:items; 54, call; 55, call; 56, identifier:self; 57, identifier:models; 58, identifier:output; 59, call; 60, boolean_operator; 61, comparison_operator:output["BMD"] > 0; 62, expression_statement; 63, if_statement; 64, block; 65, conditional_expression:len(model.output["parameters"])
                  if hasattr(model, "output") and "parameters" in model.output
                  else 0; 66, attribute; 67, argument_list; 68, attribute; 69, argument_list; 70, identifier:getattr; 71, argument_list; 72, call; 73, call; 74, subscript; 75, integer:0; 76, assignment; 77, comparison_operator:key in od; 78, block; 79, else_clause; 80, expression_statement; 81, call; 82, boolean_operator; 83, integer:0; 84, identifier:_models; 85, identifier:sort; 86, keyword_argument; 87, identifier:od; 88, identifier:values; 89, identifier:model; 90, string:"output"; 91, dictionary; 92, attribute; 93, argument_list; 94, attribute; 95, argument_list; 96, identifier:output; 97, string:"BMD"; 98, identifier:key; 99, call; 100, identifier:key; 101, identifier:od; 102, expression_statement; 103, block; 104, assignment; 105, identifier:len; 106, argument_list; 107, call; 108, comparison_operator:"parameters" in model.output; 109, identifier:key; 110, identifier:_get_num_params; 111, identifier:output; 112, identifier:get; 113, string:"AIC"; 114, identifier:output; 115, identifier:get; 116, string:"BMD"; 117, attribute; 118, argument_list; 119, call; 120, expression_statement; 121, subscript; 122, list; 123, subscript; 124, identifier:hasattr; 125, argument_list; 126, string:"parameters"; 127, attribute; 128, string:"{}-{}"; 129, identifier:format; 130, subscript; 131, subscript; 132, attribute; 133, argument_list; 134, assignment; 135, identifier:od; 136, identifier:i; 137, identifier:model; 138, attribute; 139, string:"parameters"; 140, identifier:model; 141, string:"output"; 142, identifier:model; 143, identifier:output; 144, identifier:output; 145, string:"AIC"; 146, identifier:output; 147, string:"BMD"; 148, subscript; 149, identifier:append; 150, identifier:model; 151, subscript; 152, list; 153, identifier:model; 154, identifier:output; 155, identifier:od; 156, identifier:key; 157, identifier:od; 158, identifier:key; 159, identifier:model
code: def _group_models(self):
          """
          If AIC and BMD are numeric and identical, then treat models as
          identical. Returns a list of lists. The outer list is a list of related
          models, the inner list contains each individual model, sorted by the
          number of parameters in ascending order.
  
          This is required because in some cases, a higher-order model may not use
          some parameters and can effectively collapse to a lower order model
          (for example, a 2nd order polynomial and power model may collapse to a
          linear model). In summary outputs, we may want to present all models in
          one row, since they are the same model effectively.
          """
          od = OrderedDict()
  
          # Add models to appropriate list. We only aggregate models which
          # completed successfully and have a valid AIC and BMD.
          for i, model in enumerate(self.models):
              output = getattr(model, "output", {})
              if output.get("AIC") and output.get("BMD") and output["BMD"] > 0:
                  key = "{}-{}".format(output["AIC"], output["BMD"])
                  if key in od:
                      od[key].append(model)
                  else:
                      od[key] = [model]
              else:
                  od[i] = [model]
  
          # Sort each list by the number of parameters
          def _get_num_params(model):
              return (
                  len(model.output["parameters"])
                  if hasattr(model, "output") and "parameters" in model.output
                  else 0
              )
  
          for key, _models in od.items():
              _models.sort(key=_get_num_params)
  
          return list(od.values())