class_docstr: Complements the event system by providing aggregation functionality. How the events are aggregated
  is specified by the event interface decorators (see event_decorators.py).
  
  Note that the statistics calculation for episode aggregators will automatically be
  triggered on every `increment_log_step()` call.
class_docstr_tok: ["Complements","the","event","system","by","providing","aggregation","functionality",".","How","the","events","are","aggregated","is","specified","by","the","event","interface","decorators","(","see","event_decorators.py",")",".","Note","that","the","statistics","calculation","for","episode","aggregators","will","automatically","be","triggered","on","every","`","increment_log_step","(",")","`","call","."]
docstr: Add a recorded event to this aggregator.
  
  The aggregator only keeps track of event/attributes with relevant statistics decoration, everything else is
  filtered out immediately.
  
  :param event_record:
docstr_tok: ["Add","a","recorded","event","to","this","aggregator",".","The","aggregator","only","keeps","track","of","event\/attributes","with","relevant","statistics","decoration",",","everything","else","is","filtered","out","immediately",".",":","param","event_record",":"]
code: def add_event(self, event_record: EventRecord) -> None:
      """
      Add a recorded event to this aggregator.
  
      The aggregator only keeps track of event/attributes with relevant statistics decoration, everything else is
      filtered out immediately.
  
      :param event_record:
      """
      # find out if there is an aggregation decorator specified for this event
      event = event_record.interface_method
      input_to_reducers = getattr(event, self.level.name, None)
  
      # no aggregation defined on this event
      if not input_to_reducers:
          return
  
      # handle grouping
      group_by_list = getattr(event, "group_by", None)
      group = (
          tuple(
              event_record.attributes[group_by]
              for group_by in group_by_list
          )
          if group_by_list
          else None
      )
  
      # add all event attributes if there are reducers operating on the event level
      if None in input_to_reducers:
          # flatten the dict if there is only a single event attribute
          self.add_value(
              event,
              self._flatten_single_attribute(
                  event_record.attributes, group_by_list
              ),
              name=None,
              group=group,
          )
  
      # iterate over the individual event attributes
      for (
          attribute_name,
          attribute_value,
      ) in event_record.attributes.items():
          reducers = input_to_reducers.get(attribute_name, None)
          if not reducers:
              continue
  
          self.add_value(
              event, attribute_value, name=attribute_name, group=group
          )
  
imports: from abc import ABC, abstractmethod
  from collections import defaultdict
  from enum import Enum
  from typing import Union, Callable, Dict, Optional, NamedTuple, List, Type, TypeVar, Tuple, Any
  import numpy
  from maze.core.events.event_record import EventRecord
  from maze.core.events.event_topic_factory import event_topic_factory
label_desc: Use image node_id 4 for calling the LogStatsAggregator obj's underlying member method code with example usage: obj.add_event(event_record) without return types