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, \]
= (5, 3.3, 4)
coord1 print(coord1)
\[(age,\ gender,\ height,\ weight)\]
= (15, "Female", 162, 55.3)
person1 print(person1)
Tuple is immutable.
- not much built-in methods
= ("1", 1, 5, "5", "1")
tp0 "1") # how many "1"
tp0.count(5) # where is 5 tp0.index(
- no way to change its binding values only if you change it to list
= list(tp0)
list_tp0 3] = 4
list_tp0[print(list_tp0)
= tuple(list_tp0)
tp1 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}\).
= (4, 5)
preference_params def U(x,y):
= preference_params
(alpha, beta) return x**alpha*y**beta
2,3) U(
(alpha, beta) = preference_params
is called unpacking. The left hand side (LHS) can ignore( )
.
= preference_params
alpha, beta print(alpha)
print(beta)
- Both list and tuple can be unpacked.
= (3, 4)
tuple_params = tuple_params
par1, par2
= [7, 8]
list_params = list_params
par3, par4
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."
), = c
x1, x2
if 5*x1+7*x2 <= 10000: # alternative must lie inside the budget constraint
return c
else:
return None
# EOF
Since 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
= 50
I = 5
P_apple = 7
P_banana
= math.floor(I / P_apple)
x1_max
= []
X for x1 in range(x1_max+1):
= math.floor((I-5*x1)/P_banana) # 無條件捨去
x2_max = [(x1, x2) for x2 in range(x2_max+1)]
list_x1
X.extend(list_x1)
# EOF
5.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"
),
= params
alpha, beta = x
x1, x2 = x1**alpha * x2**beta
u
return u
# EOF
try:
2,3])
U_cobbDouglas([except:
"Error"
2,3))
U_cobbDouglas((3,4)) U_cobbDouglas((
Basically utility function is a mapping from alternative to a real number. We can also use dictionary to define mapping:
= (1,1)
alpha, beta = {
U_cobbDouglas_dict 0]**alpha * key[1]**beta for key in X
key: key[ }
- tuple can act as dictionary key.
U_cobbDouglas_dict.keys()4,2)]
U_cobbDouglas_dict[(3,4)] U_cobbDouglas_dict[(
5.4 Build a consumer
One consumer:
=5; p_x2=7; I=100
p_x1
<- new.env()
consumer
$params <- c(1,1)
consumer
$preference <- function(x, params=consumer$params){
consumer::assert_that(
assertthatis.numeric(x)
&& length(x) == 2,
msg="alternative must be a vector of 2 numeric values"
)
= x[[1]]**params[[1]] * x[[2]]**params[[2]]
u
return(u)
}
$validate_c <- function(x){
consumer::assert_that(
assertthatis.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)
} }
$params
consumer$preference(c(1,2))
consumer$validate_c(c(1,2)) consumer
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.
=5; p_x2=7; I=100
p_x1
= function(params){
Consumer
<- new.env()
consumer
$params <- params # replace c(1,1)
consumer
$preference <- function(x, params=consumer$params){
consumer::assert_that(
assertthatis.numeric(x)
&& length(x) == 2,
msg="alternative must be a vector of 2 numeric values"
)
= x[[1]]**params[[1]] * x[[2]]**params[[2]]
u
return(u)
}
$validate_c <- function(x){
consumer::assert_that(
assertthatis.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)
}
<- Consumer(c(1,1)) # preference: x*y
consumer11 <- Consumer(c(3,5)) # preference: x**3 * y**5
consumer35
$preference(c(2,5))
consumer11$preference(c(2,5)) consumer35
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"
),
= self.params
alpha, beta = x
x1, x2 = x1**alpha * x2**beta
u
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."
), = c
x1, x2
if 5*x1+7*x2 <= 100: # alternative must lie inside the budget constraint
return c
else:
return None
# EOF
The 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 self
at the end.method_name(self, ...)
: Dynamic instance method.
Method that requiresself
content in order to work or is used to updateself
content.Static instance method:
Method that does not require or updateself
content.
@staticmethod
def method_name(...): # no self in input
...
= Consumer(params=[1,1])
consumer
consumer.params2,3))
consumer.preference((2,3)) consumer.validate_c((
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.
= Consumer((1,1)) # preference: x*y
consumer11 = Consumer((3,5)) # preference: x**3 * y**5 consumer35
All objects when created are an instance of some class.
= 5
int0 = 5.5
float0 = "hello"
char0
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 str
is 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
= (5, 7)
prices
# difference
= 100
I = (1, 1)
params
def preference(params, x):
= x
x1, x2 = params
alpha, beta = x1**alpha * x2**beta
u return u
def validate_c(I, x):
= x
x1, x2 = prices
p_x1, p_x2 if p_x1*x1 + p_x2*x2 <= I:
return x
else:
return None
The key to build a class is:
- heterogeneous parameters should be defined as
self.xxx
and 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
self
as its first input.when method requires no heterogeneous parameters, it should be a static method without
self
input.
Heterogenous parameters are called instance variables (aka instance attributes). Homogeneous parameters are called class variables (aka class attributes).
class Consumer:
# same prices
= (5, 7)
prices
# difference
def __init__(self, I, params):
self.I = I
self.params = params
def preference(self, x):
= x
x1, x2 = self.params # heterogeneous parameters
alpha, beta = x1**alpha * x2**beta
u return u
def validate_c(self, x):
= x
x1, x2 = Consumer.prices # homogeneous parameters
p_x1, p_x2 if p_x1*x1 + p_x2*x2 <= self.I:
return x
else:
return None
# EOF
= Consumer(100, (1,1))
consumer11_100 = Consumer(250, (3,5)) consumer35_250
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.params
Consumer.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.
= (3,3)
Consumer.prices
consumer11_100.prices consumer35_250.prices
- Changing class variable values through Class changes all instances’ facing such values.
# change an instance's class variable
= (5, 5)
consumer11_100.prices
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:
# the instance variable **prices**
consumer11_100.prices # the class variable **prices** consumer11_100.__class__.prices
consumer35_250.prices
will prompt Python to look up prices as an instance variable within
consumer35_250
instance.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
= (10, 2)
params = Demand(params) # generate a demand instance demand
where
2) demand.q(
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
= runif(n=5, 0, 0.5)
rns_noSeed print(rns_noSeed)
# with seed
set.seed(2020)
= runif(n=5, 0, 0.5)
rns_seed 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_rng
= default_rng() # no seed
rng_noSeed = rng_noSeed.uniform(50, 100, size=5)
rns_noSeed print(rns_noSeed)
= default_rng(2038) # with seed 2038
rng_seed = rng_seed.uniform(50, 100, size=5)
rns_seed 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:
2, ind_demands) aggregate_demand(
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(width=2, height=10)
square # shows 2
square.width # shows 10
square.height # shows 20 square.area()
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:
= (2, 3, 5)
alpha1, alpha2, rho = (alpha1, alpha2, rho)
params = CES(params)
ces = (1, 10)
x # show the utility levels of x given alpha1, alpha2, rho preference parameter values ces.U(x)
4.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\]
= 2
b = QL(q, b)
ql = (2, 5)
x # returns the utility levels of x given q(x1) and b ql.U(x)
zip
from numpy.random import default_rng
= default_rng(293)
rng1 = default_rng(283)
rng2 = zip(rng1.uniform(50,100, size=3), rng2.uniform(2,10, size=3))
rng_zip list(rng_zip)
=5
p= [ alpha - beta*p for alpha, beta in rng_zip] qs
rng_zip
is 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:
= default_rng(293)
rng1 = default_rng(283)
rng2 = [ alpha - beta*p for alpha, beta in zip(rng1.uniform(50, 100, size=3), rng2.uniform(2, 10, size=3))]
qs qs
zip()
zips columns of vectors into 1 column of tuples.
= ["a", "b", "c"]
X = [2, 3, 5]
Y = zip(X, Y) zip_xy
list(zip_xy)
zip_xy
is iterable.
= iter(zip_xy)
iter_xy next(iter_xy)
= ["a", "b", "c"]
X = [2, 3, 5]
Y = { key: value for (key, value) in zip(X, Y)}
dict_xy print(dict_xy)