Friday, September 23, 2016

Building Supply and Demand Graphs in Python

When I first started teaching, I grew frustrated with the process of creating visualizations. Microsoft office graphs look tacky. I wanted something that is can be standardized for making graphs of supply and demand. I also wanted it to be customizable and not be corrupted by the name of the company that provided the means for creating the graph (see here). After having used Python and the matplot library to make other visualizations, I realized that I could use similar code for making a generic supply and demand graph. You can customize Matplotlib's graphs to a significant degree.



If you are afraid of programming, never fear. This is a great place to start. Just download Anaconda and use this template. The code below is filled with notes to help you fill out the template. The final graph is depicted above:
  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
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages

pp = PdfPages('LaborSupplyAndDemandFloor.pdf')

# "supply", "demand", "floor" and "equilibrium" are all numpy arrays
# created at the bottom of the script.
# the supplyAndDemandFloor method is also called at the bottom of the script
def supplyAndDemandFloor(supply, demand, floor, equilibrium):
    
    fig = plt.figure(dpi=128, figsize=(10,6))

    # Plot supply and demand curves
    # Must instantiate numpy arrays -- see lines 85 and 87
    plt.plot(supply,  'k-', linewidth=3)
    plt.plot(demand, 'k-', linewidth=3)

    # The length of the equilibrium line must match the horizontal coordinate 
    # of the equilibrium quanity -- see lines 98, 100
    plt.plot(equilibrium, 'k--', linewidth=1.5)
    # The floor is a horizontal line -- see lines 90, 94
    plt.plot(floor, 'r--',label= "Price Floor", linewidth=1.5)

    # plt plt takes the form like:
    # plt.plot((x1,x2)),(y1,y2), 'r-', linewidth = x)
    
    # e.g., for command on line ##:
    # create a vertical line at x1 = 2000, y2 = 2000, y1 = 0, y2 = 8000
    plt.plot((2000, 2000), (0, 8000), 'r--', linewidth=1.5)
    plt.plot((8000, 8000), (0, 8000), 'r--', linewidth=1.5)  
    plt.plot((5000, 5000), (0, 5000), 'k--', linewidth=1.5)      

    # frame = plt.gca() required to use commands for removing
    # values on axes        
    frame = plt.gca()    
    
    # remove axis values
    frame.axes.get_xaxis().set_visible(False)
    frame.axes.get_yaxis().set_visible(False)    

    plt.title('Pizza Market', fontsize = 32)
    
    # p variable on top of y-axis
    plt.text(-500, 10000, "$p$", fontsize=24)

    # surplus price variable
    plt.text(-550, 7900, "$p_s$", fontsize=24)
    
    # equilibrium price variable
    plt.text(-550, 5000, "$p_e$", fontsize=24)

    # identify supply curve    
    plt.text(8200, 9000,"$S_p$", fontsize = 24)
    # identify demand curve    
    plt.text(8200, 2000,"$D_p$", fontsize = 24)
    

    # quantity demanded at surplu price
    plt.text(1800, -650, "$Q_d$", fontsize=24)
    # quantity supplied at surplus price
    plt.text(7800, -650, "$Q_s$", fontsize=24)
    # Qs = Qd at equilibrium price
    plt.text(4800, -650, "$Q_e$", fontsize=24)
    # mark that Q is represented on horizontal axis
    plt.text(10000, -650, "$Q$", fontsize=24)

    # label the axes
    plt.xlabel("Pizza", fontsize=20)
    plt.ylabel("Price", fontsize = 20)
    plt.tick_params(axis='both', which='major', labelsize=16)
#    plt.legend(floor, loc='upper right')
    
    # save PDF of figure
    # you can also save to image file
    # pdf is useful if multiple graphs are created
    pp.savefig(fig)
    
    # close PDF
    pp.close()
    

# Instantiate array with supply curve points
supply = np.arange(10000)
# ... demand curve points
demand = np.arange(10000,0, -1)

# ... for price floor line
priceFloor = np.arange(1, 10000)
# set value of all points value of price floor
# value of prices are arbitrary since we dropped
# axis values
priceFloor[priceFloor > 0] = 8000

# ... for equilibrium price, length of this array 
# is equal to the equilibrium quantity
priceEquilibrium = np.arange(1, 5000)
# set all values to the equilibrium price
priceEquilibrium[priceEquilibrium > 0] = 5000

# finally, call method to produce the graph
supplyAndDemandFloor(supply,demand, priceFloor, priceEquilibrium)

