If you don't know what is Schelling's model of segregation, you can read it here.
I have written the following code to run the Schelling's model of segregation simulation.
import matplotlib.pyplot as plt
import matplotlib.animation
import itertools
import random
import copy
class Schelling:
def __init__(self, width, height, empty_ratio, similarity_threshold, n_iterations, ratio,dataframe,county_num, races = 2):
self.width = width
self.height = height
self.races = races
self.empty_ratio = empty_ratio
self.similarity_threshold = similarity_threshold
self.n_iterations = n_iterations
self.ratio=ratio
self.county_num=county_num
self.dataframe=dataframe
def populate(self):
"""
Populate the square randomly with the two races.
"""
self.empty_houses = []
self.agents = {}
self.all_houses = list(itertools.product(range(self.width),range(self.height)))
random.shuffle(self.all_houses)
self.n_empty = int(self.empty_ratio * len(self.all_houses))
self.empty_houses = self.all_houses[:self.n_empty]
self.remaining_houses = self.all_houses[self.n_empty:]
divider=int(round(len(self.remaining_houses)*self.ratio)) #divides the list in two different parts according to the ratio between white and the minority given in two different states
houses_white=self.remaining_houses[:divider]
houses_black=self.remaining_houses[divider:]
self.agents.update(dict(zip(houses_white,[1]*divider)))
self.agents.update(dict(zip(houses_black,[2]*int(len(self.remaining_houses)-divider))))
return self.remaining_houses #for debugging purposes
def is_unsatisfied(self, x, y):
"""
Checking if an agent is unsatisfied or satisified at its current
position.
"""
race = self.agents[(x,y)]
count_similar = 0
count_different = 0
if x > 0 and y > 0 and (x-1, y-1) not in self.empty_houses:
if self.agents[(x-1, y-1)] == race:
count_similar += 1
else:
count_different += 1
if y > 0 and (x,y-1) not in self.empty_houses:
if self.agents[(x,y-1)] == race:
count_similar += 1
else:
count_different += 1
if x < (self.width-1) and y > 0 and (x+1,y-1) not in self.empty_houses:
if self.agents[(x+1,y-1)] == race:
count_similar += 1
else:
count_different += 1
if x > 0 and (x-1,y) not in self.empty_houses:
if self.agents[(x-1,y)] == race:
count_similar += 1
else:
count_different += 1
if x < (self.width-1) and (x+1,y) not in self.empty_houses:
if self.agents[(x+1,y)] == race:
count_similar += 1
else:
count_different += 1
if x > 0 and y < (self.height-1) and (x-1,y+1) not in self.empty_houses:
if self.agents[(x-1,y+1)] == race:
count_similar += 1
else:
count_different += 1
if x > 0 and y < (self.height-1) and (x,y+1) not in self.empty_houses:
if self.agents[(x,y+1)] == race:
count_similar += 1
else:
count_different += 1
if x < (self.width-1) and y < (self.height-1) and (x+1,y+1) not in self.empty_houses:
if self.agents[(x+1,y+1)] == race:
count_similar += 1
else:
count_different += 1
if (count_similar+count_different) == 0:
return False
else:
return float(count_similar)/(count_similar+count_different) < self.similarity_threshold
def plot(self):
"""
plot the square with below function to show where the agents at
"""
fig, ax = plt.subplots()
agent_colors = {1:'b', 2:'r'}
for agent in self.agents:
ax.scatter(agent[0]+0.5, agent[1]+0.5, color=agent_colors[self.agents[agent]])
ax.set_title("Need to change it later", fontsize=10, fontweight='bold')
ax.set_xlim([0, self.width])
ax.set_ylim([0, self.height])
ax.set_xticks([])
ax.set_yticks([])
def update(self):
"""
Update the square on the basis of similarity threshhold. This is the
function which actually runs the simulation.
"""
fig, ax = plt.subplots()
agent_colors = {1:'b', 2:'r'}
ax.set_xlim([0, self.width])
ax.set_ylim([0, self.height])
ax.set_xticks([])
ax.set_yticks([])
def update(i):
self.old_agents = copy.deepcopy(self.agents)
n_changes = 0
for agent in self.old_agents:
ax.scatter(agent[0]+0.5, agent[1]+0.5, color=agent_colors[self.agents[agent]])
ax.set_title(str(n_changes), fontsize=10, fontweight='bold')
if self.is_unsatisfied(agent[0], agent[1]):
agent_race = self.agents[agent]
empty_house = random.choice(self.empty_houses)
self.agents[empty_house] = agent_race
del self.agents[agent]
self.empty_houses.remove(empty_house)
self.empty_houses.append(agent)
n_changes += 1
if n_changes==0:
return
ani = matplotlib.animation.FuncAnimation(fig, update, frames= self.n_iterations,repeat=False)
plt.show()
def move_to_empty(self, x, y):
"""
Moves an unsatisfied agent to an empty house.
"""
race = self.agents[(x,y)]
empty_house = random.choice(self.empty_houses)
self.updated_agents[empty_house] = race
del self.updated_agents[(x, y)]
self.empty_houses.remove(empty_house)
self.empty_houses.append((x, y))
def calculate_similarity(self):
"""
Calculates the mean similarity after the update.
"""
similarity = []
for agent in self.agents:
count_similar = 0
count_different = 0
x = agent[0]
y = agent[1]
race = self.agents[(x,y)]
if x > 0 and y > 0 and (x-1, y-1) not in self.empty_houses:
if self.agents[(x-1, y-1)] == race:
count_similar += 1
else:
count_different += 1
if y > 0 and (x,y-1) not in self.empty_houses:
if self.agents[(x,y-1)] == race:
count_similar += 1
else:
count_different += 1
if x < (self.width-1) and y > 0 and (x+1,y-1) not in self.empty_houses:
if self.agents[(x+1,y-1)] == race:
count_similar += 1
else:
count_different += 1
if x > 0 and (x-1,y) not in self.empty_houses:
if self.agents[(x-1,y)] == race:
count_similar += 1
else:
count_different += 1
if x < (self.width-1) and (x+1,y) not in self.empty_houses:
if self.agents[(x+1,y)] == race:
count_similar += 1
else:
count_different += 1
if x > 0 and y < (self.height-1) and (x-1,y+1) not in self.empty_houses:
if self.agents[(x-1,y+1)] == race:
count_similar += 1
else:
count_different += 1
if x > 0 and y < (self.height-1) and (x,y+1) not in self.empty_houses:
if self.agents[(x,y+1)] == race:
count_similar += 1
else:
count_different += 1
if x < (self.width-1) and y < (self.height-1) and (x+1,y+1) not in self.empty_houses:
if self.agents[(x+1,y+1)] == race:
count_similar += 1
else:
count_different += 1
try:
similarity.append(float(count_similar)/(count_similar+count_different))
except:
similarity.append(1)
return sum(similarity)/len(similarity)
schelling= Schelling(20, 20, 0.3, 0.6, 1000, 0.5,90,2)
schelling.populate()
schelling.update()
I am running the simulation here on matplotlib grid or plot. Like shown below
But the thing I want to do is to run the simulation on an American State or Some countries map. I have tried the mesa-geo module but it doesn't work well. I just want to change the square grid with the map.
Is it possible to achieve it with matplotlib library by changing the shape of the current plot to the shape of a state (say Arizona) or country (say Germany). Or is there any other module present in python to do it. I really want to do it with python as i need to do further calculations which are easier to do in python. Also I would like to get a solution which is easier to integrate with the current methods I have. Also someone can share there experience if they ever did a schellings simulation on geographical area or map.