Created
May 25, 2019 06:10
-
-
Save mohcinemadkour/8bb33c9c1f693c314c0c0cf82999e875 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Author : Mohcine Madkour | |
Email : mohcine.madkour@gmail.com | |
Date : 10-Sep-2014 | |
Description: Simulations of Schelling's seggregation model | |
http://www.binpress.com/tutorial/introduction-to-agentbased-models-an-implementation-of-schelling-model-in-python/144 | |
''' | |
import matplotlib.pyplot as plt | |
import itertools | |
import random | |
import copy | |
class Schelling: | |
def __init__(self, width, height, empty_ratio, similarity_threshold, n_iterations, 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 | |
def populate(self): | |
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:] | |
houses_by_race = [self.remaining_houses[i::self.races] for i in range(self.races)] | |
for i in range(self.races): | |
#create agents for each race | |
self.agents = dict( | |
self.agents.items() + | |
dict(zip(houses_by_race[i], [i+1]*len(houses_by_race[i]))).items() | |
) | |
def is_unsatisfied(self, x, y): | |
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 update(self): | |
for i in range(self.n_iterations): | |
self.old_agents = copy.deepcopy(self.agents) | |
n_changes = 0 | |
for agent in self.old_agents: | |
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 | |
#print 'Iteration: %d , Number of changes: %d' %(i+1, n_changes) | |
if n_changes == 0: | |
break | |
def move_to_empty(self, x, y): | |
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 plot(self, title, file_name): | |
fig, ax = plt.subplots() | |
#If you want to run the simulation with more than 7 colors, you should set agent_colors accordingly | |
agent_colors = {1:'b', 2:'r', 3:'g', 4:'c', 5:'m', 6:'y', 7:'k'} | |
for agent in self.agents: | |
ax.scatter(agent[0]+0.5, agent[1]+0.5, color=agent_colors[self.agents[agent]]) | |
ax.set_title(title, fontsize=10, fontweight='bold') | |
ax.set_xlim([0, self.width]) | |
ax.set_ylim([0, self.height]) | |
ax.set_xticks([]) | |
ax.set_yticks([]) | |
plt.savefig(file_name) | |
def calculate_similarity(self): | |
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) | |
def main(): | |
##First Simulation | |
schelling_1 = Schelling(50, 50, 0.3, 0.3, 500, 2) | |
schelling_1.populate() | |
schelling_2 = Schelling(50, 50, 0.3, 0.5, 500, 2) | |
schelling_2.populate() | |
schelling_3 = Schelling(50, 50, 0.3, 0.8, 500, 2) | |
schelling_3.populate() | |
schelling_1.plot('Schelling Model with 2 colors: Initial State', 'schelling_2_initial.png') | |
schelling_1.update() | |
schelling_2.update() | |
schelling_3.update() | |
schelling_1.plot('Schelling Model with 2 colors: Final State with Happiness Threshold 30%', 'schelling_2_30_final.png') | |
schelling_2.plot('Schelling Model with 2 colors: Final State with Happiness Threshold 50%', 'schelling_2_50_final.png') | |
schelling_3.plot('Schelling Model with 2 colors: Final State with Happiness Threshold 80%', 'schelling_2_80_final.png') | |
##Second Simulation Measuring Seggregation | |
similarity_threshold_ratio = {} | |
for i in [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7]: | |
schelling = Schelling(50, 50, 0.3, i, 500, 2) | |
schelling.populate() | |
schelling.update() | |
similarity_threshold_ratio[i] = schelling.calculate_similarity() | |
fig, ax = plt.subplots() | |
plt.plot(similarity_threshold_ratio.keys(), similarity_threshold_ratio.values(), 'ro') | |
ax.set_title('Similarity Threshold vs. Mean Similarity Ratio', fontsize=15, fontweight='bold') | |
ax.set_xlim([0, 1]) | |
ax.set_ylim([0, 1.1]) | |
ax.set_xlabel("Similarity Threshold") | |
ax.set_ylabel("Mean Similarity Ratio") | |
plt.savefig('schelling_segregation.png') | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment