Skip to content

Feature/abstract data collector #156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Jun 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7799b45
abstract
Ben-geo May 27, 2025
ad86972
list removed
Ben-geo May 28, 2025
8ec3dd5
descriptions
Ben-geo May 28, 2025
2a9ef69
fleshed out flush
Ben-geo May 28, 2025
50a59f7
ent
Ben-geo May 28, 2025
18028e6
reset functionality
Ben-geo May 28, 2025
dd24042
ent
Ben-geo May 28, 2025
2e3bc0f
removed register stats func
Ben-geo May 28, 2025
0b19475
doc fixes
Ben-geo May 28, 2025
ec04fd4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 28, 2025
bc6e9b1
more descriptive
Ben-geo May 31, 2025
5847323
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 31, 2025
444a863
resolve
Ben-geo May 31, 2025
2ac2b23
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 31, 2025
83724f4
removed doc
Ben-geo May 31, 2025
7a77cf2
add doc to flush
Ben-geo May 31, 2025
1f2a748
removed load_data
Ben-geo May 31, 2025
03d4784
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 31, 2025
870a7d6
condtitional collect
Ben-geo Jun 1, 2025
8d3643c
trigger default is pass
Ben-geo Jun 1, 2025
a88d35b
added seed and fixed docs
Ben-geo Jun 1, 2025
063adfd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 1, 2025
3ced8d3
adding pre-commit and ruff to dev dependencies
adamamer20 Jun 1, 2025
9cac504
Merge branch 'main' into feature/abstract-data-collector
adamamer20 Jun 1, 2025
9a05e68
precommit
Ben-geo Jun 4, 2025
7b32764
Merge branch 'feature/abstract-data-collector' of https://github.com/…
adamamer20 Jun 6, 2025
3d2afb3
Merge branch 'main' of https://github.com/projectmesa/mesa-frames int…
adamamer20 Jun 6, 2025
cb50066
Merge branch 'main' into feature/abstract-data-collector
adamamer20 Jun 6, 2025
aa8da74
Merge branch 'main' of https://github.com/projectmesa/mesa-frames int…
adamamer20 Jun 6, 2025
7843a76
Merge branch 'feature/abstract-data-collector' of https://github.com/…
adamamer20 Jun 6, 2025
65277f2
fix: uv.lock was outdated
adamamer20 Jun 6, 2025
0b55cb6
Merge branch 'main' into feature/abstract-data-collector
adamamer20 Jun 6, 2025
a14af67
spell check
Ben-geo Jun 6, 2025
108bfbb
periods
Ben-geo Jun 6, 2025
15e1585
precommit
Ben-geo Jun 6, 2025
6be3274
uv
Ben-geo Jun 6, 2025
efdd04d
Merge branch 'main' into feature/abstract-data-collector
Ben-geo Jun 6, 2025
fa788e9
suggested changes
Ben-geo Jun 8, 2025
5123bb7
sync
Ben-geo Jun 8, 2025
1329439
Merge branch 'main' into feature/abstract-data-collector
adamamer20 Jun 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions mesa_frames/abstract/datacollector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
"""
Abstract base classes for data collection components in mesa-frames.

This module defines the core abstractions for data collection in mesa-frames.
It provides a standardized interface for collecting model- and agent-level
data during simulation runs, supporting flexible triggers, custom statistics,
and optional external storage.

Classes:
AbstractDataCollector:
An abstract base class defining the structure and core logic for
all data collector implementations. It supports flexible reporting
of model and agent attributes, conditional data collection using
triggers, and pluggable backends for storage.

These classes are designed to be subclassed by concrete implementations that
handle the specifics of data collection and storage such as in-memory, CSV,
or database-backed collectors, potentially using Polars for high-performance
tabular operations.

Usage:
These classes should not be instantiated directly. Instead, they should be
subclassed to create concrete DataCollector:

from mesa_frames.abstract.datacollector import AbstractDataCollector

class DataCollector(AbstractDataCollector):
def collect(self):
# Implementation using Polars DataFrame to collect model and agent data
...

def conditional_collect(self):
# Implementation using Polars DataFrame to collect model and agent data if trigger returns True
...

def data(self):
# Returns the data currently in memory
...

def flush(self):
# Persists collected data if configured and optionally deletes data from memory
...

For more detailed information on each class, refer to their individual docstrings.
"""

from abc import ABC, abstractmethod
from typing import Dict, Optional, Union, Any, Literal, List
from collections.abc import Callable
from mesa_frames import ModelDF
import polars as pl

Check warning on line 51 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L47-L51

Added lines #L47 - L51 were not covered by tests


class AbstractDataCollector(ABC):

Check warning on line 54 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L54

Added line #L54 was not covered by tests
"""
Abstract Base Class for Mesa-Frames DataCollector.

This class defines methods for collecting data from both model and agents.
Sub classes must implement logic for the methods
"""