Thursday, September 1, 2016

How to Succinctly Visualize the BehaviorSpace from NetLogo using Python

When I first programmed a heuristic rendition of Sugarcape, I was impressed by the results. Time and again, they were consistent across the behavior-space. But how does one convey results that take many hours to observe into simple visualizations? Is it possible to condense the data in such a manner?

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')

Saturday, July 23, 2016

New Austrian Macroecomics Issue for the Review of Austrian Economics

Articles for the coming RAE issue on New Austrian Macroeconomics are posted online. An early excerpt from Richard Wagner and Paul Lewis explain the contribution of each paper contained in the issue:
In “Toward a New Austrian Macroeconomics,” Vipin P. Veetil and Lawrence H. White present a wide-ranging comparison of points of similarity and difference between their vision of a New Austrian macroeconomics and standard macro framework grounded in the DSGE (Dynamic Stochastic General Equilibrium) model of a macroeconomy. In “Playing at Markets: A New Austrian Perspective on Macroeconomic Policy,” Alexander William Salter explains how orthodox expositions of macro policy create a form of shell game in deflecting the attention of observers away from the micro-level interactions out of which macro variables emerge. In “Dynamic Coordinating Non-Equilibrium,” Santiago Gangotena sets forth an alternative to the DSGE model wherein generally coordinated patterns of economic activity arise within a generative or non-equilibrium scheme of modeling. In “The Unresolved Problem of Gratuitous Credit in Austrian Banking Theory,” Raymond Niles probes some points of controversy in Austrian banking theory over whether elasticity in the supply of money is a modest or a severe source of economic discoordination, or whether it might actually have socially beneficial properties in facilitating experimentation. In “Entrepreneurship, Search Costs, and Ecological Rationality in an Agentbased Economy,” James Caton explores different approaches to rationality in individual action in a manner that is congruent with continual experimentation and the injection of novelty into non-equilibrium but generally coordinated processes.

Monday, April 25, 2016

Entrepreneurship, Search Costs, and Ecological Rationality in an Agent-based Economy

New paper, "Entrepreneurship, Search Costs, and Ecological Rationality in an Agent-based Economy", forthcoming in the Review of Austrian Economics:
Since Coase’s paper on the firm, transaction costs have occupied much attention as a field of economic inquiry. Yet, with few exceptions, neoclassical theory has failed to integrate transaction costs into its core. The dominant mode of theorizing depends upon Brouwer fixed points which cannot integrate transaction costs in more than a superficial manner. Agent-based modeling presents an opportunity for researchers to investigate the nature of transaction costs and integrate them into the core of economic theory.
To the extent that transaction costs reduce economic efficiency, these costs provide opportunities for entrepreneurs to earn a profit by reducing these costs. We employ an extension of Epstein and Axtell’s (1996) Sugarscape to demonstrate this point one type of transaction costs: search costs. When agents do not face the cost of finding a trading partner, the system quickly reaches a steady state with tightly constrained prices regardless of agent production strategies. When search costs are present, entrepreneurs may use competing strategies for production and exchange that allow them to earn higher revenues than they would earn otherwise. These cost reducing innovations tend to promote concatenate coordination (Klein 2012). In our model, search costs are tied to consumption rates of our agents. The agent’s production strategies represent technology in the form of mental models (Denzau and North 1994) that shape agent action with regard to the agent’s environment. The success of these are dependent on their ability to overcome search costs. Finally, we find that the average profit, or market return, earned by each of these mental structures tends to equalize as a result of competition.

Monday, April 4, 2016

Being and Decision: New Interests, New Blog

I have a new blog to serve as an outlet for more general interests.

The essence of human life can be categorized broadly as being - life as it is without the intervention of consciousness - and decision - the reprogramming of existence so as to transform on some margin or margins. Humans employ logic to understand their environment, which includes themselves, and intervene in it with intention of generating some outcome. The outcomes generated may or may not be in accordance with the intention of the acting agent. Agents face the challenge of how to be effective in action while not generating outcomes that harm themselves and others. To the extent that they harm themselves, agents will not long survive. To the extent that agents harm others, they form relationships defined by enmity. This also hurts their ability to survive, though many engage in such practice while successfully isolating themselves from the negative consequences of their actions. "He who lives by the sword dies by the sword." Such isolation must be finitely lived. 
The challenge that confronts society is for those acting within it to do so in such a manner that does not make others worse off: to aim for Paretian improvements. When this is not possible and others are made worse off by our own actions, whether or not recompense is required by law, we can at least be sure to leave others the opportunity to improve their own situations whenever possible. We need an ethic that appreciates that the social animal is also an institutional animal. Humankind must be careful to consider the effects of our interventions as the effects of these are travel through institutional channels that increase the distance of the outcome from the motivator of that outcome. This is the golden rule applied to the logic of institutions.

