recnexteval 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. recnexteval/__init__.py +20 -0
  2. recnexteval/algorithms/__init__.py +99 -0
  3. recnexteval/algorithms/base.py +377 -0
  4. recnexteval/algorithms/baseline/__init__.py +10 -0
  5. recnexteval/algorithms/baseline/decay_popularity.py +110 -0
  6. recnexteval/algorithms/baseline/most_popular.py +72 -0
  7. recnexteval/algorithms/baseline/random.py +39 -0
  8. recnexteval/algorithms/baseline/recent_popularity.py +34 -0
  9. recnexteval/algorithms/itemknn/__init__.py +14 -0
  10. recnexteval/algorithms/itemknn/itemknn.py +119 -0
  11. recnexteval/algorithms/itemknn/itemknn_incremental.py +65 -0
  12. recnexteval/algorithms/itemknn/itemknn_incremental_movielens.py +95 -0
  13. recnexteval/algorithms/itemknn/itemknn_rolling.py +17 -0
  14. recnexteval/algorithms/itemknn/itemknn_static.py +31 -0
  15. recnexteval/algorithms/time_aware_item_knn/__init__.py +11 -0
  16. recnexteval/algorithms/time_aware_item_knn/base.py +248 -0
  17. recnexteval/algorithms/time_aware_item_knn/decay_functions.py +260 -0
  18. recnexteval/algorithms/time_aware_item_knn/ding_2005.py +52 -0
  19. recnexteval/algorithms/time_aware_item_knn/liu_2010.py +65 -0
  20. recnexteval/algorithms/time_aware_item_knn/similarity_functions.py +106 -0
  21. recnexteval/algorithms/time_aware_item_knn/top_k.py +61 -0
  22. recnexteval/algorithms/time_aware_item_knn/utils.py +47 -0
  23. recnexteval/algorithms/time_aware_item_knn/vaz_2013.py +50 -0
  24. recnexteval/algorithms/utils.py +51 -0
  25. recnexteval/datasets/__init__.py +109 -0
  26. recnexteval/datasets/base.py +316 -0
  27. recnexteval/datasets/config/__init__.py +113 -0
  28. recnexteval/datasets/config/amazon.py +188 -0
  29. recnexteval/datasets/config/base.py +72 -0
  30. recnexteval/datasets/config/lastfm.py +105 -0
  31. recnexteval/datasets/config/movielens.py +169 -0
  32. recnexteval/datasets/config/yelp.py +25 -0
  33. recnexteval/datasets/datasets/__init__.py +24 -0
  34. recnexteval/datasets/datasets/amazon.py +151 -0
  35. recnexteval/datasets/datasets/base.py +250 -0
  36. recnexteval/datasets/datasets/lastfm.py +121 -0
  37. recnexteval/datasets/datasets/movielens.py +93 -0
  38. recnexteval/datasets/datasets/test.py +46 -0
  39. recnexteval/datasets/datasets/yelp.py +103 -0
  40. recnexteval/datasets/metadata/__init__.py +58 -0
  41. recnexteval/datasets/metadata/amazon.py +68 -0
  42. recnexteval/datasets/metadata/base.py +38 -0
  43. recnexteval/datasets/metadata/lastfm.py +110 -0
  44. recnexteval/datasets/metadata/movielens.py +87 -0
  45. recnexteval/evaluators/__init__.py +189 -0
  46. recnexteval/evaluators/accumulator.py +167 -0
  47. recnexteval/evaluators/base.py +216 -0
  48. recnexteval/evaluators/builder/__init__.py +125 -0
  49. recnexteval/evaluators/builder/base.py +166 -0
  50. recnexteval/evaluators/builder/pipeline.py +111 -0
  51. recnexteval/evaluators/builder/stream.py +54 -0
  52. recnexteval/evaluators/evaluator_pipeline.py +287 -0
  53. recnexteval/evaluators/evaluator_stream.py +374 -0
  54. recnexteval/evaluators/state_management.py +310 -0
  55. recnexteval/evaluators/strategy.py +32 -0
  56. recnexteval/evaluators/util.py +124 -0
  57. recnexteval/matrix/__init__.py +48 -0
  58. recnexteval/matrix/exception.py +5 -0
  59. recnexteval/matrix/interaction_matrix.py +784 -0
  60. recnexteval/matrix/prediction_matrix.py +153 -0
  61. recnexteval/matrix/util.py +24 -0
  62. recnexteval/metrics/__init__.py +57 -0
  63. recnexteval/metrics/binary/__init__.py +4 -0
  64. recnexteval/metrics/binary/hit.py +49 -0
  65. recnexteval/metrics/core/__init__.py +10 -0
  66. recnexteval/metrics/core/base.py +126 -0
  67. recnexteval/metrics/core/elementwise_top_k.py +75 -0
  68. recnexteval/metrics/core/listwise_top_k.py +72 -0
  69. recnexteval/metrics/core/top_k.py +60 -0
  70. recnexteval/metrics/core/util.py +29 -0
  71. recnexteval/metrics/ranking/__init__.py +6 -0
  72. recnexteval/metrics/ranking/dcg.py +55 -0
  73. recnexteval/metrics/ranking/ndcg.py +78 -0
  74. recnexteval/metrics/ranking/precision.py +51 -0
  75. recnexteval/metrics/ranking/recall.py +42 -0
  76. recnexteval/models/__init__.py +4 -0
  77. recnexteval/models/base.py +69 -0
  78. recnexteval/preprocessing/__init__.py +37 -0
  79. recnexteval/preprocessing/filter.py +181 -0
  80. recnexteval/preprocessing/preprocessor.py +137 -0
  81. recnexteval/registries/__init__.py +67 -0
  82. recnexteval/registries/algorithm.py +68 -0
  83. recnexteval/registries/base.py +131 -0
  84. recnexteval/registries/dataset.py +37 -0
  85. recnexteval/registries/metric.py +57 -0
  86. recnexteval/settings/__init__.py +127 -0
  87. recnexteval/settings/base.py +414 -0
  88. recnexteval/settings/exception.py +8 -0
  89. recnexteval/settings/leave_n_out_setting.py +48 -0
  90. recnexteval/settings/processor.py +115 -0
  91. recnexteval/settings/schema.py +11 -0
  92. recnexteval/settings/single_time_point_setting.py +111 -0
  93. recnexteval/settings/sliding_window_setting.py +153 -0
  94. recnexteval/settings/splitters/__init__.py +14 -0
  95. recnexteval/settings/splitters/base.py +57 -0
  96. recnexteval/settings/splitters/n_last.py +39 -0
  97. recnexteval/settings/splitters/n_last_timestamp.py +76 -0
  98. recnexteval/settings/splitters/timestamp.py +82 -0
  99. recnexteval/settings/util.py +0 -0
  100. recnexteval/utils/__init__.py +115 -0
  101. recnexteval/utils/json_to_csv_converter.py +128 -0
  102. recnexteval/utils/logging_tools.py +159 -0
  103. recnexteval/utils/path.py +155 -0
  104. recnexteval/utils/url_certificate_installer.py +54 -0
  105. recnexteval/utils/util.py +166 -0
  106. recnexteval/utils/uuid_util.py +7 -0
  107. recnexteval/utils/yaml_tool.py +65 -0
  108. recnexteval-0.1.0.dist-info/METADATA +85 -0
  109. recnexteval-0.1.0.dist-info/RECORD +110 -0
  110. recnexteval-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,111 @@
