Skip to content

Commit

Permalink
Deprecate initialize_data_collector (#2327)
Browse files Browse the repository at this point in the history
Deprecate the Model's `initialize_data_collector`.

Back when we had a schedulers it was logical, but now you can't forget to add Agents to a scheduler since that happens automatically.

The other reason was the `batch_run` assuming a Model attribute called `datacollector` to be present. An error in `batch_run` itself is far more robust to enforce that behavior, so that's added..

It also removes the behavior of directly collecting after init. Now you can do it when you want. It makes using the DataCollector explicit.

The migration guide was updated. Basically, replace:
```python
self.initialize_data_collector(...)
```

With:
```python
self.datacollector = DataCollector(...)
```
  • Loading branch information
EwoutH committed Sep 26, 2024
1 parent 1cfd624 commit 28e52d5
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 33 deletions.
16 changes: 16 additions & 0 deletions docs/migration_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,19 @@ def show_steps(model):

SolaraViz(model, components=[show_steps])
```

### Other changes
#### Removal of Model.initialize_data_collector
The `initialize_data_collector` in the Model class is removed. In the Model class, replace:

Replace:
```python
self.initialize_data_collector(...)
```

With:
```python
self.datacollector = DataCollector(...)
```

- Ref: [PR #2327](https://github.com/projectmesa/mesa/pull/2327), Mesa-examples [PR #208](https://github.com/projectmesa/mesa-examples/pull/208))
7 changes: 7 additions & 0 deletions mesa/batchrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ def batch_run(
Returns:
List[Dict[str, Any]]
Notes:
batch_run assumes the model has a `datacollector` attribute that has a DataCollector object initialized.
"""
runs_list = []
run_id = 0
Expand Down Expand Up @@ -173,6 +176,10 @@ def _collect_data(
step: int,
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
"""Collect model and agent data from a model using mesas datacollector."""
if not hasattr(model, "datacollector"):
raise AttributeError(
"The model does not have a datacollector attribute. Please add a DataCollector to your model."
)
dc = model.datacollector

model_data = {param: values[step] for param, values in dc.model_vars.items()}
Expand Down
15 changes: 7 additions & 8 deletions mesa/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,13 @@ def initialize_data_collector(
tables: tables to collect
"""
if not hasattr(self, "schedule") or self.schedule is None:
raise RuntimeError(
"You must initialize the scheduler (self.schedule) before initializing the data collector."
)
if self.schedule.get_agent_count() == 0:
raise RuntimeError(
"You must add agents to the scheduler before initializing the data collector."
)
warnings.warn(
"initialize_data_collector() is deprecated. Please use the DataCollector class directly. "
"by using `self.datacollector = DataCollector(...)`.",
DeprecationWarning,
stacklevel=2,
)

self.datacollector = DataCollector(
model_reporters=model_reporters,
agent_reporters=agent_reporters,
Expand Down
27 changes: 2 additions & 25 deletions tests/test_datacollector.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def __init__(self): # noqa: D107
self.n = 10
for i in range(1, self.n + 1):
self.schedule.add(MockAgent(self, val=i))
self.initialize_data_collector(
self.datacollector = DataCollector(
model_reporters={
"total_agents": lambda m: m.schedule.get_agent_count(),
"model_value": "model_val",
Expand Down Expand Up @@ -132,6 +132,7 @@ class TestDataCollector(unittest.TestCase):
def setUp(self):
"""Create the model and run it a set number of steps."""
self.model = MockModel()
self.model.datacollector.collect(self.model)
for i in range(7):
if i == 4:
self.model.schedule.remove(self.model.schedule._agents[3])
Expand Down Expand Up @@ -234,30 +235,6 @@ def test_exports(self):
table_df = data_collector.get_table_dataframe("not a real table")


class TestDataCollectorInitialization(unittest.TestCase):
"""Tests for DataCollector initialization."""

def setUp(self): # noqa: D102
self.model = Model()

def test_initialize_before_scheduler(self): # noqa: D102
with self.assertRaises(RuntimeError) as cm:
self.model.initialize_data_collector()
self.assertEqual(
str(cm.exception),
"You must initialize the scheduler (self.schedule) before initializing the data collector.",
)

def test_initialize_before_agents_added_to_scheduler(self): # noqa: D102
with self.assertRaises(RuntimeError) as cm:
self.model.schedule = BaseScheduler(self)
self.model.initialize_data_collector()
self.assertEqual(
str(cm.exception),
"You must add agents to the scheduler before initializing the data collector.",
)


class TestDataCollectorWithAgentTypes(unittest.TestCase):
"""Tests for DataCollector with agent-type-specific reporters."""

Expand Down

0 comments on commit 28e52d5

Please sign in to comment.