nodes: 0, module; 1, function_definition; 2, function_name:aggregate_repeated_calls; 3, parameters; 4, block; 5, identifier:frame; 6, identifier:options; 7, expression_statement; 8, if_statement; 9, expression_statement; 10, comment:# iterate over a copy of the children since it's going to mutate while we're iterating; 11, for_statement; 12, comment:# recurse into the children; 13, for_statement; 14, comment:# sort the children by time; 15, comment:# it's okay to use the internal _children list, sinde we're not changing the tree; 16, comment:# structure.; 17, expression_statement; 18, comment:# pylint: disable=W0212; 19, return_statement; 20, string; 21, comparison_operator:frame is None; 22, block; 23, assignment; 24, identifier:child; 25, attribute; 26, block; 27, identifier:child; 28, attribute; 29, block; 30, call; 31, identifier:frame; 32, string_content:Converts a timeline into a time-aggregate summary.
  
      Adds together calls along the same call stack, so that repeated calls appear as the same
      frame. Removes time-linearity - frames are sorted according to total time spent.
  
      Useful for outputs that display a summary of execution (e.g. text and html outputs); 33, identifier:frame; 34, None; 35, return_statement; 36, identifier:children_by_identifier; 37, dictionary; 38, identifier:frame; 39, identifier:children; 40, if_statement; 41, identifier:frame; 42, identifier:children; 43, expression_statement; 44, attribute; 45, argument_list; 46, None; 47, comparison_operator:child.identifier in children_by_identifier; 48, block; 49, else_clause; 50, call; 51, attribute; 52, identifier:sort; 53, keyword_argument; 54, keyword_argument; 55, attribute; 56, identifier:children_by_identifier; 57, expression_statement; 58, comment:# combine the two frames, putting the children and self_time into the aggregate frame.; 59, expression_statement; 60, if_statement; 61, comment:# remove this frame, it's been incorporated into aggregate_frame; 62, expression_statement; 63, comment:# never seen this identifier before. It becomes the aggregate frame.; 64, block; 65, identifier:aggregate_repeated_calls; 66, argument_list; 67, identifier:frame; 68, identifier:_children; 69, identifier:key; 70, call; 71, identifier:reverse; 72, True; 73, identifier:child; 74, identifier:identifier; 75, assignment; 76, augmented_assignment; 77, attribute; 78, block; 79, call; 80, expression_statement; 81, identifier:child; 82, keyword_argument; 83, identifier:methodcaller; 84, argument_list; 85, identifier:aggregate_frame; 86, subscript; 87, attribute; 88, attribute; 89, identifier:child; 90, identifier:children; 91, expression_statement; 92, attribute; 93, argument_list; 94, assignment; 95, identifier:options; 96, identifier:options; 97, string; 98, identifier:children_by_identifier; 99, attribute; 100, identifier:aggregate_frame; 101, identifier:self_time; 102, identifier:child; 103, identifier:self_time; 104, call; 105, identifier:child; 106, identifier:remove_from_parent; 107, subscript; 108, identifier:child; 109, string_content:time; 110, identifier:child; 111, identifier:identifier; 112, attribute; 113, argument_list; 114, identifier:children_by_identifier; 115, attribute; 116, identifier:aggregate_frame; 117, identifier:add_children; 118, attribute; 119, identifier:child; 120, identifier:identifier; 121, identifier:child; 122, identifier:children
code: def aggregate_repeated_calls(frame, options):
      '''
      Converts a timeline into a time-aggregate summary.
  
      Adds together calls along the same call stack, so that repeated calls appear as the same
      frame. Removes time-linearity - frames are sorted according to total time spent.
  
      Useful for outputs that display a summary of execution (e.g. text and html outputs)
      '''
      if frame is None:
          return None
  
      children_by_identifier = {}
  
      # iterate over a copy of the children since it's going to mutate while we're iterating
      for child in frame.children:
          if child.identifier in children_by_identifier:
              aggregate_frame = children_by_identifier[child.identifier]
  
              # combine the two frames, putting the children and self_time into the aggregate frame.
              aggregate_frame.self_time += child.self_time
              if child.children:
                  aggregate_frame.add_children(child.children)
  
              # remove this frame, it's been incorporated into aggregate_frame
              child.remove_from_parent()
          else:
              # never seen this identifier before. It becomes the aggregate frame.
              children_by_identifier[child.identifier] = child
  
      # recurse into the children
      for child in frame.children:
          aggregate_repeated_calls(child, options=options)
  
      # sort the children by time
      # it's okay to use the internal _children list, sinde we're not changing the tree
      # structure.
      frame._children.sort(key=methodcaller('time'), reverse=True)  # pylint: disable=W0212
  
      return frame