1
+ import logging
2
+ import uuid
3
+ from warnings import warn
4
+
5
+ from recnexteval.algorithms import Algorithm
6
+ from recnexteval.evaluators.evaluator_pipeline import EvaluatorPipeline
7
+ from ..state_management import AlgorithmStateManager
8
+ from .base import Builder
9
+
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class EvaluatorPipelineBuilder(Builder):
15
+ """Builder to facilitate construction of evaluator.
16
+
17
+ Provides methods to set specific values for the evaluator and enforce checks
18
+ such that the evaluator can be constructed correctly and to avoid possible
19
+ errors when the evaluator is executed.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ ignore_unknown_user: bool = False,
25
+ ignore_unknown_item: bool = False,
26
+ seed: int = 42,
27
+ ) -> None:
28
+ """Initialize the EvaluatorPipelineBuilder.
29
+
30
+ Args:
31
+ ignore_unknown_user: Ignore unknown user in the evaluation.
32
+ ignore_unknown_item: Ignore unknown item in the evaluation.
33
+ seed: Random seed for reproducibility.
34
+ """
35
+ super().__init__(
36
+ ignore_unknown_user=ignore_unknown_user,
37
+ ignore_unknown_item=ignore_unknown_item,
38
+ seed=seed,
39
+ )
40
+ self.algo_state_mgr = AlgorithmStateManager()
41
+
42
+ def add_algorithm(
43
+ self,
44
+ algorithm: type[Algorithm],
45
+ params: dict[str, int] = {},
46
+ algo_uuid: None | uuid.UUID = None,
47
+ ) -> None:
48
+ """Add algorithm to evaluate.
49
+
50
+ Adding algorithm to evaluate on. The algorithm can be added by specifying the class type
51
+ or by specifying the class name as a string.
52
+
53
+ Args:
54
+ algorithm: Algorithm to evaluate.
55
+ params: Parameter for the algorithm.
56
+
57
+ Raises:
58
+ ValueError: If algorithm is not found in ALGORITHM_REGISTRY.
59
+ """
60
+ if not self._check_setting_exist():
61
+ raise RuntimeError(
62
+ "Setting has not been set. To ensure conformity, of the addition of"
63
+ " other components please set the setting first. Call add_setting() method."
64
+ )
65
+
66
+ self.algo_state_mgr.register(algo_ptr=algorithm, params=params, algo_uuid=algo_uuid)
67
+
68
+ def _check_ready(self) -> None:
69
+ """Check if the builder is ready to construct Evaluator.
70
+
71
+ Raises:
72
+ RuntimeError: If there are invalid configurations.
73
+ """
74
+ super()._check_ready()
75
+
76
+ if len(self.algo_state_mgr) == 0:
77
+ raise RuntimeError("No algorithms specified, can't construct Evaluator")
78
+
79
+ for algo_state in self.algo_state_mgr.values():
80
+ if (
81
+ algo_state.params is not None
82
+ and "K" in algo_state.params
83
+ and algo_state.params["K"] < self.setting.top_K
84
+ ):
85
+ warn(
86
+ f"Algorithm {algo_state.name} has K={algo_state.params['K']} but setting"
87
+ f" is configured top_K={self.setting.top_K}. The number of predictions"
88
+ " returned by the model is less than the K value to evaluate the predictions"
89
+ " on. This may distort the metric value."
90
+ )
91
+
92
+ def build(self) -> EvaluatorPipeline:
93
+ """Build Evaluator object.
94
+
95
+ Raises:
96
+ RuntimeError: If no metrics, algorithms or settings are specified.
97
+
98
+ Returns:
99
+ EvaluatorPipeline: The built evaluator object.
100
+ """
101
+ self._check_ready()
102
+ return EvaluatorPipeline(
103
+ # algorithm_entries=self.algorithm_entries,
104
+ algo_state_mgr=self.algo_state_mgr,
105
+ metric_entries=list(self.metric_entries.values()),
106
+ setting=self.setting,
107
+ metric_k=self.metric_k,
108
+ ignore_unknown_user=self.ignore_unknown_user,
109
+ ignore_unknown_item=self.ignore_unknown_item,
110
+ seed=self.seed,
111
+ )
@@ -0,0 +1,54 @@
1
+ import logging
2
+
3
+ from recnexteval.evaluators.evaluator_stream import EvaluatorStreamer
4
+ from .base import Builder
5
+
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class EvaluatorStreamerBuilder(Builder):
11
+ """Builder to facilitate construction of evaluator.
12
+
13
+ Provides methods to set specific values for the evaluator and enforce checks
14
+ such that the evaluator can be constructed correctly and to avoid possible
15
+ errors when the evaluator is executed.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ ignore_unknown_user: bool = False,
21
+ ignore_unknown_item: bool = False,
22
+ seed: int = 42,
23
+ ) -> None:
24
+ """Initialize the EvaluatorStreamerBuilder.
25
+
26
+ Args:
27
+ ignore_unknown_user: Ignore unknown user in the evaluation.
28
+ ignore_unknown_item: Ignore unknown item in the evaluation.
29
+ seed: Random seed for reproducibility.
30
+ """
31
+ super().__init__(
32
+ ignore_unknown_user=ignore_unknown_user,
33
+ ignore_unknown_item=ignore_unknown_item,
34
+ seed=seed,
35
+ )
36
+
37
+ def build(self) -> EvaluatorStreamer:
38
+ """Build Evaluator object.
39
+
40
+ Raises:
41
+ RuntimeError: If no metrics, algorithms or settings are specified.
42
+
43
+ Returns:
44
+ EvaluatorStreamer: The built evaluator object.
45
+ """
46
+ self._check_ready()
47
+ return EvaluatorStreamer(
48
+ metric_entries=list(self.metric_entries.values()),
49
+ setting=self.setting,
50
+ metric_k=self.metric_k,
51
+ ignore_unknown_user=self.ignore_unknown_user,
52
+ ignore_unknown_item=self.ignore_unknown_item,
53
+ seed=self.seed,
54
+ )
@@ -0,0 +1,287 @@
1
+ import logging
2
+ from warnings import warn
3
+
4
+ from tqdm import tqdm
5
+
6
+ from recnexteval.metrics import Metric
7
+ from recnexteval.registries import METRIC_REGISTRY, MetricEntry
8
+ from recnexteval.settings import EOWSettingError, Setting
9
+ from ..matrix import PredictionMatrix
10
+ from .accumulator import MetricAccumulator
11
+ from .base import EvaluatorBase
12
+ from .state_management import AlgorithmStateManager
13
+
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class EvaluatorPipeline(EvaluatorBase):
19
+ """Evaluation via pipeline
20
+
21
+ The diagram below shows the diagram of the pipeline evaluator for the
22
+ sliding window setting. If the setting is a single time point setting, the
23
+ evaluator will only run phase 1 and 2. This is the same as setting the sliding
24
+ window setting to only having 1 split.
25
+
26
+ .. image:: /_static/pipeline_scheme.png
27
+ :align: center
28
+ :scale: 40 %
29
+ :alt: Pipeline diagram
30
+
31
+ The pipeline is responsible for evaluating algorithms with metrics and evaluates
32
+ the algorithms in 3 phases:
33
+
34
+ 1. Pre-phase
35
+ 2. Evaluation phase
36
+ 3. Data release phase
37
+
38
+ In Setting 3 (single time point split), the evaluator will only run phase 1 and 2.
39
+ In Setting 1 (sliding window setting), the evaluator will run all 3 phases, with
40
+ phase 2 and 3 repeated for each window/split. This can be seen in the diagram
41
+ above.
42
+
43
+ :param algorithm_entries: List of algorithm entries
44
+ :type algorithm_entries: List[AlgorithmEntry]
45
+ :param metric_entries: List of metric entries
46
+ :type metric_entries: List[MetricEntry]
47
+ :param setting: Setting object
48
+ :type setting: Setting
49
+ :param metric_k: Number of top interactions to consider
50
+ :type metric_k: int
51
+ :param ignore_unknown_user: To ignore unknown users
52
+ :type ignore_unknown_user: bool
53
+ :param ignore_unknown_item: To ignore unknown items
54
+ :type ignore_unknown_item: bool
55
+ :param seed: Random seed for the evaluator
56
+ :type seed: Optional[int]
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ # algorithm_entries: list[AlgorithmEntry],
62
+ algo_state_mgr: AlgorithmStateManager,
63
+ metric_entries: list[MetricEntry],
64
+ setting: Setting,
65
+ metric_k: int,
66
+ ignore_unknown_user: bool = True,
67
+ ignore_unknown_item: bool = True,
68
+ seed: int = 42,
69
+ ):
70
+ super().__init__(
71
+ metric_entries=metric_entries,
72
+ setting=setting,
73
+ metric_k=metric_k,
74
+ ignore_unknown_user=ignore_unknown_user,
75
+ ignore_unknown_item=ignore_unknown_item,
76
+ seed=seed,
77
+ )
78
+
79
+ # self.algorithm_entries = algorithm_entries
80
+ self.algo_state_mgr = algo_state_mgr
81
+ """Algorithm state manager to manage the algorithm states."""
82
+ # self.algorithm: list[Algorithm]
83
+
84
+ def _ready_algo(self) -> None:
85
+ """Train the algorithms with the background data.
86
+
87
+ This method should be called after :meth:`_instantiate_algorithm()`. The
88
+ algorithms are trained with the background data, and the set of known
89
+ user/item is updated.
90
+
91
+ :raises ValueError: If algorithm is not instantiated
92
+ """
93
+ background_data = self.setting.background_data
94
+ self.user_item_base.update_known_user_item_base(background_data)
95
+ training_data = PredictionMatrix.from_interaction_matrix(background_data)
96
+ training_data.mask_user_item_shape(self.user_item_base.known_shape)
97
+
98
+ for algo_state in self.algo_state_mgr.values():
99
+ algo_state.algo_ptr.fit(training_data)
100
+
101
+ def _ready_evaluator(self) -> None:
102
+ """Pre-phase of the evaluator. (Phase 1)
103
+
104
+ Summary
105
+ -------
106
+
107
+ This method prepares the evaluator for the evaluation process.
108
+ It instantiates the algorithm, trains the algorithm with the background data,
109
+ instantiates the metric accumulator, and prepares the data generators.
110
+ The next phase of the evaluator is the evaluation phase (Phase 2).
111
+
112
+ Specifics
113
+ ---------
114
+
115
+ The evaluator is prepared by:
116
+ 1. Instantiating the each algorithm from the algorithm entries
117
+ 2. For each algorithm, train the algorithm with the background data from
118
+ :attr:`setting`
119
+ 3. Instantiate the metric accumulator for micro and macro metrics
120
+ 4. Create an entry for each metric in the macro metric accumulator
121
+ 5. Prepare the data generators for the setting
122
+ """
123
+ logger.info("Phase 1: Preparing the evaluator...")
124
+ self._ready_algo()
125
+ logger.debug("Algorithms trained with background data...")
126
+
127
+ self._acc = MetricAccumulator()
128
+ logger.debug("Metric accumulator instantiated...")
129
+
130
+ self.setting.restore()
131
+ logger.debug("Setting data generators ready...")
132
+
133
+ def _evaluate_step(self) -> None:
134
+ """Evaluate performance of the algorithms. (Phase 2)
135
+
136
+ Summary
137
+ -------
138
+
139
+ This method evaluates the performance of the algorithms with the metrics.
140
+ It takes the unlabeled data, predicts the interaction, and evaluates the
141
+ performance with the ground truth data.
142
+
143
+ Specifics
144
+ ---------
145
+
146
+ The evaluation is done by:
147
+ 1. Get the next unlabeled data and ground truth data from the setting
148
+ 2. Get the current timestamp from the setting
149
+ 3. Update the unknown user/item base with the ground truth data
150
+ 4. Mask the unlabeled data to the known user/item base shape
151
+ 5. Mask the ground truth data based on the provided flags :attr:`ignore_unknown_user`
152
+ and :attr:`ignore_unknown_item`
153
+ 6. Predict the interaction with the algorithms
154
+ 7. Check the shape of the prediction matrix
155
+ 8. Store the results in the micro metric accumulator
156
+ 9. Cache the results in the macro metric accumulator
157
+ 10. Repeat step 6 for each algorithm
158
+
159
+ :raises EOWSettingError: If there is no more data to be processed
160
+ """
161
+ logger.info("Phase 2: Evaluating the algorithms...")
162
+ try:
163
+ unlabeled_data, ground_truth_data, current_timestamp = self._get_evaluation_data()
164
+ except EOWSettingError as e:
165
+ raise e
166
+
167
+ # get the top k interaction per user
168
+ # X_true = ground_truth_data.get_users_n_first_interaction(self.metric_k)
169
+ X_true = ground_truth_data.item_interaction_sequence_matrix
170
+ for algo_state in self.algo_state_mgr.values():
171
+ X_pred = algo_state.algo_ptr.predict(unlabeled_data)
172
+ logger.debug("Shape of prediction matrix: %s", X_pred.shape)
173
+ logger.debug("Shape of ground truth matrix: %s", X_true.shape)
174
+ X_pred = self._prediction_shape_handler(X_true, X_pred)
175
+
176
+ for metric_entry in self.metric_entries:
177
+ metric_cls = METRIC_REGISTRY.get(metric_entry.name)
178
+ params = {
179
+ 'timestamp_limit': current_timestamp,
180
+ 'user_id_sequence_array': ground_truth_data.user_id_sequence_array,
181
+ 'user_item_shape': ground_truth_data.user_item_shape,
182
+ }
183
+ if metric_entry.K is not None:
184
+ params['K'] = metric_entry.K
185
+ metric = metric_cls(**params)
186
+ metric.calculate(X_true, X_pred)
187
+ self._acc.add(
188
+ metric=metric,
189
+ algorithm_name=self.algo_state_mgr.get_algorithm_identifier(algo_state.algorithm_uuid),
190
+ )
191
+
192
+ def _data_release_step(self) -> None:
193
+ """Data release phase. (Phase 3)
194
+
195
+ This method releases the data from the evaluator. This method should only
196
+ be called when the setting is a sliding window setting.
197
+
198
+ The data is released by updating the known user/item base with the
199
+ incremental data. After updating the known user/item base, the incremental
200
+ data is masked to the known user/item base shape. The algorithms are then
201
+ trained with the incremental data.
202
+
203
+ .. note::
204
+ Previously unknown user/item base is reset after the data release.
205
+ Since these unknown user/item base should be within the incremental
206
+ data released.
207
+ """
208
+ if not self.setting.is_sliding_window_setting:
209
+ return
210
+ logger.info("Phase 3: Releasing the data...")
211
+
212
+ incremental_data = self.setting.get_split_at(self._run_step).incremental
213
+ if incremental_data is None:
214
+ raise ValueError("Incremental data is None in sliding window setting")
215
+ self.user_item_base.reset_unknown_user_item_base()
216
+ self.user_item_base.update_known_user_item_base(incremental_data)
217
+ incremental_data = PredictionMatrix.from_interaction_matrix(incremental_data)
218
+ incremental_data.mask_user_item_shape(self.user_item_base.known_shape)
219
+
220
+ for algo_state in self.algo_state_mgr.values():
221
+ algo_state.algo_ptr.fit(incremental_data)
222
+
223
+ def run_step(self, reset=False) -> None:
224
+ """Run a single step of the evaluator.
225
+
226
+ This method runs a single step of the evaluator. The evaluator is split
227
+ into 3 phases. In the first run, all 3 phases are run. In the subsequent
228
+ runs, only the evaluation and data release phase are run. The method
229
+ will run all steps until the number of splits is reached. To rerun the
230
+ evaluation again, call with `reset=True`.
231
+
232
+ :param reset: To reset the evaluation step , defaults to False
233
+ :type reset: bool, optional
234
+ """
235
+ if reset:
236
+ self._run_step = 0
237
+ logger.info(f"There is a total of {self.setting.num_split} steps. Running step {self._run_step}")
238
+ self._ready_evaluator()
239
+ logger.info("Evaluator ready...")
240
+ self._evaluate_step()
241
+ self._data_release_step()
242
+ return
243
+
244
+ if self._run_step > self.setting.num_split:
245
+ logger.info("Finished running all steps, call `run_step(reset=True)` to run the evaluation again")
246
+ warn("Running this method again will not have any effect.")
247
+ return
248
+ logger.info("Running step %d", self._run_step)
249
+ self._evaluate_step()
250
+ self._data_release_step()
251
+
252
+ def run_steps(self, num_steps: int) -> None:
253
+ """Run multiple steps of the evaluator.
254
+
255
+ Effectively runs :meth:`run_step` method :param:`num_steps` times. Call
256
+ this method to run multiple steps of the evaluator at once.
257
+
258
+ :param num_steps: Number of steps to run
259
+ :type num_steps: int
260
+ """
261
+ if self._run_step + num_steps > self.setting.num_split:
262
+ raise ValueError(f"Cannot run {num_steps} steps, only {self.setting.num_split - self._run_step} steps left")
263
+ for _ in tqdm(range(num_steps)):
264
+ self.run_step()
265
+
266
+ def run(self) -> None:
267
+ """Run the evaluator across all steps and splits
268
+
269
+ Runs all 3 phases across all splits (if there are multiple splits).
270
+ This method should be called when the programmer wants to step through
271
+ all phases and splits to arrive to the metrics computed. An alternative
272
+ to running through all splits is to call :meth:`run_step` method which runs
273
+ only one step at a time.
274
+ """
275
+ self._ready_evaluator()
276
+
277
+ with tqdm(total=self.setting.num_split, desc="Evaluating steps") as pbar:
278
+ while self._run_step <= self.setting.num_split:
279
+ logger.info("Running step %d", self._run_step)
280
+ self._evaluate_step()
281
+ pbar.update(1)
282
+ # if is last step, no need to release data anymore
283
+ # since there is no more evaluation that can be done
284
+ # break out of the loop
285
+ if self._run_step == self.setting.num_split:
286
+ break
287
+ self._data_release_step()