Sunday, March 20, 2016

Perception, Expectation, and Action: A Framework for Agent-oriented Theorizing

I have posted a new paper on SSRN:
For much of the last century, economic theory was developed by aid of models who elements and structure bare a limited similarity to reality. Under the dominant paradigm, agents are modeled as obeying utility functions that are defined by systems of linear equations. While these models have been useful in conveying a particular economic logic, they do not allow for modeling the complexity of agent decision-making or agent interaction. In recent decades, agent-based computational models have emerged as a tool for applying and exploring social theory, including theories of the market. While the neoclassical formulation of rationality has been used by many to model activity, there has been little exploration in modeling the actual decision-making structure of an agent. We consider a general structure of human perception, expectation, and action that can be used to construct an agent’s whose decision-making structures include elementary economic logic and fine-grained detail of the environment. 

Tuesday, December 1, 2015

Ecological (or Realist) Rationality in Economics

Those unfamiliar with ecological rationality may be surprised by the term ecological used in in a discussion of economic methodology and methods. For the last century, economic theory has been described increasing in terms of of systems linear equations. In order to be mathematically tractable, this framework assumes homogeneous agents whose preferences are only described in terms of prices and quantities. In this theoretical world, only one price, centrally computed by the Walrasian auctioneer, can exist simultaneously (Axtell 2005). All action is determined solely by preferences regarding price and quantity, couched in terms of utility maximization (Arrow and Hahn, 1971). Agents do not interact with one another directly, but rather, only indirectly through the prices and quantities proffered by the auctioneer. An agent’s decisions to interact with the auctioneer is always a consequence of their desire to maximize utility, a variable treated always as a cardinal measure. This is the core of neoclassical economic theory as it has come to be practiced professionally.

Not to buck the trend, macroeconomists have followed the same type of pattern. Over the last half century, macroeconomists have built models that employ either macrovariables, representative agents, or both (Hirschleifer. 1970; Kohn 1981; Lucas 1972). The move to DSGE simulations represents an extension of this equilibrium paradigm (Wagner 2011). The relaxation of some assumptions, such as the assumption of perfect information, still leaves economics with the same core. Agents in this paradigm are automatons driven by a singular desire to maximize a utility vector. The motives of these little resemble that of agents in the real world. To the extent that we are wrong, we would prefer not to associate with such avaricious, monotonously motivated agents.

The social world is far richer than the description provided by what has come to be accepted as the neoclassical paradigm. While a perfect model of reality would be redundant, the dominant paradigm lacks the fidelity necessary to be considered a simplified replication of a reality within an open system. Action of economic agents are not driven by solely by price and quantity vectors, although they do interact with prices and quantities. To the extent that they are, a subjective interpretation of price must be employed (Mises 1949). The world which economic agents inhabit is a subjective one. In this world, agents are not only imperfectly informed; they imperfectly perceive the world. They are certainly in no position to optimize (Chaitain, Doria, and da Costa 2011). Even when the world is defined in terms of prices and quantities, agent optimization according to the typical calculus is computationally intractable. In the least this is an insurmountable problem for the Walrasian auctioneer (Axtell 2005).

It is necessary, then, that economic theory not only drop its assumption of perfect information, but that economists altogether reevaluate the perceptual framework that modeled agents inherit. Theory must identify and employ only the elements most significant for the domain of study, but with the realization that agents interact with particular objects at a given place and time. An appropriate framework allows for the appearance of general contexts that represents the details of that contexts as general types. A ecological, or realist, framework of perception and rationality fulfills these requirements.

An ecological perspective can be described as containing heterogeneous and interacting agents. These agents inherently interact with the world in a manner consistent with Bayesian updating.

