Tuesday, October 4, 2016

Visualize the Behavior Space in Netlogo: 1.1

Last month, I posted code for visualizing the behavior-space using Python to process data from NetLogo. See that post for the netlogo template you will want to use. The py file I posted before was not the easiest to interpret or to work with. It required that a programmer plan ahead, noting the position of runs with particular values for exogenously determined parameters. I rewrote the code so that the data processing is more efficient and easier to work with. The instance I am posting works with a behavior space where only two variables have been set exogenously across runs.  However, if it was necessary, a third or fourth variable could be altered in the same set of runs so long as that third and/or fourth variable was assigned a particular value as runs are collected in lines 196-209. Also note that I have reduced the variety of heatmaps presented since only one is required to convey the process. I loop the image generator code to create a series of frames.

I may eventually post such an extension of the code if I generate output that requires this in the future. For now, I wanted to leave you with something easier to work with. Remember, you should end up with something that looks like this (from earlier post):



See the updated code below:

  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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import pandas as pd
from matplotlib.backends.backend_pdf import PdfPages

# of csv files generated by netlogo = # of runs
files = 1280

# length of each run of the model
ticks = 50

# these are the values of interest
# in this case we adjusted water metabolism and sugar metabolism
axis_values = np.array([.45, .5, .55, .6, .65, .7, .75, .8])

# length of x-axis and y-axis for heatmap
num_x = len(axis_values)
num_y = num_x
runs_per_setting = 20

# min and max x and y parameters for heatmap
min_sugar_metabolism = .45
max_sugar_metabolism = .8
min_water_metabolism = .45
max_water_metabolism = .8

# increments tested (here we assume constant increment, could be logged, quadratic, etc...)
inc_sugar = .05
inc_water = .05
            
# set up names of each column of data from csv
names = pd.Series([
    'sugar_metabolism_rate',
    'water_metabolism_rate',
    'sugar', 
    'water',
    'mean_price',
    'price_variance',
    'population', 
    'basic_only',
    'basic_herder',
    'basic_arbitrageur', 
    'basic_herder_arbitrageur', 
    'switcher_only', 
    'switcher_herder', 
    'switcher_arbitrageur', 
    'switcher_herder_arbitrageur', 
    'percent_basic',
    'percent_arbitrageur', 
    'percent_herder', 
    'percent_switcher', 
    'basic_only_wealth',
    'basic_herder_wealth',
    'basic_arbitrageur_wealth',
    'basic_herder_arbitrageur_wealth',
    'switcher_only_wealth',
    'switcher_herder_wealth',
    'switcher_arbitrageur_wealth',
    'switcher_herder_arbitrageur_wealth', 
    'sugar_flow', 
    'water_flow',
    'distance_from_equilibrium_price',
    'fifty_period_RAP', 
    'mean_rate_of_price_change'])
# names dict used to instantiate an m X n array that will be used
# to keep track of the average value of each variable at each tick for each category
# will be inserted into mean_values dataframe
names_dict = {
    'sugar_metabolism_rate': [np.arange(ticks)],              
    'water_metabolism_rate': [np.arange(ticks)],              
    'sugar': [np.arange(ticks)],                        
    'water': [np.arange(ticks)],
    'mean_price': [np.arange(ticks)], 
    'price_variance': [np.arange(ticks)],
    'population': [np.arange(ticks)],
    'basic_only': [np.arange(ticks)], 
    'basic_herder': [np.arange(ticks)],
    'basic_arbitrageur': [np.arange(ticks)],
    'basic_herder_arbitrageur': [np.arange(ticks)],
    'switcher_only': [np.arange(ticks)],
    'switcher_herder': [np.arange(ticks)],
    'switcher_arbitrageur': [np.arange(ticks)],
    'switcher_herder_arbitrageur': [np.arange(ticks)],
    'percent_basic': [np.arange(ticks)],
    'percent_arbitrageur': [np.arange(ticks)],
    'percent_herder': [np.arange(ticks)], 
    'percent_switcher': [np.arange(ticks)],
    'basic_only_wealth': [np.arange(ticks)],
    'basic_herder_wealth': [np.arange(ticks)],
    'basic_arbitrageur_wealth': [np.arange(ticks)],
    'basic_herder_arbitrageur_wealth': [np.arange(ticks)],
    'switcher_only_wealth': [np.arange(ticks)],
    'switcher_herder_wealth': [np.arange(ticks)],
    'switcher_arbitrageur_wealth': [np.arange(ticks)],
    'switcher_herder_arbitrageur_wealth': [np.arange(ticks)], 
    'sugar_flow': [np.arange(ticks)],
    'water_flow': [np.arange(ticks)],
    'distance_from_equilibrium_price': [np.arange(ticks)],
    'fifty_period_RAP': [np.arange(ticks)],
    'basic_only_wealth_per_capita': [np.arange(ticks)],
    'basic_herder_wealth_per_capita': [np.arange(ticks)],
    'basic_arbitrageur_wealth_per_capita': [np.arange(ticks)],
    'basic_herder_arbitrageur_wealth_per_capita': [np.arange(ticks)],
    'switcher_only_wealth_per_capita': [np.arange(ticks)],
    'switcher_herder_wealth_per_capita': [np.arange(ticks)],
    'switcher_arbitrageur_wealth_per_capita': [np.arange(ticks)],
    'switcher_herder_arbitrageur_wealth_per_capita': [np.arange(ticks)],             
    'percent_basic_only': [np.arange(ticks)],
    'percent_basic_herder': [np.arange(ticks)],
    'percent_basic_arbitraguer': [np.arange(ticks)],
    'percent_basic_herder_arbitraguer': [np.arange(ticks)],
    'percent_switcher_only': [np.arange(ticks)],
    'percent_switcher_herder': [np.arange(ticks)],
    'percent_switcher_arbitraguer': [np.arange(ticks)],
    'percent_switcher_herder_arbitraguer': [np.arange(ticks)],
}

