Chapter 5 Preference
\[U(x)=u \mbox{ for x} \in X\]
5.1 Tuple
Another types of commonly used vector container in Python is called tuple. Tuple acts like coordinates: \[(x_1, x_2, x_3)\in \mathcal{R}^3, \]
coord1 = (5, 3.3, 4)
print(coord1)\[(age,\ gender,\ height,\ weight)\]
person1 = (15, "Female", 162, 55.3)
print(person1)Tuple is immutable.
- not much built-in methods
tp0 = ("1", 1, 5, "5", "1")
tp0.count("1") # how many "1"
tp0.index(5) # where is 5- no way to change its binding values only if you change it to list
list_tp0 = list(tp0)
list_tp0[3] = 4
print(list_tp0)
tp1 = tuple(list_tp0)
print(tp1)For tuple’s immutability, it is ideal to use it to store data that you don’t want to accidentally mess up, such as:
one record from a person;
fixed model parameters: \(U(x,y)=x^{\alpha}y^{\beta}\).
preference_params = (4, 5)
def U(x,y):
(alpha, beta) = preference_params
return x**alpha*y**beta
U(2,3)(alpha, beta) = preference_paramsis called unpacking. The left hand side (LHS) can ignore( ).
alpha, beta = preference_params
print(alpha)
print(beta)- Both list and tuple can be unpacked.
tuple_params = (3, 4)
par1, par2 = tuple_params
list_params = [7, 8]
par3, par4 = list_params
print(par1)
print(par2)
print(par3)
print(par4)5.2 Choice set revisited
\[P_{apple} * x_1 + P_{banana} * x_2 <= I, \] And
\[X=\{ (x_1, x_2)\ | \ P_{apple} * x_1 + P_{banana} * x_2 \leq I\}\] Suppose \(I=10000, P_{apple}=5, P_{banana}=7\). Suppose \(x_1\) and \(x_2\) must be integer.
In this case, it is ideal to model alternative as a tuple of two values.
Choice set as a validation function:
alternative must be a tuple.
alternative must lie inside the budget constraint.
def validate_c(c):
assert (
type(c) is tuple # alternative must be a tuple
and len(c) == 2
and all([ type(ci) is int or type(ci) is float for ci in c])
), "c must be a tuple of two numbers."
x1, x2 = c
if 5*x1+7*x2 <= 10000: # alternative must lie inside the budget constraint
return c
else:
return None
# EOFSince the choice set is finite, we can also choose to list all tuple alternatives as the choice set; all elements pass validation test.
import math # most math functions are in this module
I = 50
P_apple = 5
P_banana = 7
x1_max = math.floor(I / P_apple)
X = []
for x1 in range(x1_max+1):
x2_max = math.floor((I-5*x1)/P_banana) # 無條件捨去
list_x1 = [(x1, x2) for x2 in range(x2_max+1)]
X.extend(list_x1)
# EOF5.3 Tuple as key
def U_cobbDouglas(x, params=(1,1)):
assert (
type(x) is tuple
and len(x) == 2
and all([type(xi) is int for xi in range(len(x))])
), "Alternative should be a tuple of two integers"
alpha, beta = params
x1, x2 = x
u = x1**alpha * x2**beta
return u
# EOFtry:
U_cobbDouglas([2,3])
except:
"Error"
U_cobbDouglas((2,3))
U_cobbDouglas((3,4))Basically utility function is a mapping from alternative to a real number. We can also use dictionary to define mapping:
alpha, beta = (1,1)
U_cobbDouglas_dict = {
key: key[0]**alpha * key[1]**beta for key in X
}- tuple can act as dictionary key.
U_cobbDouglas_dict.keys()
U_cobbDouglas_dict[(4,2)]
U_cobbDouglas_dict[(3,4)]5.4 Build a consumer
One consumer:
p_x1=5; p_x2=7; I=100
consumer <- new.env()
consumer$params <- c(1,1)
consumer$preference <- function(x, params=consumer$params){
assertthat::assert_that(
is.numeric(x)
&& length(x) == 2,
msg="alternative must be a vector of 2 numeric values"
)
u = x[[1]]**params[[1]] * x[[2]]**params[[2]]
return(u)
}
consumer$validate_c <- function(x){
assertthat::assert_that(
is.numeric(x)
&& length(x) == 2,
msg="alternative must be a vector of 2 numeric values"
)
if(p_x1*x[[1]]+p_x2*x[[2]] <= I){
return(x)
} else {
return(NA)
}
}consumer$params
consumer$preference(c(1,2))
consumer$validate_c(c(1,2))5.5 Consumer generator
The following function generates consumer who:
Faces prices \(p_{x1}=5, p_{x2}=7\) and has income \(I=100\);
Have preference as a Cobb-Douglass function, \(x^{\alpha}y^{\beta}\) where \(\alpha, \beta\) can be heterogeneous when a consumer is generated.
p_x1=5; p_x2=7; I=100
Consumer = function(params){
consumer <- new.env()
consumer$params <- params # replace c(1,1)
consumer$preference <- function(x, params=consumer$params){
assertthat::assert_that(
is.numeric(x)
&& length(x) == 2,
msg="alternative must be a vector of 2 numeric values"
)
u = x[[1]]**params[[1]] * x[[2]]**params[[2]]
return(u)
}
consumer$validate_c <- function(x){
assertthat::assert_that(
is.numeric(x)
&& length(x) == 2,
msg="alternative must be a vector of 2 numeric values"
)
if(p_x1*x[[1]]+p_x2*x[[2]] <= I){
return(x)
} else {
return(NA)
}
}
return(consumer)
}consumer11 <- Consumer(c(1,1)) # preference: x*y
consumer35 <- Consumer(c(3,5)) # preference: x**3 * y**5
consumer11$preference(c(2,5))
consumer35$preference(c(2,5))5.6 Class
class Consumer:
def __init__(self, params):
self.params = params
def preference(self, x):
assert (
type(x) is tuple
and len(x) == 2
and all([type(x[i]) is int for i in range(len(x))])
), "Alternative should be a tuple of two integers"
alpha, beta = self.params
x1, x2 = x
u = x1**alpha * x2**beta
return u
@staticmethod
def validate_c(c):
assert (
type(c) is tuple # alternative must be a tuple
and len(c) == 2
and all([ type(ci) is int or type(ci) is float for ci in c]) # all returns one True/False
), "c must be a tuple of two numbers."
x1, x2 = c
if 5*x1+7*x2 <= 100: # alternative must lie inside the budget constraint
return c
else:
return None
# EOFThe basic three methods:
__init__(self, ...): To define instance attributes.
self represents the instance. Inside the function body, anyself._attribute_ = ...will bind...value toself._attribute_even noreturn selfat the end.method_name(self, ...): Dynamic instance method.
Method that requiresselfcontent in order to work or is used to updateselfcontent.Static instance method:
Method that does not require or updateselfcontent.
@staticmethod
def method_name(...): # no self in input
...consumer = Consumer(params=[1,1])
consumer.params
consumer.preference((2,3))
consumer.validate_c((2,3))Construct a Consumer class which can generates consumer instance who:
Has Cobb-Douglas type of preference \(x^{\alpha}y^{\beta}\) but heterogeneous parameter values of \(\alpha, \beta\).
Has different income levels.
Class object is a generator. Any new object generated by it is an instance.
consumer11 = Consumer((1,1)) # preference: x*y
consumer35 = Consumer((3,5)) # preference: x**3 * y**5All objects when created are an instance of some class.
int0 = 5
float0 = 5.5
char0 = "hello"
int0.__class__
float0.__class__
char0.__class__From instance-class perspective, to check an object type:
type(int0) is int
type(float0) is float
type(char0) is stris the same as to check its instance class:
isinstance(int0, int)
isinstance(float0, float)
isinstance(char0, str)5.7 Instance and class variables
All consumers faces the same \(p_{x1}=5, p_{x2}=7\).
A consumer is different from the other at:
different income \(I\).
different preference parameters \(params\).
in function he will
- feel
.preference()differently.
- face different choice set
.validate_c().
# same prices
prices = (5, 7)
# difference
I = 100
params = (1, 1)
def preference(params, x):
x1, x2 = x
alpha, beta = params
u = x1**alpha * x2**beta
return u
def validate_c(I, x):
x1, x2 = x
p_x1, p_x2 = prices
if p_x1*x1 + p_x2*x2 <= I:
return x
else:
return NoneThe key to build a class is:
- heterogeneous parameters should be defined as
self.xxxand accessed viaself.xxx.
- market parameters (homogeneous parameters) should be accessed via
{class_name}.xxx.
when method requires heterogeneous parameters, it should be a dynamic method with
selfas its first input.when method requires no heterogeneous parameters, it should be a static method without
selfinput.
Heterogenous parameters are called instance variables (aka instance attributes). Homogeneous parameters are called class variables (aka class attributes).
class Consumer:
# same prices
prices = (5, 7)
# difference
def __init__(self, I, params):
self.I = I
self.params = params
def preference(self, x):
x1, x2 = x
alpha, beta = self.params # heterogeneous parameters
u = x1**alpha * x2**beta
return u
def validate_c(self, x):
x1, x2 = x
p_x1, p_x2 = Consumer.prices # homogeneous parameters
if p_x1*x1 + p_x2*x2 <= self.I:
return x
else:
return None
# EOFconsumer11_100 = Consumer(100, (1,1))
consumer35_250 = Consumer(250, (3,5))print('homogeneous price: ' + str(consumer11_100.prices))
consumer11_100.I
consumer11_100.params
print('homogeneous price: ' + str(consumer35_250.prices))
consumer35_250.I
consumer35_250.paramsConsumer.prices
consumer11_100.prices
consumer35_250.prices- Market prices change to \((3,3)\)
prices are faced by all consumer instances, called class variables. I and params are different across consumer instances, called instance variables.
Consumer.prices = (3,3)
consumer11_100.prices
consumer35_250.prices- Changing class variable values through Class changes all instances’ facing such values.
# change an instance's class variable
consumer11_100.prices = (5, 5)
consumer11_100.prices
# WILL NOT change the other instance's class variable
consumer35_250.prices- Change class variable value through Instance only creates a counterpart instance variable. To see the instance’s class varible of the same name:
consumer11_100.prices # the instance variable **prices**
consumer11_100.__class__.prices # the class variable **prices**consumer35_250.priceswill prompt Python to look up prices as an instance variable within
consumer35_250instance.However, there is no such an instance variable.
The scoping rule of Python will continue its search in class variables.
5.8 綜合練習
1. Aggregate demand
Suppose in an economy individual demands are like: \[q(p)=\alpha - \beta*p,\] where \(p>0\), and \(\alpha\) and \(\beta\) are numbers that guarantee \(q \geq 0\) and the demand rule holds (i.e. \(\beta > 0\)).
1.1
Construct an individual demand function that has \(\alpha=10\) and \(\beta=2\).
1.2
Construct a Demand class generator that
params = (10, 2)
demand = Demand(params) # generate a demand instancewhere
demand.q(2) returns the quantity demanded of an individual demand function \(10-2p\) at \(p=2\).
Generate random numbers
In R, there are four functions associated with random variables:
d{distribution}: the density function of{distribution}(the proportion of population that has height = 172cm)
dunif(0.3, 0, 0.5) # density at 0.3 for uniform(0, 0.5)p{distribution}: the (cummulative) distribution function of{distribution}(the proportion of population that has height less or equal to 172 cm)
punif(0.3, 0, 0.5) # cdf at 0.3 for uniform(0, 0.5)q{distribution}: the quantile of{distribution}(if the quantile of 70% is 172cm meaning that 70% of population has height less or equal to 172cm )
qunif(0.5, 0, 0.5) # quantile of 0.5 (ie. median) of uniform(0, 0.5)r{distribution}: random draws from{distribution}
# no seed
rns_noSeed = runif(n=5, 0, 0.5)
print(rns_noSeed)# with seed
set.seed(2020)
rns_seed = runif(n=5, 0, 0.5)
print(rns_seed)numpy.random is a popular module to deal with distributions. It encapsulates all distribution tools as methods inside an instance of various generators. Among them, to generate random variables, default_rng is commonly used.
from numpy.random import default_rngrng_noSeed = default_rng() # no seed
rns_noSeed = rng_noSeed.uniform(50, 100, size=5)
print(rns_noSeed)rng_seed = default_rng(2038) # with seed 2038
rns_seed = rng_seed.uniform(50, 100, size=5)
print(rns_seed)1.3
Generate 500 individual demands with \(\alpha\)’s drawn from uniform(50,100), and \(\beta\)’s drawn from uniform(2, 30). Save them in a list, ind_demands.
1.4
Construct an aggregate demand function:
aggregate_demand(2, ind_demands)returns an aggregated quantity demanded at the price of 2.
2. Intertemporal Consumption
A consumer lives for two periods, choosing consumption today and consumption tomorrow. Assume no inflation (or for simplicity price level is fixed at one), with interest rate 5% decreasing $1 of consumption today will increase saving $1 today and yield $1+5% additional of consumption tomorrow. Given that the consumer has a present discounted value of life wealth $100 (ie the maximal \(c_0\) he can get if he spends all his life wealth today), the following consumption alternatives of \((c_0, c_1)\) are feasible: \[[(100,0), (99, 1.05), (98, 2.10), ..., (0, 105)]\] The alternatives above all satisfy: \[c_0+\frac{1}{1.05}c_1 = 100\] Any spending on the LHS less than $100 is also feasible. In other words, the consumer faces the following intertemporal budget constraint: \[c_0 + \frac{1}{1.05}c_1 \leq 100\]
2.1
If \(c_0\) has to be integer, but \(c_1\) can be float, construct a list of tuples that represents \([(100,0), (99, 1.05), (98, 2.10), ..., (0, 105)]\).
2.2
Construct a validation function of consumption that when input a tuple of two numbers, it validates if the alternative is feasible – if feasible return the original tuple; otherwise, return None.
2.3
If both \(c_0\) and \(c_1\) have to be integer, construct the consumer’s choice set (as a list containing all tuples that satisfies the intertemporal budget constraint)
2.4
Suppose the consumer has the following preference function: \[U(c_0, c_1)=\log(c_0)+\beta*\log(c_1),\] where \(\log\) is a natural based log function that can be assessed in Python as:
import math
math.log()Construct the consumer’s preference function.
2.5
Construct Consumer class object that can generate any consumer with user-chosen \(\beta\) and facing the same intertemporal budget constraint as a validation function as stated at the beginning of the question.
3. class Square
Construct a Square class generator such that:
square = Square(width=2, height=10)
square.width # shows 2
square.height # shows 10
square.area() # shows 20 4. Utility generator
There are two types of utility functions we want to build:
Constant Elasticity of Substitution (CES):
\[U(x)=[\alpha_1 x_1^{\rho} +\alpha_2 x_2^{\rho}]^{\frac{1}{\rho}}\]Quasi-linear (QL): \[U(x)=g(x_1) + b*x2\]
All symbols other than \(x_1, x_2\) are preference parameters.
4.1
Build a CES class such that:
alpha1, alpha2, rho = (2, 3, 5)
params = (alpha1, alpha2, rho)
ces = CES(params)
x = (1, 10)
ces.U(x) # show the utility levels of x given alpha1, alpha2, rho preference parameter values4.2
Build a QL class such that:
When
import math
def q(x1):
return math.log(x1)the following code generates a QL utility instance representing: \[U(x)=\log(x_1)+x_2\]
b = 2
ql = QL(q, b)
x = (2, 5)
ql.U(x) # returns the utility levels of x given q(x1) and bzip
from numpy.random import default_rng
rng1 = default_rng(293)
rng2 = default_rng(283)
rng_zip = zip(rng1.uniform(50,100, size=3), rng2.uniform(2,10, size=3))
list(rng_zip)p=5
qs = [ alpha - beta*p for alpha, beta in rng_zip]rng_zipis an iterable.An iterable when loops finish will be empty; nothing to iterate afterward.
list(rng_zip)- Normally we won’t set aside the zipped object, but zip columns in loop under the hood as:
rng1 = default_rng(293)
rng2 = default_rng(283)
qs = [ alpha - beta*p for alpha, beta in zip(rng1.uniform(50, 100, size=3), rng2.uniform(2, 10, size=3))]
qszip() zips columns of vectors into 1 column of tuples.
X = ["a", "b", "c"]
Y = [2, 3, 5]
zip_xy = zip(X, Y)list(zip_xy)zip_xyis iterable.
iter_xy = iter(zip_xy)
next(iter_xy)X = ["a", "b", "c"]
Y = [2, 3, 5]
dict_xy = { key: value for (key, value) in zip(X, Y)}
print(dict_xy)