Skip to content

Commit fe89a10

Browse files
committed
feat: Implement experimental DataCollector API
1 parent 449d230 commit fe89a10

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

mesa/experimental/observer.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from collections import defaultdict
2+
3+
import pandas as pd
4+
5+
6+
class DataCollector:
7+
"""
8+
Example: a model consisting of a hybrid of Boltzmann wealth model and
9+
Epstein civil violence.
10+
```
11+
def get_citizen():
12+
return model.get_agents_of_type(Citizen)
13+
14+
collectors = {
15+
model: {
16+
"n_quiescent": lambda model: len(
17+
model.agents.select(
18+
agent_type=Citizen,
19+
filter_func=lambda a: a.condition == "Quiescent"
20+
)
21+
),
22+
"gini": lambda model: calculate_gini(model.agents.get("wealth"))
23+
},
24+
get_citizen: {"condition": condition},
25+
# This is a string, because model.agents may refer to a different
26+
# object, over time.
27+
"agents": {"wealth": "wealth"}
28+
}
29+
# Then finally
30+
model.datacollector = DataCollector(model, collectors=collectors).collect()
31+
```
32+
"""
33+
34+
def __init__(self, model, collectors=None) -> "DataCollector":
35+
self.model = model
36+
self.collectors = collectors
37+
self.data_collection = defaultdict(list)
38+
return self
39+
40+
def collect(self) -> "DataCollector":
41+
for group, group_collector in self.collectors.items():
42+
group_object = group
43+
if group == "agents":
44+
group_object = self.model.agents
45+
elif callable(group):
46+
group_object = group()
47+
data = {
48+
name: self._collect_element(group_object, collector)
49+
for name, collector in group_collector.items()
50+
}
51+
self.data_collection[group].append(data)
52+
return self
53+
54+
def _collect_element(self, group, collector):
55+
def _get_or_apply(obj, value):
56+
# get
57+
if isinstance(value, str):
58+
return getattr(obj, value)
59+
# apply
60+
return value(obj)
61+
62+
if group is self.model:
63+
return {
64+
name: _get_or_apply(group, value) for name, value in collector.items()
65+
}
66+
return {
67+
name: [_get_or_apply(e, value) for e in group]
68+
for name, value in collector.items()
69+
}
70+
71+
def to_df(self, group=None):
72+
if group is None:
73+
return {
74+
group: pd.DataFrame(data_list)
75+
for group, data_list in self.data_collection.items()
76+
}
77+
return pd.DataFrame(self.data_collection[group])

0 commit comments

Comments
 (0)