# Lists for used to generate new categories of data
new_category = pd.Series([
    'basic_only_wealth_per_capita',
    'basic_herder_wealth_per_capita',
    'basic_arbitrageur_wealth_per_capita',
    'basic_herder_arbitrageur_wealth_per_capita',
    'switcher_only_wealth_per_capita',
    'switcher_herder_wealth_per_capita',
    'switcher_arbitrageur_wealth_per_capita',
    'switcher_herder_arbitrageur_wealth_per_capita',
    'percent_basic_only',
    'percent_basic_herder',
    'percent_basic_arbitrageur',
    'percent_basic_herder_arbitrageur',
    'percent_switcher_only',
    'percent_switcher_herder',
    'percent_switcher_arbitrageur',
    'percent_switcher_herder_arbitrageur'])
  
numerator_category = pd.Series([
    'basic_only_wealth',
    'basic_herder_wealth',
    'basic_arbitrageur_wealth',
    'basic_herder_arbitrageur_wealth',
    'switcher_only_wealth',
    'switcher_herder_wealth',
    'switcher_arbitrageur_wealth',
    'switcher_herder_arbitrageur_wealth',
    'basic_only',
    'basic_herder',
    'basic_arbitrageur',
    'basic_herder_arbitrageur',
    'switcher_only',
    'switcher_herder',
    'switcher_arbitrageur',
    'switcher_herder_arbitrageur'])

denominator_category = pd.Series([
    'basic_only',
    'basic_herder',
    'basic_arbitrageur',
    'basic_herder_arbitrageur',
    'switcher_only_wealth',
    'switcher_herder_wealth',
    'switcher_arbitrageur_wealth',
    'switcher_herder_arbitrageur',
    'population',
    'population',
    'population',
    'population',
    'population',
    'population',
    'population',
    'population'])
        
def process_data():
    # Dataframe will house the average values of each variable across runs for some given 
    # pair of parameters
    mean_values = pd.DataFrame(columns = [np.arange(num_x)], index = [np.arange(num_y)])    
    for x in range(0,num_x):
        for y in range(0,num_y):
            mean_values[x][y] = pd.DataFrame(names_dict)
    # import csvs using names array as headers for each column
    # order of elements in names array should match order of objects recorded
    # during runs in NetLogo

    for i  in range(1, files + 1):
        filename = str(i) + 'sugarscapeGlobalTradeBasics.csv'
        runs = pd.read_csv(filename, names = names)
        for x in range(len(new_category)):
            add_per_capita_categories(runs, new_category[x], numerator_category[x], denominator_category[x])
    
        c = 0
        
        # Find runs whose fixed parameter values match the values for the target
        # coordinates in mean values, will eventually transfer to heatmap
        for a in axis_values:
            if runs.iloc[0]['sugar_metabolism_rate'] == a: # round(a,2):
                for b in axis_values:
                    if runs.iloc[0]['water_metabolism_rate'] == b: # round(b,2):
                        x =  int(round(((b - min_water_metabolism) / inc_water),0))
                        y =  int(round(((a - min_sugar_metabolism) / inc_sugar),0))

                        if c < 1:
                            # avoid error in 0th element of each category 
                            mean_values[x][y] = runs
                        else:
                            # once the first run data has been placed 
                            mean_values[x][y] = mean_values[x][y].add(runs, fill_value = 0)

                        c += 1

    mean_values = mean_values / runs_per_setting
    print_behavior_space_representation(mean_values)
            
##############################################################################################                        
##############################################################################################

def add_per_capita_categories(target_array, per_capita_name, per_capita_numerator, population_denominator):
    target_array[per_capita_name] = target_array[per_capita_numerator] / target_array[population_denominator]
    

def initialize_image(num_x, num_y):
 image = []
 for i in range(num_y):
  x_colors = []
  for j in range(num_x):
   x_colors.append(0)
  image.append(x_colors)
 return image


def color_points(title, filename, category, mean_values,pp, tick, min_val, max_val):
    # x_p = num_x
    # y_p = num_y
    image = initialize_image(num_x, num_y)
    
    for i in range(0, num_x):
        for j in range(0, num_y):
            image[i][j] = mean_values[i][j].iloc[tick][category]
    print(image)        
    plt.imshow(image, origin='lower', extent=(min_sugar_metabolism, max_sugar_metabolism,  min_water_metabolism, max_water_metabolism),
               cmap=cm.Greys_r, interpolation = 'nearest')
    plt.colorbar()
    plt.clim(min_val, max_val)
    plt.xlabel('Water Consumption Rate')
    plt.ylabel('Sugar Consumption Rate')
    plt.title(title + " " + str(tick + 1) + " Ticks")
    fig = plt.gcf()
    plt.show()
    plt.draw()
    pp.savefig(fig)

#    fig.savefig(filename + ".pdf")

def print_behavior_space_representation(data):  
    interval = 1
    pp = PdfPages('population_local_basic_tick_50.pdf')
    for q in range(1, ticks, interval):    
        color_points("Population\nGlobal Trade Basic","Population Global Trade Basic", "population", data, pp, q, 0, 300)
    plt.close('all')
    pp.close()
                    
if __name__ == '__main__':     
    np.save('data',    process_data()) 
    np.load('data.npy')

No comments:

Post a Comment