Fundamentals 7 min read

How Tiny Preferences Spark Massive Segregation: Exploring Schelling’s Model with Python

This article explains Schelling’s segregation model, shows how modest individual color preferences can lead to large-scale racial clustering, presents the underlying mathematics, and provides a complete Python simulation that visualizes the emergence of segregated patterns over time.

Model Perspective
Model Perspective
Model Perspective
How Tiny Preferences Spark Massive Segregation: Exploring Schelling’s Model with Python
In many countries, certain neighborhoods become concentrated with a particular race or culture, a phenomenon known as racial or cultural segregation, and the article asks whether policy or individual choices drive this.

Grid Experiment

Imagine a large checkerboard where each cell can host a red or blue agent. Agents are not strongly hostile to different colors, but they have a slight preference for having a portion of their neighbors share their own color.

Schelling proposed that each agent has a satisfaction threshold indicating how many same‑color neighbors it desires; if the threshold isn’t met, the agent moves to a new location.

The Logic Behind the Math

Let N be the total number of neighbors for an agent and S the number of same‑color neighbors. If the satisfaction condition is not satisfied, the agent will relocate.

Surprising Findings of the Model

When Schelling simulated this model on a computer, he discovered that even with low satisfaction thresholds (e.g., agents only require a small fraction of neighbors to match), the system quickly evolves into clearly segregated regions.

This shows that even minimal individual preferences can accumulate to produce strong clustering effects, leading to pronounced segregation.

Python Simulation

import numpy as np
import matplotlib.pyplot as plt

# Parameters for the model
size = 50  # grid size
red_fraction = 0.4
blue_fraction = 0.4

red_count = int(red_fraction * size**2)
blue_count = int(blue_fraction * size**2)
empty_count = size**2 - red_count - blue_count

# Create the initial grid with agents
agents = np.array([0] * empty_count + [1] * red_count + [2] * blue_count)
np.random.shuffle(agents)
grid = agents.reshape((size, size))

def find_unsatisfied(x, y, grid):
    value = grid[x, y]
    if value == 0:
        return False
    total = 0
    same_count = 0
    for dx in [-1, 0, 1]:
        for dy in [-1, 0, 1]:
            if dx == dy == 0:
                continue
            if 0 <= x + dx < size and 0 <= y + dy < size:
                total += 1
                if grid[x + dx, y + dy] == value:
                    same_count += 1
    return same_count / total < threshold

def get_unsatisfied_agents(grid):
    unsatisfied = []
    for x in range(size):
        for y in range(size):
            if find_unsatisfied(x, y, grid):
                unsatisfied.append((x, y))
    return unsatisfied

def relocate_unsatisfied(grid):
    unsatisfied_agents = get_unsatisfied_agents(grid)
    empty_cells = list(zip(*np.where(grid == 0)))
    np.random.shuffle(unsatisfied_agents)
    np.random.shuffle(empty_cells)
    num_moved = min(len(unsatisfied_agents), len(empty_cells))
    for i in range(num_moved):
        x, y = unsatisfied_agents[i]
        new_x, new_y = empty_cells[i]
        grid[new_x, new_y] = grid[x, y]
        grid[x, y] = 0

def simulate_segregation(grid, max_steps=500):
    snapshots = [grid.copy()]
    for step in range(max_steps):
        prev_grid = grid.copy()
        relocate_unsatisfied(grid)
        if np.array_equal(prev_grid, grid):
            break
        if step in [0, 1, 2, 4, 9]:
            snapshots.append(grid.copy())
    snapshots.append(grid.copy())
    return snapshots

snapshots = simulate_segregation(grid)

fig, axes = plt.subplots(2, 3, figsize=(15, 10))

titles = ["Initial State", "After 1 Move", "After 2 Moves", "After 3 Moves", "After 5 Moves", "Final State"]
for ax, snapshot, title in zip(axes.ravel(), snapshots, titles):
    ax.imshow(snapshot, cmap="bwr", vmin=0, vmax=2)
    ax.set_title(title)
    ax.axis('off')

plt.tight_layout()
plt.show()

The images show agents gradually clustering by color (representing race) over time, even when the satisfaction threshold is low.

Insights and Reflections

Schelling’s model offers a new perspective for understanding racial and cultural segregation, highlighting how individual preferences interact with social structure to produce macro‑level patterns.

It reminds us that seemingly harmless or mild personal choices can have profound effects in a larger societal context, influencing where we live and whom we associate with.

The model not only provides a research tool for social scientists but also encourages ordinary people to reflect on their own behavior, helping us build a more diverse and harmonious society.

Pythonagent-based simulationSchelling modelsegregationsocial dynamics
Model Perspective
Written by

Model Perspective

Insights, knowledge, and enjoyment from a mathematical modeling researcher and educator. Hosted by Haihua Wang, a modeling instructor and author of "Clever Use of Chat for Mathematical Modeling", "Modeling: The Mathematics of Thinking", "Mathematical Modeling Practice: A Hands‑On Guide to Competitions", and co‑author of "Mathematical Modeling: Teaching Design and Cases".

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.