-
Notifications
You must be signed in to change notification settings - Fork 0
/
stats.py
148 lines (113 loc) · 3.73 KB
/
stats.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import statistics
from dataclasses import dataclass, field
from typing import List
@dataclass
class GeneratedStats:
values: List[float | int] = field(default_factory=list)
def append(self, value: float | int):
self.values.append(value)
@property
def mean(self):
if len(self.values) == 0:
return 0
try:
return statistics.mean(self.values)
except AssertionError:
return 0
@property
def median(self):
if len(self.values) == 0:
return 0
try:
return statistics.median(self.values)
except AssertionError:
return 0
@property
def minimum(self):
if len(self.values) == 0:
return 0
return min(self.values)
@property
def maximum(self):
if len(self.values) == 0:
return 0
return max(self.values)
def __len__(self):
return len(self.values)
def __str__(self):
return f'{self.minimum:.2f}/{self.mean:.2f}/{self.median:.2f}/{self.maximum:.2f}'
def to_dict(self):
return {
'mean': self.mean,
'median': self.median,
'minimum': self.minimum,
'maximum': self.maximum,
}
def __repr__(self) -> str:
return f'<GeneratedStats size={len(self.values)}>'
def copy(self):
return GeneratedStats(self.values.copy())
@dataclass
class CombinedStats:
stats: List[GeneratedStats | int] = field(default_factory=list)
def append(self, stat: GeneratedStats):
self.stats.append(stat)
def extend(self, stats: List[GeneratedStats]):
self.stats.extend(stats)
@property
def mean(self):
if len(self.stats) == 0:
return 0
if isinstance(self.stats[0], int):
return statistics.mean(self.stats)
return statistics.mean([stat.mean for stat in self.stats])
@property
def median(self):
if len(self.stats) == 0:
return 0
if isinstance(self.stats[0], int):
return statistics.median(self.stats)
return statistics.mean([stat.median for stat in self.stats])
@property
def minimum(self):
if len(self.stats) == 0:
return 0
if isinstance(self.stats[0], int):
return min(self.stats)
return statistics.mean([stat.minimum for stat in self.stats])
@property
def maximum(self):
if len(self.stats) == 0:
return 0
if isinstance(self.stats[0], int):
return max(self.stats)
return statistics.mean([stat.maximum for stat in self.stats])
def __str__(self):
return f'{self.minimum:.2f}/{self.mean:.2f}/{self.median:.2f}/{self.maximum:.2f}'
def __or__(self, other: 'GeneratedStats') -> 'CombinedStats':
"""Combines another GeneratedStats object using the | operator"""
if not isinstance(other, GeneratedStats):
return super().__or__(other)
return CombinedStats(self.stats + [other])
def __len__(self):
return len(self.stats)
def to_dict(self):
return {
'mean': self.mean,
'median': self.median,
'minimum': self.minimum,
'maximum': self.maximum,
}
@dataclass
class SimulationStats:
ticks: int
algorithm_name: str
wait_time: GeneratedStats
time_in_lift: GeneratedStats
occupancy: GeneratedStats
def __str__(self) -> str:
fmt_text = f'Tick: {self.ticks}\nAlgorithm: {self.algorithm_name}\n\n(MIN/MEAN/MED/MAX)\n\n'
fmt_text += f'Wait Time: {self.wait_time}\n'
fmt_text += f'Time in Lift: {self.time_in_lift}\n'
fmt_text += f'Occupancy: {self.occupancy}'
return fmt_text