-
Notifications
You must be signed in to change notification settings - Fork 0
/
sim_app.py
192 lines (159 loc) · 9.36 KB
/
sim_app.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import streamlit as st
import altair as alt
import pandas as pd
import src.utils as utils
from src.interact import Interact
st.set_page_config(layout = 'wide', page_title = 'Neighborhood Composting Simulation', page_icon = '🚮')
st.header('How quickly can a neighborhood adopt the practice of composting their food scraps?')
st.write("""People like to pat themselves on the back when they start
doing something eco-friendly like composting, but how impactful
is an isolated individual partaking in a positive behavior vs. a social individual spreading
the positive behavior through their network? Several neighbors are already composting at the beginning of the simulation.
Tinker with the parameters representing neighbors' social compatibility, their willingness
to encourage others to compost, and their willingness to try composting
if encouraged, to see how close the neighborhood gets to completely converting
to composting within a 30-day period.
What combinations of conditions are necessary for
composting to really take off in the neighborhood?""")
st.subheader('About our assumptions')
st.write("""In this simplified model of social interaction, neighbors randomly encounter other neighbors each day, and decide to converse
if their personalities are compatible.""")
st.write("""For some of the modeling below we use a beta distribution. Our choice is due to its
convenient shape, being bounded between 0 and 1, and tapering off smoothly at the boundaries.""")
model_plot = None
#initialize the model dataframe to an empty one if not already in the
#session state.
if 'model_data_frame' not in st.session_state:
st.session_state["model_data_frame"] = pd.DataFrame()
st.subheader('Choose Your Parameters')
my_cols_tuple = (7, 1, 7, 1, 7)
col1, col2, col3, col4, col5 = st.columns(my_cols_tuple)
with col1:
# ************************************************************************
# st.subheader('Random Seed')
utils.write_custom_subheader('Random Seed')
seed_input = st.text_input("""Random seed (optional): pick one or leave it blank.""",
key = 'seed_input')
# Check on random seed. Accept only numbers.
seed_input = utils.check_random_seed_content(seed_input)
optional_seed = seed_input if seed_input!= '' else None
st.write(f"(Random seed = {optional_seed})")
# TODO: be able to select number of days
with col3:
# ************************************************************************
# st.subheader('Neighborhood size')
utils.write_custom_subheader('Neighborhood size')
neighborhood_size = st.slider('How many people live in the neighborhood?',
min_value = 10,
max_value = 200,
step = 10,
value = 70,
key = 'nbhd')
with col5:
# ************************************************************************
# ************************************************************************
# st.subheader('Fraction of initial composters')
utils.write_custom_subheader('Fraction of initial composters')
frac_composters = st.slider('What is the % of composters at the beginning of the simulation?',
min_value = 1,
max_value = 50,
value = 10,
format="%g%%",
key = 'n_composters')
num_composters = int(frac_composters*neighborhood_size/100)
my_cols_tuple = (7, 1, 7, 1, 7, 1, 7)
col1, col2, col3, col4, col5, col6, col7 = st.columns(my_cols_tuple)
with col1:
# ************************************************************************
# st.subheader('Spread of Personalities')
utils.write_custom_subheader('Spread of Personalities')
personality_xmax = 10
personality_spread = st.slider("""We create personality scores from 1 to 10 assuming
a symmetric beta distribution and multiplying the result by 10.
Choose the spread of the distribution. You can go all the way
from narrow (1) to flat (10).""",
min_value = 1,
max_value = personality_xmax,
value = 5)
utils.visualize_parameter_distr(personality_spread, personality_xmax, 'Personality Score', 'simmetric_beta')
with col3:
# ************************************************************************
# st.subheader('Sociability Margins')
utils.write_custom_subheader('Sociability Margins')
sociability_spread = st.slider("""We add to each personality a 'margin'.
Two people will converse only if their personality score +/- margin overlap.
We sample margins from a normal distribution.
Choose its standard deviation.""",
value = float(1),
min_value = float(0.5),
max_value = float(1.5),
step = 0.1)
utils.visualize_parameter_distr(sociability_spread, 4, 'Sociability Margin', 'normal')
with col5:
# ************************************************************************
# st.subheader('Encouragement skew')
utils.write_custom_subheader('Encouragement Skew')
# depending on the value 1 through 10, beta a = the value and b = 10-the value
encouragement_xmax = 10
encouragement_skew = st.slider("""If someone already composts, how likely are they to encourage
other people to do the same?
We model this probability via a beta distribution.
Choose its skewness, where higher values mean more
likely to encourage others.""",
value = 5,
min_value = 1,
max_value = encouragement_xmax)
utils.visualize_parameter_distr(encouragement_skew, encouragement_xmax, 'Probability of Encouragement', 'beta')
# ************************************************************************
with col7:
# ************************************************************************
# st.subheader('Openness Skew')
utils.write_custom_subheader('Openness Skew')
openness_xmax = 10
openness_skew = st.slider("""If someone doesn't compost, how likely are they to be convinced by a
neighbor to start? We model this probability via a beta distribution.
Choose its skewness, where higher values mean more likely to be convinced.""",
value = 3,
min_value = 1,
max_value = openness_xmax)
utils.visualize_parameter_distr(openness_skew, openness_xmax, 'Probability of Being Convinced', 'beta')
model = Interact(n_neighbors = neighborhood_size,
n_already_composting = num_composters,
personality_spread = personality_spread,
personality_xmax = personality_xmax,
sociability_spread = sociability_spread,
encouragement_skew = encouragement_skew,
encouragement_xmax = encouragement_xmax,
openness_skew = openness_skew,
openness_xmax = openness_xmax,
days = 30,
seed = optional_seed)
# run themodel
for thick_i in range(model.ticks):
model.step()
# collect the data
model_data = model.datacollector.get_model_vars_dataframe().reset_index()
# show day number rather than tick number
model_data['day'] = model_data['index'].divide(model_data['neighborhood_size'])
# ************************************************************************
st.subheader("Let's see the results!")
my_cols_tuple = (1, 1)
col1, col2 = st.columns(my_cols_tuple)
with col1:
utils.plot_composters_over_time(model_data, neighborhood_size)
with col2:
# st.button('Collect data', on_click = utils.retain_model_data(model_data))
st.download_button('Download collected data', data = model_data.to_csv())
st.write(f"Data frame dimensions = {model_data.shape}")
st.write(f"")
# st.button('I want to reset the data before downloading them!', on_click = utils.allow_reset())
# if st.session_state['allow_data_reset']:
# st.button('Reset data collection', on_click = utils.remove_model_data())
# st.write(f"Data frame dimensions = {st.session_state['model_data_frame'].shape}")
st.write("""Note: the exported data is indexed by neighbors per day,
meaning if there are 30 people in the neighborhood,
there will be 30*30 = 900 rows in the data set per simulation.
This is because each neighbor gets a chance to interact with another
neighbor in a day before the day resets. You can easily modify
this dataset to show just the total number_of_composters at the end
of each day.""")