SimPersistent
- class pydsol.core.statistics.SimPersistent(key: str, name: str, simulator: SimulatorInterface, *, producer: EventProducer | None = None, event_type: EventType | None = None)[source]
Bases:
EventBasedTimestampWeightedTally
,SimStatisticsInterface
The SimPersistent receive the observations in the same way as the EventBasedWeigtedTally statistics class, but this class is also aware of the Simulator. This means they can (a) subscribe to the Simulator’s WARMUP_EVENT taking care that the statistics are initialized appropriately, and (b) register themselves as output statistics in the model. The SimPersistent can immediately register itself with an EventProducer for a certain EventType in the model, that will generate the data for the statistics object. The EventProducer and EventTypes to listen to can also be added later with the listen_to method.
The SimPersistent can receive its observations by subscribing (listening) to one or more EventProducers that provides the values for the statistic using the EventProducer’s fire(…) method. This way, the statistic gathering and processing is decoupled from the process in the simulation that generates the data: there can be zero, one, or many statistics listeners for each data producing object in the simulation.
This event-based statistic object also fire events with the values of the calculated statistics values, so a GUI-element such as a graph or table can subscribe to this event-based statistics object and be automatically updated when values of the statistic change. Again, this provides decoupling and flexibility where on beforehand it is not known whether zero, one, or many (graphics or simulation) objects are interested in the values that this statistics object calculates.
The SimPersistent is a statistics object that calculates descriptive statistics for piecewise constant observations, such as weighted mean, weighted variance, minimum observation, maximum observation, etc. Contrary to the WeightedTally, the weights are implicitly calculated based on timestamps that are provided with each observation.
The initialize() method resets the statistics object. The initialize method can, for instance, be called when the warmup period of the simulation experiment has completed.
In order to properly ‘close’ the series of observations, a virtual observation has to be provided at the end of the observation period, to count the value and duration of the last interval into the statistics. The end_observations method is automatically called at the end of the replication. After the replication has ended, further calls to the register method will be silently ignored.
In a sense, the SimPersistent can be seen as a normal Tally where the observations are multiplied by the duration (interval between two successive timestamps) when the observation had that particular value. But instead of dividing by the number of observations to calculate the mean of the ordinary Tally, the sum of durations times observation values is divided by the total duration of the observation period till the last registered timestamp.
Example
In discrete-event simulation, the SimPersistent is often used to calculate statistics for (average) queue length, or (average) utilization of a server. Every time the actual queue length or utilization changes, the new value is registered with the timestamp, and the previous observation value is counted towards the statistic with the time interval between the previous timestamp and the new timestamp as the weight.
- Attributes:
_key (str) – the key by which the statistics object can be easily found
_name (str) – the name by which the statistics object can be identified
_n (int) – the number of observations
_n_nonzero (int) – the number of non-zero weights
_sum_of_weights (float) – the sum of the weights
_weighted_sum (float) – the sum of the observation values times their weights
_weight_times_variance (float) – the weighted variant of the second moment of the statistic
_min (float) – the lowest value in the current observations
_max (float) – the highest value in the current observations
_start_time (float) – timestamp of the first registered observation
_last_timestamp (float) – timestamp when the currently valid observation value was set
_last_value – currently valid observation value
_active – true after initializations until end_observations has been called
_simulator (SimulatorInterface) – the simulator
_event_types (set[EventType]) – the event types from EventProducers to listen to
- __init__(key: str, name: str, simulator: SimulatorInterface, *, producer: EventProducer | None = None, event_type: EventType | None = None)[source]
This event-based statistic object also fire events with the values of the calculated statistics values, so a GUI-element such as a graph or table can subscribe to this event-based statistics object and be automatically updated when values of the statistic change. Again, this provides decoupling and flexibility where on beforehand it is not known whether zero, one, or many (graphics or simulation) objects are interested in the values that this statistics object calculates.
The SimPersistent statistic object also fire events with the values of the calculated statistics values, so a GUI-element such as a graph or table can subscribe to this event-based statistics object and be automatically updated when values of the statistic change. Again, this provides decoupling and flexibility where on beforehand it is not known whether zero, one, or many (graphics or simulation) objects are interested in the values that this statistics object calculates.
The SimPersistent is a a statistics object that calculates descriptive statistics for a number of observations, such as mean, variance, minimum, maximum, sum, etc.
Given the fact that the SimPersistent is linked to the Simulator, it is subscribed to the WARMUP_EVENT of the Simulator to initialize the statistics, and to the END_REPLICATION event of the Simulator to properly terminate the series of observations.
- Parameters:
key (str) – The key by which the statistics object can be easily found.
name (str) – A descriptive name by which the statistics object can be identified.
simulator (SimulatorInterface) – The simulator for subscribing to the WARMUP_EVENT and END_REPLICATION_EVENT, and for accessing the Model to register this output statistic.
producer (EventProducer (optional)) – A class (often a simulation object such as a Server-type object, a Queue, or an Entity) that extends EventProducer, and is able to fire TIMESTAMP_DATA_EVENT to its listeners. This statistics object registers itself with the event producer for the specified event_type.
event_type (EventType (optional)) – The EventType that indicates the type of event we are interested in. By default use the TIMESTAMP_DATA_EVENT, but when the notify-method is changed to also receive other types of events, the listen_to method can of course also register for these event-types, possibly with a different payload, as well.
- Raises:
TypeError – when key is not a string
TypeError – when name is not a string
TypeError – when simulator is not of type SimulatorInterface
TypeError – if producer is not None, but it is not an EventProducer
TypeError – if event_type is not None, but it is not an EventType
- listen_to(producer: ~pydsol.core.pubsub.EventProducer, event_type: ~pydsol.core.pubsub.EventType = EventType[StatEvents.TIMESTAMP_DATA_EVENT metadata={self._metadata}])[source]
The statistics objects can register themselves with an EventProducer for a certain EventType in the model, that will generate the data for the statistics object. it is possible to call listen_to multiple time. In that case, the events from all EventProducers where this statistics object is registered, will be processed.
Sending the events with observations is done by the EventProducer’s fire(…) method. This way, the statistic gathering and processing is decoupled from the process in the simulation that generates the data: there can be zero, one, or many statistics listeners for each data producing object in the simulation.
- Parameters:
producer (EventProducer) – A class (often a simulation object such as a Server-type object, a Queue, or an Entity) that extends EventProducer, and is able to fire TIMESTAMP_DATA_EVENT to its listeners. This statistics object registers itself with the event producer for the specified event_type.
event_type (EventType) – The EventType that indicates the type of event we are interested in. By default it is the TIMESTAMP_DATA_EVENT, but when the notify-method is changed to also receive other types of events, the listen_to method can of course also register for these event-types as well.
- Raises:
TypeError – if producer is not an EventProducer
TypeError – if event_type is not an EventType
- property key: str
Return the key by which the statistics object can be easily found.
- Returns:
The key by which the statistics object can be easily found.
- Return type:
str
- property simulator: SimulatorInterface
Return the simulator. The statistic listens to the Simulator for the WARMUP-event.
- Returns:
An instance to the simulator to which this statistic is linked.
- Return type:
- notify(event: TimedEvent)[source]
The notify method is the method that is called by EventProducer where this object was added as a listener to register an observation. The EventType for the observation should typically be a TimedEvent with StatEvents.TIMESTAMP_DATA_EVENT as the EventType and the payload should be a float for the value. The timestamp-value combination will be registered by the statistic.
A second event to which the SimPersistent listens automatically is the WARMUP_EVENT as fired by the Simulator. When that event is received, the statistics are initialized.
A third event to which the SimPersistent listens is the END_REPLICATION event from the Simulator. This event takes care that the last observation value is taken into account by calling the end_observations method when the END_REPLICATION event is received.
Other events are silently skipped.
- Parameters:
event (TimedEvent) – (1) The event fired by the EventProducer to provide data to the statistic. The event’s content should be a single float with the value. (2) The WARMUP_EVENT as fired by the Simulator. This event has no payload. (3) The END_REPLICATION_EVENT as fired by the Simulator.
- Raises:
TypeError – when event is not of the type TimedEvent
TypeError – when the TIMESTAMP_DATA_EVENT’s payload is not a float
ValueError – when timestamp or value is NaN
ValueError – when the provided timestamp is before the last registered timestamp
- add_listener(event_type: EventType, listener: EventListener)
Add an EventListener to this EventProducer for a given EventType. If the listener already is registered for this EventType, this will be ignored.
- Parameters:
event_type (EventType) – the EventType for which this listener subscribes
listener (EventListener) – the subscriber to register for the provided Eventtype
- Raises:
EventError – if any of the arguments is of the wrong type
- end_observations(timestamp: float)
In order to properly ‘close’ the series of observations, a virtual observation has to be provided at the end of the observation period, to count the value and duration of the last interval into the statistics. The end_observations method takes care of ending the observation period. After calling end_observations(timestamp), further calls to the register method will be silently ignored.
- Parameters:
timestamp (float) – The timestamp of the final interval before the observations end. The last registered value will be counted into the statistics for the duration of (timestamp - last_timestamp).
- Raises:
ValueError – when the provided timestamp is nan
ValueError – when the provided timestamp is before the last registered timestamp
- has_listeners() bool
indicate whether this producer has any listeners or not
- initialize()
Initialize the statistics object, resetting all values to the state where no observations have been made. This method can, for instance, be called when the warmup period of the simulation experiment has completed.
- isactive() bool
Indicate whether the statistic is active and can register observations. After calling end_observations(timestamp) _active will be set to False and further calls to the register method will be silently ignored.
- Returns:
Whether the statistic is active and can register observations.
- Return type:
bool
- last_value() float
Return the last registered value (this value has not yet been counted into the statistics, unless end_observations() has been called).
- Returns:
The last registered value.
- Return type:
float
- max() float
Return the (unweighted) observation with the highest value. When no observations were registered, NaN is returned.
- Returns:
The observation with the highest value, or NaN when no observations were registered.
- Return type:
float
- min() float
Return the (unweighted) observation with the lowest value. When no observations were registered, NaN is returned.
- Returns:
The observation with the lowest value, or NaN when no observations were registered.
- Return type:
float
- n() int
Return the number of observations.
- Returns:
The number of observations.
- Return type:
int
- property name: str
Return the name of this statistics object.
- Returns:
The name of this statistics object.
- Return type:
str
- register(timestamp: float, value: float)
The event-based classes still have a register method. This method is called by the notify method, but can also be called separately. The method processes one timestamped observation.
The method processes one observation value and a timestamp that indicates from which time the observation is valid, and calculate all statistics up to and including the previous registered value for the duration between the last timestamp and the timestamp provided in this method. Successive timestamps can be the same, but a later timestamp cannot be before an earlier one.
Note
When two successive timestamps are the same, the observation value is counted towards the number of observations, and for the minimum and maximum observation value, but it does not contribute to the other statistics.
- Parameters:
timestamp (float) – The timestamp from which the observation value is valid.
value (float) – The observation value.
- Raises:
TypeError – when timestamp or value is not a number
ValueError – when weight or value is NaN
ValueError – when the provided timestamp is before the last registered timestamp
- remove_all_listeners(event_type: EventType | None = None, listener: EventListener | None = None)
Remove an EventListener (if given) for a provided EventType (if given) for this EventProducer. It is no problem if there are no matches. There are four situations:
- event_type == None and listener == None
all listeners for all event types are removed
- event_type == None and listener is specified
the listener is removed for any event for which it was registered
- event_type is specified and listener == None
all listeners are removed for the given event_type
- event_type and listener are both specified
the listener for the given event type is removed, if it was registered; in essence this is the same as remove_listener
- Parameters:
event_type (EventType, optional) – the EventType for which this listener unsubscribes
listener (EventListener, optional) – the subscriber to remove for the provided EventType
- Raises:
EventError – if any of the arguments is of the wrong type
- remove_listener(event_type: EventType, listener: EventListener)
Remove an EventListener of this EventProducer for a given EventType. If the listener is not registered for this EventType, this will be ignored.
- Parameters:
event_type (EventType) – the EventType for which this listener unsubscribes
listener (EventListener) – the subscriber to remove for the provided Eventtype
- Raises:
EventError – if any of the arguments is of the wrong type
Return a string representing a footer for a textual table with a monospaced font that can contain multiple tallies.
- classmethod report_header() str
Return a string representing a header for a textual table with a monospaced font that can contain multiple weighted tallies.
- report_line() str
Return a string representing a line with important statistics values for this tally, for a textual table with a monospaced font that can contain multiple tallies.
- weighted_mean() float
Return the weighted mean. When no observations were registered, NaN is returned.
The weighted mean of the WeightedTally is calculated with the formula:
\[\mu_{W} = \frac{\sum_{i=1}^{n} w_{i}.x_{i}}{\sum_{i=1}^{n} w_{i}}\]where n is the number of observations, \(w_{i}\) are the weights, and \(x_{i}\) are the observations.
- Returns:
The weighted mean, or NaN when no observations were registered.
- Return type:
float
- weighted_stdev(biased: bool = True) float
Return the (biased) weighted population standard deviation of all observations since the statistic initialization. The biased version needs at least one observation. For the unbiased version, two observations are needed. When too few observations were registered, NaN is returned.
The formula for the biased (population) weighted standard deviation is:
\[\sigma_{W} = \sqrt{ \frac{\sum_{i=1}^{n}{w_i (x_i - \mu_{W})^2}} {\sum_{i=1}^{n}{w_i}} }\]where \(w_i\) are the weights, \(x_i\) are the observations, \(n\) is the number of observations, and \(\mu_W\) is the weighted mean of the observations.
For the unbiased (sample) weighted variance (and, hence, for the standard deviation), different algorithms are suggested by the literature. As an example, R and MATLAB calculate weighted sample variance differently. SPSS rounds the sum of weights to the nearest integer and counts that as the ‘sample size’ in the unbiased formula. When weights are used as so-called reliability weights (non-integer) rather than as frequency weights (integer), rounding to the nearest integer and using that to calculate a ‘sample size’ is obviously incorrect. See the discussion at https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance and at https://stats.stackexchange.com/questions/51442/weighted-variance-one-more-time. Here we have chosen to implement the version that uses reliability weights. The reason is that the weights in simulation study are most usually time intervals that can be on any (non-integer) scale.
The formula used for the unbiased (sample) weighted standard deviation is:
\[S_{W} = \sqrt{ \frac{M}{M - 1} . \sigma^2_{W} }\]or as a complete formula:
\[S_{W} = \sqrt{ \frac{M}{M - 1} . \frac{\sum_{i=1}^{n}{w_i (x_i - \mu_{W})^2}} {\sum_{i=1}^{n}{w_i}} }\]where \(w_i\) are the weights, \(x_i\) are the observations, \(n\) is the number of observations, \(M\) is the number of non-zero observations, and \(\mu_W\) is the weighted mean of the observations.
- Parameters:
biased (bool) – Whether to return the biased (population) standard deviation or the unbiased (sample) standard deviation. By default, biased is True and the population standard deviation is returned.
- Returns:
The weighted standard deviation of all observations since the initialization, or NaN when too few (non-zero) observations were registered.
- Return type:
float
- weighted_sum() float
Return the sum of all observations times their weights since the statistic initialization.
- Returns:
The sum of the observations times their weights.
- Return type:
float
- weighted_variance(biased: bool = True) float
Return the weighted population variance of all observations since the statistic initialization. The biased version needs at least one observation. For the unbiased version, two observations with non-zero weights are needed. When too few observations were registered, NaN is returned.
The formula for the biased (population) weighted variance is:
\[\sigma^{2}_{W} = \frac{\sum_{i=1}^{n}{w_i (x_i - \mu_{W})^2}} {\sum_{i=1}^{n}{w_i}}\]where \(w_i\) are the weights, \(x_i\) are the observations, \(n\) is the number of observations, and \(\mu_W\) is the weighted mean of the observations.
For the unbiased (sample) weighted variance, different algorithms are suggested by the literature. As an example, R and MATLAB calculate weighted sample variance differently. SPSS rounds the sum of weights to the nearest integer and counts that as the ‘sample size’ in the unbiased formula. When weights are used as so-called reliability weights (non-integer) rather than as frequency weights (integer), rounding to the nearest integer and using that to calculate a ‘sample size’ is obviously incorrect. See the discussion at https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance and at https://stats.stackexchange.com/questions/51442/weighted-variance-one-more-time. Here we have chosen to implement the version that uses reliability weights. The reason is that the weights in simulation study are most usually time intervals that can be on any (non-integer) scale.
The formula used for the unbiased (sample) weighted variance is:
\[S^{2}_{W} = \frac{M}{M - 1} . \sigma^2_{W}\]or as a complete formula:
\[S^{2}_{W} = \frac{M}{M - 1} . \frac{\sum_{i=1}^{n}{w_i (x_i-\mu_{W})^2}} {\sum_{i=1}^{n}{w_i}}\]where \(w_i\) are the weights, \(x_i\) are the observations, \(n\) is the number of observations, \(M\) is the number of non-zero observations, and \(\mu_W\) is the weighted mean of the observations.
- Parameters:
biased (bool) – Whether to return the biased (population) variance or the unbiased (sample) variance. By default, biased is True and the population variance is returned.
- Returns:
The weighted variance of all observations since the initialization, or NaN when too few (non-zero) observations were registered.
- Return type:
float