For this lecture you can focus on the mathematics and data manipulations.
The code is there to help you better understand the math and statistics.
You do not need to understand all of the code!
I will indicate which code components are important.
# Packages for this lecture
using CSV
using DataFrames
using Downloads
using ForwardDiff
using LinearAlgebra
using Plots
using Random
using Roots
using Statistics
using Symbolics
using Zygote
In this section we discuss basic principles from the DataFrames.jl
package.
For the first topic we look at how to construct and access DataFrames.
The fundamental object that we care about is the DataFrame
.
This is similar to a dataframe
that you would find in R or in Pandas (Python).
DataFrames are essentially matrices, with the rows being observations and the columns indicating the variables.
The easiest thing to construct is an empty DataFrame.
DataFrame() # empty DataFrame
0 rows × 0 columns
You could also construct a DataFrame with different keyword arguments.
Notice the different types of the different columns.
DataFrame(A = 2:5, B = randn(4), C = "Hello")
4 rows × 3 columns
A | B | C | |
---|---|---|---|
Int64 | Float64 | String | |
1 | 2 | 0.716393 | Hello |
2 | 3 | -0.135105 | Hello |
3 | 4 | -0.657844 | Hello |
4 | 5 | 0.924146 | Hello |
You can also create a DataFrame from a matrix,
x = DataFrame(rand(3, 3), :auto) # automatically assign column names with :auto
3 rows × 3 columns
x1 | x2 | x3 | |
---|---|---|---|
Float64 | Float64 | Float64 | |
1 | 0.724487 | 0.778178 | 0.210157 |
2 | 0.205457 | 0.358746 | 0.288093 |
3 | 0.653048 | 0.885845 | 0.886389 |
We will often work with large datasets in economics.
y = DataFrame(rand(1:100, 1000, 10), :auto)
1,000 rows × 10 columns
x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | |
---|---|---|---|---|---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | |
1 | 24 | 22 | 30 | 25 | 53 | 50 | 53 | 55 | 19 | 5 |
2 | 37 | 59 | 29 | 65 | 23 | 100 | 4 | 50 | 66 | 46 |
3 | 2 | 45 | 99 | 53 | 38 | 80 | 69 | 10 | 9 | 52 |
4 | 76 | 33 | 65 | 100 | 67 | 87 | 48 | 18 | 38 | 87 |
5 | 11 | 29 | 79 | 41 | 68 | 25 | 87 | 34 | 53 | 18 |
6 | 39 | 67 | 14 | 66 | 76 | 64 | 28 | 49 | 64 | 62 |
7 | 95 | 24 | 60 | 35 | 13 | 37 | 90 | 69 | 85 | 94 |
8 | 10 | 47 | 70 | 61 | 57 | 60 | 5 | 50 | 24 | 75 |
9 | 25 | 25 | 51 | 73 | 99 | 75 | 45 | 70 | 81 | 67 |
10 | 76 | 13 | 23 | 45 | 56 | 43 | 45 | 63 | 2 | 35 |
⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
We can look at the first few rows using the first
function.
first(y, 5) # first 5 rows
5 rows × 10 columns
x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | |
---|---|---|---|---|---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | |
1 | 24 | 22 | 30 | 25 | 53 | 50 | 53 | 55 | 19 | 5 |
2 | 37 | 59 | 29 | 65 | 23 | 100 | 4 | 50 | 66 | 46 |
3 | 2 | 45 | 99 | 53 | 38 | 80 | 69 | 10 | 9 | 52 |
4 | 76 | 33 | 65 | 100 | 67 | 87 | 48 | 18 | 38 | 87 |
5 | 11 | 29 | 79 | 41 | 68 | 25 | 87 | 34 | 53 | 18 |
There are multiple ways to access particular columns of the DataFrame.
The most obvious way is to to use y.col
.
Here col
stands for the column name.
This provides us the column in vector format.
y.x2 # get a single column
1000-element Vector{Int64}: 22 59 45 ⋮ 51 77
y[!, :x2] # or y[!, 2] or y[:, :x2]
1000-element Vector{Int64}: 22 59 45 ⋮ 51 77
You can access several columns as follows,
y[:, [:x1, :x2]]
1,000 rows × 2 columns
x1 | x2 | |
---|---|---|
Int64 | Int64 | |
1 | 24 | 22 |
2 | 37 | 59 |
3 | 2 | 45 |
4 | 76 | 33 |
5 | 11 | 29 |
6 | 39 | 67 |
7 | 95 | 24 |
8 | 10 | 47 |
9 | 25 | 25 |
10 | 76 | 13 |
⋮ | ⋮ | ⋮ |
Getting rows is also quite easy,
y[1, :]
DataFrameRow (10 columns)
x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | |
---|---|---|---|---|---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | Int64 | |
1 | 24 | 22 | 30 | 25 | 53 | 50 | 53 | 55 | 19 | 5 |
I have created a dataset that is hosted on GitHub.
Now let us download this data with Julia.
Downloads.download(
"https://raw.githubusercontent.com/DawievLill/Macro-318/main/data/tut2_data.csv",
"tut2_data.csv"
)
"tut2_data.csv"
Next we read the CSV file into Julia and make it a DataFrame.
sa_data = DataFrame(CSV.File("tut2_data.csv", dateformat = "yyyy/mm/dd")) # specify the date format
71 rows × 5 columns
date | gdp | repo | cpi | inflation | |
---|---|---|---|---|---|
Date | Int64 | Float64 | Float64 | Float64 | |
1 | 2004-03-31 | 1571580 | 8.0 | 51.1689 | -2.05565 |
2 | 2004-06-30 | 1640953 | 8.0 | 51.4474 | -2.02507 |
3 | 2004-09-30 | 1674699 | 7.66667 | 51.5677 | -1.00851 |
4 | 2004-12-31 | 1731000 | 7.5 | 51.8483 | 1.62524 |
5 | 2005-03-31 | 1768828 | 7.5 | 52.1796 | 1.97509 |
6 | 2005-06-30 | 1803783 | 7.0 | 52.456 | 1.9603 |
7 | 2005-09-30 | 1873743 | 7.0 | 52.8125 | 2.41398 |
8 | 2005-12-31 | 1918423 | 7.0 | 52.9391 | 2.10385 |
9 | 2006-03-31 | 1960150 | 7.0 | 53.243 | 2.03793 |
10 | 2006-06-30 | 2048534 | 7.16667 | 53.7536 | 2.47365 |
⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
For this example we are going to look at GDP.
GDP is the second column in the dataset.
We can access GDP by calling the variable name.
However, we can also use the fact that it is located in the second column of the table.
gdp = sa_data.gdp # or we could have done gdp = sa_data[!, 2]
71-element Vector{Int64}: 1571580 1640953 1674699 ⋮ 5632802 5819983
date_sa = sa_data[!, :date] # we also want the date column for our plot
71-element Vector{Dates.Date}: 2004-03-31 2004-06-30 2004-09-30 ⋮ 2021-06-30 2021-09-30
One popular transformation of GDP data is to take a natural logarithm.
The reason for this is that differences between adjacent values in the GDP series represent growth rates once the series is "logged".
We will get back to this point at a later stage.
If we take a natural log of the series then the plot of GDP looks as follows.
plot(date_sa, log.(gdp), legend = false, lw = 2, color = :blue, alpha = 0.8)
inflation = sa_data[!, :inflation]
plot(date_sa, inflation, legend = false, lw = 2, color = :blue, alpha = 0.8)
plot!([0], legend = false, lw = 1.5, seriestype = :hline, color = :black, ls = :dash, alpha = 0.5)
repo = sa_data[!, :repo]
plot(date_sa, inflation, legend = false, lw = 2, color = :blue, alpha = 0.6)
plot!(date_sa, repo, legend = false, lw = 2, color = :red, alpha = 0.8, ls = :dashdot)
plot!([0], legend = false, lw = 1.5, seriestype = :hline, color = :black, ls = :dash, alpha = 0.5)
mean(gdp) # The average
3.7277526338028167e6
std(gdp) # The standard deviation
1.3124105578634653e6
describe(sa_data)
5 rows × 7 columns
variable | mean | min | median | max | nmissing | eltype | |
---|---|---|---|---|---|---|---|
Symbol | Union… | Any | Any | Any | Int64 | DataType | |
1 | date | 2004-03-31 | 2012-12-31 | 2021-09-30 | 0 | Date | |
2 | gdp | 3.72775e6 | 1571580 | 3.71359e6 | 5819983 | 0 | Int64 |
3 | repo | 6.73709 | 3.5 | 6.66667 | 12.0 | 0 | Float64 |
4 | cpi | 82.2662 | 51.1689 | 80.0 | 122.233 | 0 | Float64 |
5 | inflation | 4.83013 | -2.05565 | 4.86163 | 11.0829 | 0 | Float64 |
Histogram gives general idea of what the distribution for inflation looks like,
histogram(inflation, legend = false, alpha = 0.5, bins = 20)
plot!([mean(inflation)], seriestype = :vline, lw = 3, colour = :black, ls = :dash) # plots the mean value
Computing growth rates is quite important in macroeconomics.
This is something that you will frequently encounter.
The growth rate between subsequent two dates can be calculated as follows,
$$ \left(\frac{Y_{t} - Y_{t-1}}{Y_{t-1}}\right) \times 100 = \left(\frac{Y_{t}}{Y_{t-1}} - 1\right) \times 100 $$The growth rate from one quarter to the same quarter next year is,
$$ \left(\frac{Y_{t}}{Y_{t-4}} - 1\right) \times 100 $$For monthly or quarterly growth rates at an annual rate,
$$ \left(\left[\frac{Y_{t}}{Y_{t-1}}\right]^{n} - 1\right) \times 100 $$where $n = 4$ represents quarterly growth and $n = 12$ gives monthly growth.
Let us try calculating some growth rates with the data at hand.
We will focus on GDP growth rates for this example,
gdp_first = sa_data[1, :gdp]; # first value of the GDP series
gdp_second = sa_data[2, :gdp]; # second value of the GDP series
gdp_growth_1 = ((gdp_second - gdp_first)/gdp_first) * 100 # using the formula for growth between two periods.
4.414220084246427
There is an alternative way to calculate an approximation to the growth rate.
We can simply take the natural logarithm of the two values and subtract them from each other.
In other words we have that,
$$ \left(\frac{Y_{t}}{Y_{t-1}} - 1\right) \times 100 \approx \log(Y_{t}) - \log(Y_{t-1}) \times 100 $$(log(gdp_second) - log(gdp_first)) * 100
4.319568788852912
A function is a rule that assigns to every element of $x \in X$ a single element of the set $Y$.
This is written as,
$$ f:X \rightarrow Y $$When we write $y = f(x)$ we are mapping from the argument $x$ in the domain $X$ to a value in the co-domain $Y$.
It is important to note that for a function we are assigning a single element from the set $X$ to the set $Y$.
x = 0:π/100:2π
y = sin.(x)
plot(x, y, title = "This is a function", legend = false, lw = 2)
x = 1; y = 1; r = 1
θ = 0:π/50:2π
x_unit = r .* cos.(θ) .+ x
y_unit = r .* sin.(θ) .+ y
plot(x_unit, y_unit, title = "This is NOT a function", legend = false, lw = 2)
m = 0.5; b = 1;
ar_x = LinRange(-5, 10, 100); ar_y = ar_x .* m .+ b;
plot(ar_x, ar_y, legend = false, title = "Linear function with slope $m and intercept $b", lw = 2)
vline!([0, -2], ls = :dash, color = :black, alpha = 0.5, xticks = ([0, -2]))
hline!([0, 1], ls = :dash, color = :red, alpha = 0.5, yticks = ([0, 1]))
A function $f$ has a global maximum at $x^{*} \in X$ if for all $x \in X$, $f(x) \leq f(x^{*})$.
A function $f$ has a local maximum at $x^{*} \in X$ if there exists and open interval $(a, b)$ such that $x^{*} \in (a, b)$, and for all $x \in (a, b), f(x) \leq f(x^{*})$.
Not all functions have a maximum.
We are going to be dealing with functions such as utility functions, production functions and budget constraints in many of our economics models.
As an example, in the case of a utility function the household is choosing the bundle of goods that provides the highest level of utility.
Most production and utility functions will NOT have a local or global maximum.
In the case of a log-utility function, higher consumption will give higher levels of utility, so no maximum exists here either.
There is one nice utility function that has a maximum, the quadratic utility function.
$$ U(x) = x - \alpha \cdot x^2 $$We can quickly draw a plot of this utility function and by inspection determine where the maximum is going to be.
The most efficient way to determine the maximum would be using a derivative and setting it equal to zero.
However, we haven't introduced derivatives yet, so let us use this inefficient way to determine the maximum for now.
npoints = 100; a, b = (-10, 10); α = 0.2;
x = range(a, b, length = npoints)
U(x) = x .- α .* x .^ 2
plot(x, U(x), legend = false, lw = 2, title = "Can you find the max?!")
fmax, ix = findmax(U.(x))
(1.249872461993674, 63)
scatter!([x[ix]], [fmax], color = :red, ms = 5)
vline!([x[ix]], lw = 1.5, color = :black, ls = :dash, alpha = 0.5, xticks = ([x[ix]]))
What is the rate of change for a function as we move along its domain?
The derivative of $f$ at the point $x_0$ is the slope of the tangent line to the graph of $f$ at $(x_0, f(x_0))$.
$$ f'(x_0) = \frac{df(x_0)}{dx} = f_{x}(x_0) $$The derivative is defined formally as,
$$ f'(x_0) = \lim_{h \rightarrow 0} \frac{f(x_{0} + h) - f(x_0)}{h} $$In taking derivatives we normally revert to the derivative rules.
It is important to note that there are several ways in which you can take derivatives on the computer.
The primary methods are,
We will not be going into detail on how these methods work. We will just use them in practice.
If $f(x) = k$ where $k$ is some constant then $f'(x_0) = 0$.
As an application of the constant rule, let us determine the derivative of $f(x) = 8$.
$$ f'(x_0) = 0 $$This is the easiest rule and can be computed in one line.
Let us see what the computer does with this example.
@variables x
D = Differential(x)
y = 8
D(y)
expand_derivatives(D(y))
0
For any positive integer $k$ the derivative of $f(x) = x^{k}$ at $x_0$ is,
$$ f'(x_0) = k \cdot x_{0}^{k - 1} $$Find the derivative for
$$ f(x) = x ^ 3 $$What do you think the answer should be? Let us check with the computer to see what we get.
y = x ^ 3
D(y)
expand_derivatives(D(y))
f(x) = x ^ 3
f'(x)
The chain rule is a bit more complicated then some of the other rules, but it is used frequently in economics.
If we have a function $f(x) = p(q(x))$ then the derivative at $x_0$ according to the chain rule is,
$$ f^{\prime } (x_0 )=p^{\prime } (q(x_0 ))\cdot q^{\prime } (x_0 ) $$A good example of where this is applicable is the function
$$ f(x) = \sqrt{(5x - 8)} $$In this case we have a composition of two functions, $p(x) = \sqrt{x}$ and $q(x) = 5x - 8$.
Here we need to take the derivatives separately and combine with our rule.
We start with $p'(x)$.
We haven't really talked about taking the derivative of a square root.
So how do we proceed?
Well, our square root can actually be written as $p(x)^{1/2}$.
Now we can use our power rule from before to get $p'(x) = \frac{x^{-1/2}}{2}$.
Next we need to take the derivative of $q(x)$.
This gives us $q'(x) = 5$ using the power rule (since $5 = 5 \cdot x^{(1 - 1)}$).
Now we combine all the components as per the chain rule,
$$ \begin{align*} f'\left( x \right) & = p'\left( {q\left( x \right)} \right)\,\,q'\left( x \right)\\ & = p'\left( {5x - 8} \right)\,\,q'\left( x \right)\\ & = \frac{1}{2}{\left( {5x - 8} \right)^{ - \frac{1}{2}}}\,\left( 5 \right)\\ & = \frac{1}{{2\sqrt {5x - 8} }}\,\,\left( 5 \right)\\ & = \frac{5}{{2\sqrt {5x - 8} }}\end{align*} $$y = sqrt(5x - 8)
D(y)
expand_derivatives(D(y))
f(x) = sqrt(5x - 8)
f'(x)
Given functions $p$ and $q$ that are differentiable at $x$ and with $f(x) = p(x) + q(x)$ we have that the derivative according to the sum rule is given by,
$$ f^{\prime } (x)=p^{\prime } (x)+q^{\prime } (x) $$Let us consider an example with the application of this rule.
Find the derivative of $f(x) = 2x^5 + 7$.
In this case, $p(x) = 2x^5$ and $q(x) = 7$.
y = 2x^5 + 7
D(y)
expand_derivatives(D(y))
f(x) = 2x^5 + 7
f'(x)
Given functions $p$ and $q$ that are differentiable at $x$ and with $f(x)=p(x)\cdot q(x)$ we have that the derivative is,
$$ f^{\prime } (x)=p^{\prime } (x)\cdot q(x)+p(x)\cdot q^{\prime } (x) $$Consider the following example, find derivative of $f(x) = (x^2+2)(3x^3−5x)$ by applying the product rule.
First, set $p(x)=x2+2$ and $q(x)=3x3−5x$.
Then $p'(x)=2x$ and $q'(x)=9x2−5$ and therefore,
$$ f'(x)=p'(x)q(x)+q'(x)p(x)=(2x)(3x^3−5x)+(9x^2−5)(x^2+2) $$If we simplify this we have $f'(x) = 15x4+3x2−10$
y = (x^2 + 2)*(3x^3 − 5x)
D(y)
expand_derivatives(D(y))
f(x) = (x^2 + 2)*(3x^3 − 5x)
f'(x)
The exponential function is used a lot in economics.
We will often have functions such as $f(x) = \exp(a \cdot x)$.
The derivative in this case is given by,
$$ f'(x) = a \cdot \exp(a \cdot x) $$Finally, we have the derivative of a log function, which is used almost everywhere in economics.
If we have the function $f(x) = \log(x)$ then the derivative is given by,
$$ f'(x) = 1 / x $$With these rules you should be able to tackle most problems that involve derivatives.
These derivatives that we calculated were first order derivatives.
We can actually take the derivative again with respect to the variable of interest and then we would have second order derivatives.
The second order derivative is then a derivative of the first order derivative.
While the first order derivative provides a rate of change (often a slope of a tangent line), the second order derivative gives us information on the rate of change of the rate of change.
Previously we looked at the definitions for global and local maxima.
In the univariate case the first and second order necessary condition for a local maxima is the following,
An easier way to determine the turning points for functions to use the first and second order conditions.
Say that we want to determine the minima and maxima of the following function,
$$ f(x) = 200 - 30x + 8x^2 - 1/2x^3 $$How would we do this?
First let us plot the function to see what it looks like.
f(x) = 200 - 30 * x + 8 * x^2 - 0.5 * x^3
plot(f, 0, 12, lw = 2, legend = false)
It appears from the graph that there are going to be two turning points.
We want to calculate the local maximum in the interval $[0, 12]$.
First we take the first order condition to find the place where the slope is zero.
Then we look at the second order condition to determine if it is a maximum or minimum.
For this we are going to be using a root finding package in Julia, called Roots
.
roots = find_zeros(f', (0, 12)) # we are essentially setting f'(x) = 0 with this package.
2-element Vector{Float64}: 2.427400704306218 8.239265962360449
f''(roots[1]), f''(roots[2])
(8.717797887081346, -8.717797887081346)
This first value is positive, so we don't believe that this is a maximum.
The second value is negative, so we believe that the second turning point is our maximum.
plot(f, 0, 12, lw = 2, legend = false)
vline!([roots[1], roots[2]], lw = 2, alpha = 0.6, ls = :dash, color = :black, xticks = ([roots[1], roots[2]]))
Here we talk about how to deal with functions $f: \mathbb{R}^{n} \rightarrow \mathbb{R}$.
This means functions that take values in $\mathbb{R}^n$ and return a value along the real line.
An example of this would be,
$$ f(x, y) = x^{2} + y^{2} $$This is a function that takes in two inputs and returns one output.
We will mostly be working with functions that have two inputs.
The following provides the plot for the function $f(x, y) = x^{2} + y^{2}$.
mmplotter(:max)
Below is a contour plot, which shows a top down view of the surface plot above.
mmcontour(:max)
For a function $f: \mathbb{R}^{n} \rightarrow \mathbb{R}$ the concept of a derivative is extended to partial derivatives for $n$ variables.
The partial of $x$ is defined by holding $y$ constant while a derivative in $x$ is taken.
The partial derivative of a function $f$ with respect to the variable $x$, can be written as $\frac{\partial{f}}{\partial{x}}$, which is defined as,
$$ \dfrac{∂f}{∂x}=f_x(x,y)=\lim_{h→0}\dfrac{f(x+h,y)−f(x,y)}{h} $$In this definition we see a new symbol, $\partial$, which indicates a partial derivative.
In this case we can also write the derivative with respect to $y$ so there are two partial derivatives for this function, since it is a function of two variables.
The gradient of $f$, which is referred to as $\nabla f$, is the vector valued function of partial derivatives $[\partial f / \partial x, \partial f / \partial y]$.
Let us look at an example.
Say that we wanted to calculate the partial derivative for some function,
$$ f(x,y)=x^2−3xy+2y^2−4x+5y−12 $$The easiesy way to take a partial derivative of $f$ with respect to $x$ would be to treat the variable $y$ like it was a constant and apply the usual single variable calculus rules.
Let us consider the individual components of the function.
In the case of $x^{2}$, we would have that the derivative is $2x$.
For $-3xy$ we treat the $y$ like a constant, so the derivative is $-3y$.
The next term, $2y^2$ falls away completely since it is constant. The same is true for $5y$ and $12$.
Finally, we are left with $-4x$, with a derivative given by $-4$.
If we then add these components together, we get our answer $2x - 3y - 4$
@variables x y
l = x^2 − 3x*y + 2y^2 − 4x + 5y − 12
H = Differential(x);
I = Differential(y);
expand_derivatives(H(l)) # partial derivative of f(x, y) wrt to x
expand_derivatives(I(l)) # partial derivative of f(x, y) wrt to y