First, I needed to generate data that could be read easily by Python. NetLogo's standard data generator does not do this well. Here is the code that I used instead:
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  | to setup
. . . 
  prep-csv-name
  reset-ticks
end
. . . 
to go
. . . 
  write-csv csv-name final-output
  tick
end
. . . 
to prepare-behavior-space-output
  set final-output (list
    total-sugar
    total-water
    mean-price-current-tick
    price-variance
    population
    
    basic-only
    basic-herder
    basic-arbitrageur
    basic-herder-arbitrageur
    switcher-only
    switcher-herder
    switcher-arbitrageur
    switcher-herder-arbitrageur
    current-percent-basic
    current-percent-arbitrageur
    current-percent-herder
    current-percent-switcher
    wealth-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
    sugar-consumed-this-tick
    water-consumed-this-tick
    distance-from-equilibrium-price
    mean-price-50-tick-average
    mean [endogenous-rate-of-price-change] of turtles
    )
end
to write-csv [ #filename #items ]
  ;; #items is a list of the data (or headers!) to write.
  if is-list? #items and not empty? #items
  [ file-open #filename
    ;; quote non-numeric items
    set #items map quote #items
    ;; print the items
    ;; if only one item, print it.
    ifelse length #items = 1 [ file-print first #items ]
    [file-print reduce [ (word ?1 "," ?2) ] #items]
    ;; close-up
    file-close
  ]
end
to prep-csv-name
  set csv-name "ssugarscapeLocalTradeAllClassesFinal.csv"
  set csv-name replace-item 0 csv-name (word behaviorspace-run-number)
end
to-report quote [ #thing ]
  ifelse is-number? #thing
  [ report #thing ]
  [ report (word "\"" #thing "\"") ]
end
 | 
This creates csvs for each run that print out the value of objects described in and in the order presented in the list. This order is important as the same ordering is used in Python when I collect data from the csvs.
The setup looked like this:
[Late Add] I used 8 different settings for sugar and water metabolism. this is reflected in the python code under "num_x" and "num_y". 64 settings were used. Each setting was run 10 times to produce 640 runs, each of whose length were 15000 periods.
Below is the Python code that I used to generate visualizations that look like this (which were used in this article):
Each column and row show a pair of values representing the rate of consumption for each by the agents. In this case, I was capturing the change in wealth per capita, defined as units of time the agent can survive given his current stock of goods. "initialize_image(num_x, num_y):" and "color_points(title, filename, category, mean_values,pp, tick, min_val, max_val):" are the methods that generalize the output.
Please note that this code will fail if the size of your data set is too large to be held by your RAM. In that case, you will need to use the dask library to instantiate the dataframes held by the elements in the "runs" and/or "mean_values" dataframes.
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  | 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 files = 640 ticks = 15000 run_period = [] #np.empty((ticks)) num_x = 8 num_y = 8 runs_per_setting = 10 for j in range(0, ticks): run_period.insert(j, j + 1) run_period = pd.Series(run_period) def prepare_lists(files): # # number of elements recorded in csv by netlogo num_categories = 30 # # number of elements created using data from netlogo additional_categories = 16 total_categories = num_categories + additional_categories runs = pd.Series([np.arange(files)]) names = pd.Series(['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 = {'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)], # Categories to be generated within python '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)], } for i in range(1, files + 1): filename = str(i) + 'sugarscapeLocalTradeBasics.csv' runs[i] = pd.read_csv(filename, names = names) # , sep = None,engine='python') # Add categories generated from data manipulation runs[i]['basic_only_wealth_per_capita'] = runs[i]['basic_only_wealth'] / runs[i]['basic_only'] runs[i]['basic_herder_wealth_per_capita'] = runs[i]['basic_herder_wealth'] / runs[i]['basic_herder'] runs[i]['basic_arbitrageur_wealth_per_capita'] = runs[i]['basic_arbitrageur_wealth'] / runs[i]['basic_arbitrageur'] runs[i]['basic_herder_arbitrageur_wealth_per_capita'] = runs[i]['basic_herder_arbitrageur_wealth'] / runs[i]['basic_herder_arbitrageur'] runs[i]['switcher_only_wealth_per_capita'] = runs[i]['switcher_only_wealth'] / runs[i]['switcher_only'] runs[i]['switcher_herder_wealth_per_capita'] = runs[i]['switcher_herder_wealth'] / runs[i]['switcher_herder'] runs[i]['switcher_arbitrageur_wealth_per_capita'] = runs[i]['switcher_arbitrageur_wealth'] / runs[i]['switcher_arbitrageur'] runs[i]['switcher_herder_arbitrageur_wealth_per_capita'] = runs[i]['switcher_herder_arbitrageur_wealth'] / runs[i]['switcher_herder_arbitrageur'] runs[i]['percent_basic_only'] = runs[i]['basic_only'] / runs[i]['population'] runs[i]['percent_basic_herder'] = runs[i]['basic_herder'] / runs[i]['population'] runs[i]['percent_basic_arbitrageur'] = runs[i]['basic_arbitrageur'] / runs[i]['population'] runs[i]['percent_basic_herder_arbitrageur'] = runs[i]['basic_herder_arbitrageur'] / runs[i]['population'] runs[i]['percent_switcher_only'] = runs[i]['switcher_only'] / runs[i]['population'] runs[i]['percent_switcher_herder'] = runs[i]['switcher_herder'] / runs[i]['population'] runs[i]['percent_switcher_arbitrageur'] = runs[i]['switcher_arbitrageur'] / runs[i]['population'] runs[i]['percent_switcher_herder_arbitrageur'] = runs[i]['switcher_herder_arbitrageur'] / runs[i]['population'] # Keep track of run # processesed print(i) # will be used to record mean parameter values for particular settings # across the behavior space mean_values = pd.DataFrame(columns = [np.arange(num_x)], index = [np.arange(num_y)]) # run_name = np.empty((num_x, num_y, runs_per_setting), dtype = np.dtype((str,32))) for x in range(0,num_x): for y in range(0,num_y): mean_values[x][y] = pd.DataFrame(names_dict) for x in range(0,num_x): for y in range(0,num_y ): for z in range(0,runs_per_setting): # Add 1 because file index starts at 1 # run_name[x][y][z] = 'Cs = ' + str(.5 + x * .025) + ' Cw = ' + str(.5 + y * .025) + ' run ' + str(z + 1) + ' of ' + str(runs_per_setting) run_number = x * num_y * runs_per_setting + y * runs_per_setting + z + 1 mean_values[x][y] = mean_values[x][y].add(runs[run_number], fill_value=0) print("processing: " + str(run_number)) mean_values = mean_values / runs_per_setting print_behavior_space_representation(mean_values) ############################################################################################## ############################################################################################## 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): 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=(.45, .8, .45, .8), 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 + " Period " + str(tick + 1)) fig = plt.gcf() plt.show() plt.draw() pp.savefig(fig) def print_behavior_space_representation(data): interval = 500 pp = PdfPages('population_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Population\nLocal Trade Basics","Population Local Trade Basics", "population", data, pp, q,250,1800) plt.close('all') pp.close() pp = PdfPages('PV_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Price Variance\nLocal Trade Basics", "Price Variance Local Trade Basics", "price_variance", data, pp, q,0 , 8) plt.close('all') pp.close() pp = PdfPages('MP_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Mean Price (Logged)\nLocal Trade Basics","Mean Price Local Trade Basics", "mean_price", data, pp, q, -1, 8) plt.close('all') pp.close() pp = PdfPages('DfEP_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Distance From Predicted Equilibrium Price\nLocal Trade Basics", "Distance From Predicted Equilibrium Price Local Trade Basics", "distance_from_equilibrium_price", data, pp, q, -1, 8) plt.close('all') pp.close() pp = PdfPages('FPAMP_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Fifty Period Average Moving Price (Logged) \nLocal Trade Basics", "Fifty Period Average Moving Price (Logged) Local Trade Basics", "fifty_period_RAP", data, pp, q, -1, 8) plt.close('all') pp.close() pp = PdfPages('MRoPC_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Mean Rate of Price Change\nLocal Trade Basics", "Mean Rate of Price Change Local Trade Basics", "mean_rate_of_price_change", data, pp, q, 0, 1) plt.close('all') pp.close() pp = PdfPages('PB_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Percent Basic \nLocal Trade Basics", "Percent Basic Local Trade Basics", "percent_basic", data, pp, q, 0, 1) plt.close('all') pp.close() pp = PdfPages('WPC_local_basic_tick_15000.pdf') for q in range(999, ticks, interval): color_points("Wealth Per Capita \nLocal Trade Basics", "Wealth Per Capita Local Trade Basics", "basic_only_wealth_per_capita", data, pp, q, 50, 400) plt.close('all') pp.close() if __name__ == '__main__': np.save('data', prepare_lists(files)) np.load('data.npy')  | 


No comments:
Post a Comment