View Javadoc
1   package org.djutils.stats.summarizers.event;
2   
3   import org.djutils.event.Event;
4   import org.djutils.event.EventListener;
5   import org.djutils.event.EventListenerMap;
6   import org.djutils.event.EventProducer;
7   import org.djutils.event.LocalEventProducer;
8   import org.djutils.exceptions.Throw;
9   import org.djutils.stats.summarizers.Tally;
10  import org.djutils.stats.summarizers.quantileaccumulator.NoStorageAccumulator;
11  import org.djutils.stats.summarizers.quantileaccumulator.QuantileAccumulator;
12  
13  /**
14   * The EventBasedTally class registers a series of values and provides mean, standard deviation, etc. of the registered values.
15   * It embeds an EventProducer so it can keep listeners informed about new observations, and it listens to external events to be
16   * able to receive observations, in addition to the register(...) method.
17   * <p>
18   * Copyright (c) 2002-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
19   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
20   * project is distributed under a three-clause BSD-style license, which can be found at
21   * <a href="https://simulation.tudelft.nl/dsol/3.0/license.html" target="_blank">
22   * https://simulation.tudelft.nl/dsol/3.0/license.html</a>. <br>
23   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
24   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
25   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
26   */
27  public class EventBasedTally extends Tally implements EventProducer, EventListener
28  {
29      /** The embedded EventProducer. */
30      private EventProducer eventProducer = null;
31  
32      /**
33       * Constructs a new EventBasedTally.
34       * @param description the description of this tally
35       * @param quantileAccumulator the input series accumulator that can approximate or compute quantiles.
36       */
37      public EventBasedTally(final String description, final QuantileAccumulator quantileAccumulator)
38      {
39          this(description, quantileAccumulator, new LocalEventProducer());
40      }
41  
42      /**
43       * Convenience constructor that uses a NoStorageAccumulator to estimate quantiles.
44       * @param description the description of this tally
45       */
46      public EventBasedTally(final String description)
47      {
48          this(description, new NoStorageAccumulator());
49      }
50  
51      /**
52       * Construct a new EventBasedCounter with a specific EventProducer, e.g. a remote one. The Tally uses uses a
53       * NoStorageAccumulator to estimate quantiles.
54       * @param description the description for this counter
55       * @param eventProducer the EventProducer to embed and use in this statistic
56       */
57      public EventBasedTally(final String description, final EventProducer eventProducer)
58      {
59          this(description, new NoStorageAccumulator(), eventProducer);
60      }
61  
62      /**
63       * Construct a new EventBasedCounter with a specific EventProducer, e.g. a remote one.
64       * @param description the description for this counter
65       * @param quantileAccumulator the input series accumulator that can approximate or compute quantiles.
66       * @param eventProducer the EventProducer to embed and use in this statistic
67       */
68      public EventBasedTally(final String description, final QuantileAccumulator quantileAccumulator,
69              final EventProducer eventProducer)
70      {
71          super(description, quantileAccumulator);
72          Throw.whenNull(eventProducer, "eventProducer cannot be null");
73          this.eventProducer = eventProducer;
74      }
75  
76      @Override
77      public EventListenerMap getEventListenerMap()
78      {
79          return this.eventProducer.getEventListenerMap();
80      }
81  
82      @Override
83      public void initialize()
84      {
85          super.initialize();
86          if (this.eventProducer != null)
87          {
88              this.eventProducer.fireEvent(StatisticsEvents.INITIALIZED_EVENT);
89          }
90      }
91  
92      @Override
93      @SuppressWarnings("checkstyle:designforextension")
94      public void notify(final Event event)
95      {
96          if (!(event.getContent() instanceof Number))
97          {
98              throw new IllegalArgumentException("Tally does not accept " + event);
99          }
100         double value = ((Number) event.getContent()).doubleValue();
101         register(value);
102     }
103 
104     @Override
105     public double register(final double value)
106     {
107         super.register(value);
108         if (hasListeners())
109         {
110             this.eventProducer.fireEvent(StatisticsEvents.OBSERVATION_ADDED_EVENT, value);
111             fireEvents();
112         }
113         return value;
114     }
115 
116     /**
117      * Method that can be overridden to fire own events or additional events when registering an observation.
118      */
119     protected void fireEvents()
120     {
121         this.eventProducer.fireEvent(StatisticsEvents.N_EVENT, getN());
122         this.eventProducer.fireEvent(StatisticsEvents.MIN_EVENT, getMin());
123         this.eventProducer.fireEvent(StatisticsEvents.MAX_EVENT, getMax());
124         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_MEAN_EVENT, getPopulationMean());
125         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_VARIANCE_EVENT, getPopulationVariance());
126         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_SKEWNESS_EVENT, getPopulationSkewness());
127         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_KURTOSIS_EVENT, getPopulationKurtosis());
128         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_EXCESS_KURTOSIS_EVENT, getPopulationExcessKurtosis());
129         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_STDEV_EVENT, getPopulationStDev());
130         this.eventProducer.fireEvent(StatisticsEvents.SUM_EVENT, getSum());
131         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_MEAN_EVENT, getSampleMean());
132         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_VARIANCE_EVENT, getSampleVariance());
133         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_SKEWNESS_EVENT, getSampleSkewness());
134         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_KURTOSIS_EVENT, getSampleKurtosis());
135         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_EXCESS_KURTOSIS_EVENT, getSampleExcessKurtosis());
136         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_STDEV_EVENT, getSampleStDev());
137     }
138 
139     @Override
140     @SuppressWarnings("checkstyle:designforextension")
141     public String toString()
142     {
143         return "EventBasedTally" + super.toString().substring(5);
144     }
145 
146 }