_model: ModelDF
_model_reporters: dict[str, Callable] | None
_agent_reporters: dict[str, str | Callable] | None
_trigger: Callable[..., bool]
_reset_memory = bool
_storage_uri: Literal["memory:", "csv:", "postgresql:"]
_frames: list[pl.DataFrame]

Check warning on line 68 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L62-L68

Added lines #L62 - L68 were not covered by tests

def __init__(

Check warning on line 70 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L70

Added line #L70 was not covered by tests
self,
model: ModelDF,
model_reporters: dict[str, Callable] | None = None,
agent_reporters: dict[str, str | Callable] | None = None,
trigger: Callable[[Any], bool] | None = None,
reset_memory: bool = True,
storage: Literal["memory:", "csv:", "postgresql:"] = "memory:",
):
"""
Initialize a Datacollector.

Parameters
----------
model : ModelDF
The model object from which data is collected.
model_reporters : dict[str, Callable] | None
Functions to collect data at the model level.
agent_reporters : dict[str, str | Callable] | None
Attributes or functions to collect data at the agent level.
trigger : Callable[[Any], bool] | None
A function(model) -> bool that determines whether to collect data.
reset_memory : bool
Whether to reset in-memory data after flushing. Default is True.
storage : Literal["memory:", "csv:", "postgresql:"]
Storage backend URI (e.g. 'memory:', 'csv:', 'postgresql:').
"""
self._model = model
self._model_reporters = model_reporters or {}
self._agent_reporters = agent_reporters or {}
self._trigger = trigger or (lambda model: False)
self._reset_memory = reset_memory
self._storage_uri = storage or "memory:"
self._frames = []

Check warning on line 103 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L97-L103

Added lines #L97 - L103 were not covered by tests

def collect(self) -> None:

Check warning on line 105 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L105

Added line #L105 was not covered by tests
"""
Trigger Data collection.

This method calls _collect() to perform actual data collection.

Example
-------
>>> datacollector.collect()
"""
self._collect()

Check warning on line 115 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L115

Added line #L115 was not covered by tests

def conditional_collect(self) -> None:

Check warning on line 117 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L117

Added line #L117 was not covered by tests
"""
Trigger data collection if condition is met.

This method caslls _collect() to perform actual data collection

Example
-------
>>> datacollector.conditional_collect()
"""
if self._should_collect():
self._collect()

Check warning on line 128 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L127-L128

Added lines #L127 - L128 were not covered by tests

def _should_collect(self) -> bool:

Check warning on line 130 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L130

Added line #L130 was not covered by tests
"""
Evaluate whether data should be collected at current step.

Returns
-------
bool
True if the configured trigger condition is met, False otherwise.
"""
return self._trigger(self._model)

Check warning on line 139 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L139

Added line #L139 was not covered by tests

@abstractmethod
def _collect(self):

Check warning on line 142 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L141-L142

Added lines #L141 - L142 were not covered by tests
"""
Perform the actual data collection logic.

This method must be im
"""
pass

Check warning on line 148 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L148

Added line #L148 was not covered by tests

@property
@abstractmethod
def data(self) -> Any:

Check warning on line 152 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L150-L152

Added lines #L150 - L152 were not covered by tests
"""
Returns collected data currently in memory as a dataframe.

Example:
-------
>>> df = datacollector.data
>>> print(df)
"""
pass

Check warning on line 161 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L161

Added line #L161 was not covered by tests

def flush(self) -> None:

Check warning on line 163 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L163

Added line #L163 was not covered by tests
"""
Persist all collected data to configured backend.

After flushing data optionally clears in-memory
data buffer if `reset_memory` is True (default behavior).

use this method to save collected data.


Example
-------
>>> datacollector.flush()
>>> # Data is saved externally and in-memory buffers are cleared if configured
"""
self._flush()
if self._reset_memory:
self._reset()

Check warning on line 180 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L178-L180

Added lines #L178 - L180 were not covered by tests

def _reset(self):

Check warning on line 182 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L182

Added line #L182 was not covered by tests
"""
Clear all collected data currently stored in memory.

Use this to free memory or start fresh without affecting persisted data.

"""
self._frames = []

Check warning on line 189 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L189

Added line #L189 was not covered by tests

@abstractmethod
def _flush(self) -> None:

Check warning on line 192 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L191-L192

Added lines #L191 - L192 were not covered by tests
"""
Implement persistence of collected data to external storage.

This method must be implemented by subclasses to handle
backend-specific data saving operations.
"""
pass

Check warning on line 199 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L199

Added line #L199 was not covered by tests

@property
def seed(self) -> int:

Check warning on line 202 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L201-L202

Added lines #L201 - L202 were not covered by tests
"""
Function to get the model seed.

Example:
--------
>>> seed = datacollector.seed
"""
return self._model._seed

Check warning on line 210 in mesa_frames/abstract/datacollector.py

View check run for this annotation

Codecov / codecov/patch

mesa_frames/abstract/datacollector.py#L210

Added line #L210 was not covered by tests
Loading