In the ecological view, thinking does not happen simply in the mind, but in interaction between the mind and its environment. This opens up a second more efficient way to solve the problem: to change the environment. The relevant part of the environment is the representation of the information, because the representation does part of the Bayesian computation [emphasis ours]. (Gigerenzer 2008, 17)
Agents offload computation onto objects and systems of objects that they perceive in the course of their existence. Agents do not optimize, they adopt patterns of behavior that tend to promote predicatable outcomes. One such family of decision-making rules is what Gigerenzer and Goldman term “Fast and Frugal” (1996). Fast and frugal rules allow the agent to make a decision based only on a single piece of information. (Are dark clouds forming to the immediate west? It is probably a good idea to wear a coat until they either subside or until a storm has come and gone. One does not need to check the weather report under such conditions.) Agents do not have the time to collect all available information when making decisions. In a world that is part of an open system and where variables of interest are not independent of one another, a fast and frugal rule actually outperforms regression analysis in predictive power (Gigerenzer 2008, 41). As agents grow accustomed to these rules, they become ingrained in habit and thereby reduce the computational work required by the agent.

Rules that guide agent action take a vast variety of forms. For example, an agent investor may only invest according to fundamental measures. Others may invest according to past data. Still others may copy the investment decisions of investors who consistently beat the market. An obvious consequence of action guided by these rules is agent interaction. Agents influence one another by influencing conditions of scarcity and by interacting with and copying one another directly. The first of these is accounted for, if only imperfectly, in the modern formulation of economic theory. The latter is non-existent in that realm. A convenient approach to framing this latter problem is in terms of an information cascade where the information transferred is a decision making rule (Earl, Peng, and Potts 2006). The rules that appear to promote an agent’s ends are copied by other agents (Hayek 1962; Bikhchandani, Hirshleifer, and Welch 1998).

Notice that this framework for decision-making fits nicely into a theory of expectations that does not assume the end it is supposed to prove. Consider the Lucas critique (1972). Lucas noted that any relationship among macrovariables will tend to disappear one it has been recognized. Proponents of rational expectation would argue that agents make predictions about the future using all available information and that agent predictions of the future are conveniently distributed around a median outcome that, absent information shocks, represents an accurate prediction (Muth 1961, Fama 1970). This model looks nothing like social reality. It suffers from the same problem that Gigerenzer identifies above concerning Bayesian computation. It has in its favor a modest degree of predictive power, but lacks the fidelity that is required for deeper understanding. The alternative contained within an ecological framework suggests that agents form rules that conform to their interpretation of reality. If agents expect that there will be a high degree of inflation, they will substitute assets in their portfolio in lieu of cash. They only need to know what sort of action to take given expectation of a particular circumstance. Those agents who tend to be better at predicting will tend to maintain larger stocks of wealth than those who predict poorly. As agents receive feedback concerning their actions, they will update if they believe they were following an inferior pattern of action. Over time, this allows strategies to be developed, tested, and either discarded or duplicated.

Roger Koppl describes this process in his theory of expectations. Borrowing from Schutz and Hayek, Koppl posits two types of expectations formed by agents: cognitive and acognitive (2002). Cognitive expectations represent conscious predictions of the world given some information. Conscious predictions, however, are limited in their scope inasmuch as they only affect agent action a single time. Agents also form habits over time that ideally promote their continued existence and prosperity. The old maxim, “early to bed, early to rise,” for example, encourages the formation of acognitive expectations with respect to ones bedtime and waketime. Those who follow must assume that action in concordance with the maxim promotes a state of affairs that is superior to a world where the agent lacks such a habit. Returning to the inflation example, agents may learn to immediately purchase assets – maybe real estate, stocks and commodities – whenever they hear a trusted source of information suggest that there will be inflation in the future. Any misallocations that occur in this process will be smoothed out over time by competing arbitrageurs in the long-run. This does not exclude the possibility of economic volatility in the meantime as relatively ignorant agents compete with one another, collectively discovering the true conditions of the market by a process of trial and error (Hayek 1942; [1968] 2002).

Over the last few decades, substantial progress has been made in understanding of human perception and that perception's interaction with the environment. Reliance on rational expectations by economists have prevented them from taking advantage of this progress. While rational expectations is useful in justifying econometric work which represents the bulk of applied research in the last half-century, there exists an opportunity to return to the mode of realist theorizing that dominated economics before World War II. Finally, I have not mentioned agent based modeling above. Econometric analysis was the workhorse of the most recent epoch of economic thought. I expect agent-based modeling to become the workhorse of economists working with pure analysis. Many of the components for this already exist (see Simon 1996; Hayek 1962; Crawford and Ostrom 1996; North and Denzau 1994; North 2005). It is only a matter of time before these methodologies are employed to create agent-based simulations across our field.