forked from SonjaKT/housing_model
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simulate.py
125 lines (100 loc) · 4.76 KB
/
simulate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""Simulation of the house_match model using the Renter
and House classes defined in the classes module."""
import scipy.stats
import numpy.random
import classes
import house_match
####################
# GENERATE OBJECTS #
####################
def create_renters(n_renters, scaling_factor = 1000):
"""Renters are drawn from a chi**2 distribution with 4 DF,
and are right-shifted by the scaling_factor."""
renter_list = []
rvs = scipy.stats.chi2.rvs(df = 4, size=n_renters)
for index, r in enumerate(rvs):
renter_list.append(classes.Renter(r*scaling_factor, index))
return renter_list
def create_houses(n_houses, mean_quality = 100, sd = 10):
"""House quality is drawn from the normal distribution.
N.B. that the quality of houses is used strictly for ranking,
so the actual distribution of house quality is not important so long
as their scores can be meaningfully ranked."""
house_list = []
samples = numpy.random.normal(mean_quality, sd, n_houses)
for index, sample in enumerate(samples):
house_list.append(classes.House(sample))
return house_list
#####################
# SINGLE SIMULATION #
#####################
def simulation(n_renters, n_houses, higher_quality_houses = 0, lower_quality_houses = 0):
"""Run house_match with initial n_renters and n_houses. Arguments higher_quality_houses
and lower_quality_houses specify houses to be added to the initial pool, from quality distributions
shifted to the right or left of the initial housing distribution, respectively.
Adding x higher-quality homes is assumed to attract x/2 renters with high WTP, while adding
y lower-quality homes is not assumed to affect the renter pool."""
renters = create_renters(n_renters)
houses = create_houses(n_houses)
if higher_quality_houses:
houses.extend(create_houses(higher_quality_houses, 120, 10))
#assume that adding high-quality housing attracts more renters to the market;
#currently we assume that for 2 added houses we add 1 renter, but should determine empirically!
renters.extend(create_renters(higher_quality_houses / 2, 1500))
if lower_quality_houses:
houses.extend(create_houses(lower_quality_houses, 80, 10))
renters.extend(create_renters(lower_quality_houses / 2, 500))
houses = house_match.sort_houses_by_niceness(houses)
house_match.stable_match(renters, houses)
#do stats
min_price = float("inf")
index = 0 #need to keep track of how many houses were actually bid on, for calculating the median
for house in houses:
if house.current_price and house.current_price < min_price:
min_price = house.current_price
index += 1
elif not house.current_price:
break #this house was never bid on
max_price = renters[houses[0].rented_by].paying
median_price = renters[houses[index/2].rented_by].paying
return max_price, median_price, min_price
#######################
# REPEATED SIMULATION #
#######################
def simulate_addgeneral(n_trials):
"""Return the mean change in maximum, median, and minimum housing cost
when additional housing is added to a restricted market."""
max_diff, med_diff, min_diff = [], [], []
for dummy in range(n_trials):
max_restricted, med_restricted, min_restricted = simulation(50, 30)
max_unrestricted, med_unrestricted, min_unrestricted = simulation(40, 30)
max_diff.append(max_restricted - max_unrestricted)
med_diff.append(med_restricted - med_unrestricted)
min_diff.append(min_restricted - min_unrestricted)
return sum(max_diff)/n_trials, sum(med_diff)/n_trials, sum(min_diff)/n_trials
def simulate_addcheaphousing(n_trials):
"""Return the mean change in maximum, median, and minimum housing cost
when low-end housing is added to a restricted market."""
max_diff, med_diff, min_diff = [], [], []
for dummy in range(n_trials):
max_nolo, med_nolo, min_nolo = simulation(50, 30)
max_lo, med_lo, min_lo = simulation(50, 30, lower_quality_houses = 10) #more renters than houses; add low-end housing
max_diff.append(max_nolo - max_lo)
med_diff.append(med_nolo - med_lo)
min_diff.append(min_nolo - min_lo)
return sum(max_diff)/n_trials, sum(med_diff)/n_trials, sum(min_diff)/n_trials
def simulate_addexpensivehousing(n_trials):
"""Return the mean change in maximum, median, and minimum housing cost when
high-end housing is added to a restricted market"""
max_diff, med_diff, min_diff = [], [], []
for dummy in range(n_trials):
max_nohi, med_nohi, min_nohi = simulation(50, 30)
max_hi, med_hi, min_hi = simulation(50, 30, higher_quality_houses = 10) #more renters than houses; add high-end housing
max_diff.append(max_nohi - max_hi)
med_diff.append(med_nohi - med_hi)
min_diff.append(min_nohi - min_hi)
return sum(max_diff)/n_trials, sum(med_diff)/n_trials, sum(min_diff)/n_trials
if __name__ == "__main__":
print simulate_addgeneral(100)
print simulate_addcheaphousing(100)
print simulate_addexpensivehousing(100)