Note

You can download this demonstration as a Jupyter Notebook here

Wealth transfer

This notebook presents a tutorial for beginners on how to create a simple agent-based model with the agentpy package. It demonstrates how to create a basic model with a custom agent type, run a simulation, record data, and visualize results.

[1]:
# Model design
import agentpy as ap
import numpy as np

# Visualization
import seaborn as sns

About the model

The model explores the distribution of wealth under a trading population of agents. Each agent starts with one unit of wealth. During each time-step, each agents with positive wealth randomly selects a trading partner and gives them one unit of their wealth. We will see that this random interaction will create an inequality of wealth that follows a Boltzmann distribution. The original version of this model been written in MESA and can be found here.

Model definition

We start by defining a new type of Agent with the following methods:

  • setup() is called automatically when a new agent is created and initializes a variable wealth.

  • wealth_transfer() describes the agent’s behavior at every time-step and will be called by the model.

[2]:
class WealthAgent(ap.Agent):

    """ An agent with wealth """

    def setup(self):

        self.wealth = 1

    def wealth_transfer(self):

        if self.wealth > 0:

            partner = self.model.agents.random()
            partner.wealth += 1
            self.wealth -= 1

Next, we define a method to calculate the Gini Coefficient, which will measure the inequality among our agents.

[3]:
def gini(x):

    """ Calculate Gini Coefficient """
    # By Warren Weckesser https://stackoverflow.com/a/39513799

    x = np.array(x)
    mad = np.abs(np.subtract.outer(x, x)).mean()  # Mean absolute difference
    rmad = mad / np.mean(x)  # Relative mean absolute difference
    return 0.5 * rmad

Finally, we define our `Model <https://agentpy.readthedocs.io/en/stable/reference_models.html>`__ with the following methods:

  • setup defines how many agents should be created at the beginning of the simulation.

  • step calls all agents during each time-step to perform their wealth_transfer method.

  • update calculates and record the current Gini coefficient after each time-step.

  • end, which is called at the end of the simulation, we record the wealth of each agent.

[4]:
class WealthModel(ap.Model):

    """ A simple model of random wealth transfers """

    def setup(self):

        self.agents = ap.AgentList(self, self.p.agents, WealthAgent)

    def step(self):

        self.agents.wealth_transfer()

    def update(self):

        self.record('Gini Coefficient', gini(self.agents.wealth))

    def end(self):

        self.agents.record('wealth')

Simulation run

To prepare, we define parameter dictionary with a random seed, the number of agents, and the number of time-steps.

[5]:
parameters = {
    'agents': 100,
    'steps': 100,
    'seed': 42,
}

To perform a simulation, we initialize our model with a given set of parameters and call `Model.run() <https://agentpy.readthedocs.io/en/stable/reference_models.html>`__.

[6]:
model = WealthModel(parameters)
results = model.run()
Completed: 100 steps
Run time: 0:00:00.124199
Simulation finished

Output analysis

The simulation returns a `DataDict <https://agentpy.readthedocs.io/en/stable/reference_output.html>`__ with our recorded variables.

[7]:
results
[7]:
DataDict {
'info': Dictionary with 9 keys
'parameters':
    'constants': Dictionary with 3 keys
'variables':
    'WealthModel': DataFrame with 1 variable and 101 rows
    'WealthAgent': DataFrame with 1 variable and 100 rows
}

The output’s info provides general information about the simulation.

[8]:
results.info
[8]:
{'model_type': 'WealthModel',
 'time_stamp': '2021-05-28 09:33:50',
 'agentpy_version': '0.0.8.dev0',
 'python_version': '3.8.5',
 'experiment': False,
 'completed': True,
 'created_objects': 100,
 'completed_steps': 100,
 'run_time': '0:00:00.124199'}

To explore the evolution of inequality, we look at the recorded `DataFrame <https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html>`__ of the model’s variables.

[9]:
results.variables.WealthModel.head()
[9]:
Gini Coefficient
t
0 0.0000
1 0.5370
2 0.5690
3 0.5614
4 0.5794

To visualize this data, we can use `DataFrame.plot <https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.html>`__.

[10]:
data = results.variables.WealthModel
ax = data.plot()
_images/agentpy_wealth_transfer_24_0.png

To look at the distribution at the end of the simulation, we visualize the recorded agent variables with seaborn.

[11]:
sns.histplot(data=results.variables.WealthAgent, binwidth=1);
_images/agentpy_wealth_transfer_26_0.png

The result resembles a Boltzmann distribution.