Schelling Segregation Model

Background

The Schelling (1971) segregation model is a classic of agent-based modeling, demonstrating how agents following simple rules lead to the emergence of qualitatively different macro-level outcomes. Agents are randomly placed on a grid. There are two types of agents, one constituting the majority and the other the minority. All agents want a certain number (generally, 3) of their 8 surrounding neighbors to be of the same type in order for them to be happy. Unhappy agents will move to a random available grid space. While individual agents do not have a preference for a segregated outcome (e.g. they would be happy with 3 similar neighbors and 5 different ones), the aggregate outcome is nevertheless heavily segregated.

Implementation

This is a demonstration of running a Mesa model in an IPython Notebook. The actual model and agent code are implemented in Schelling.py, in the same directory as this notebook. Below, we will import the model class, instantiate it, run it, and plot the time series of the number of happy agents.


In [1]:
import matplotlib.pyplot as plt
%matplotlib inline

from Schelling import model


---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-6a7c2ae7968c> in <module>()
----> 1 import matplotlib.pyplot as plt
      2 get_ipython().magic(u'matplotlib inline')
      3 
      4 from Schelling import SchellingModel

ImportError: No module named matplotlib.pyplot

Now we instantiate a model instance: a 10x10 grid, with an 80% change of an agent being placed in each cell, approximately 20% of agents set as minorities, and agents wanting at least 3 similar neighbors.


In [2]:
model = SchellingModel(10, 10, 0.8, 0.2, 3)

We want to run the model until all the agents are happy with where they are. However, there's no guarentee that a given model instantiation will ever settle down. So let's run it for either 100 steps or until it stops on its own, whichever comes first:


In [3]:
while model.running and model.schedule.steps < 100:
    model.step()
print(model.schedule.steps) # Show how many steps have actually run


23

The model has a DataCollector object, which checks and stores how many agents are happy at the end of each step. It can also generate a pandas DataFrame of the data it has collected:


In [4]:
model_out = model.datacollector.get_model_vars_dataframe()

In [5]:
model_out.head()


Out[5]:
happy
0 56
1 62
2 68
3 68
4 69

Finally, we can plot the 'happy' series:


In [6]:
model_out.happy.plot()


Out[6]:
<matplotlib.axes._subplots.AxesSubplot at 0x1097aba90>

For testing purposes, here is a table giving each agent's x and y values at each step.


In [7]:
x_positions = model.datacollector.get_agent_vars_dataframe()

In [8]:
x_positions.head()


Out[8]:
x y
Step AgentID
0 (0, 0) 7 1
(0, 1) 4 3
(0, 2) 9 0
(0, 3) 2 6
(0, 4) 1 2

Effect of Homophily on segregation

Now, we can do a parameter sweep to see how segregation changes with homophily.

First, we create a function which takes a model instance and returns what fraction of agents are segregated -- that is, have no neighbors of the opposite type.


In [9]:
from mesa.batchrunner import BatchRunner

In [10]:
def get_segregation(model):
    '''
    Find the % of agents that only have neighbors of their same type.
    '''
    segregated_agents = 0
    for agent in model.schedule.agents:
        segregated = True
        for neighbor in model.grid.neighbor_iter(agent.pos):
            if neighbor.type != agent.type:
                segregated = False
                break
        if segregated:
            segregated_agents += 1
    return segregated_agents / model.schedule.get_agent_count()

Now, we set up the batch run, with a dictionary of fixed and changing parameters. Let's hold everything fixed except for Homophily.


In [11]:
parameters = {"height": 10, "width": 10, "density": 0.8, "minority_pc": 0.2, 
              "homophily": range(1,9)}

In [12]:
model_reporters = {"Segregated_Agents": get_segregation}

In [13]:
param_sweep = BatchRunner(SchellingModel, parameters, iterations=10, 
                          max_steps=200,
                          model_reporters=model_reporters)

In [14]:
param_sweep.run_all()

In [15]:
df = param_sweep.get_model_vars_dataframe()

In [16]:
plt.scatter(df.homophily, df.Segregated_Agents)
plt.grid(True)