Julia 101: uma Abordagem Regional

  1. Pacotes Necessários

Primeiramente, vamos instalar e ler as bibliotecas necessárias para o workshop

In [1]:
using Pkg # pacote para instalar novos pacotes
using LinearAlgebra # pacote de Álgebra Linear
using SpatialDependence # pacote de análise de dados espaciais
using Optim # pacote com funções básicas de otimização
using JuMP,Ipopt # pacote com algoritmos de otimização com restrições
using Random # para criar vetores, matrizes e arranjos aleatórios
using Shapefile # para trabalhar com shapefiles
using Plots # para plot de shapefiles simples
using Jenks # para quebra natural de classes em jenks
using StableRNGs # para travar o gerador de números aleatórios na análise espacial
using DataFrames # para usar base de dados em formatos retangulares
using ForwardDiff # para calcular segundas derivadas
using ProgressBars # para barras de progresso
using PrettyTables # para criar tabelas apresentáveis
using Statistics # para funções estatísticas

Como instalamos um novo pacote na Julia? Usando o comando Pkg.add().

  1. Operações Matriciais Básicas

Primeiramente, criemos uma matriz de números aleatórios, distribuídos conforme U[0,1].

In [2]:
Random.seed!(12345)
A=rand(2,2)
2×2 Matrix{Float64}:
 0.944791  0.339612
 0.866895  0.136117

Alguém se lembra da fórmula para criar números distribuídos uniformemente a partir de uma U[0,1]?

In [3]:
B=rand(1.0:10.0,2,2)
C=rand(1:10,2,2)
B,C
([4.0 7.0; 3.0 8.0], [4 1; 6 9])

Por que B é do tipo float, enquanto C é do tipo int? Aqui a typagem forte da Julia fica mais explícita. Observe que a matriz A anterior também é float, pois ela puxa a distribuição uniforme [0.0,1.0]. Vamos agora realizar duas operações que são muito comuns na Econometria: inversão de matrizes e encontrar seus auto-valores.

In [4]:
inv(C)
2×2 Matrix{Float64}:
  0.3  -0.0333333
 -0.2   0.133333
In [5]:
eigen(A)
Eigen{Float64, Float64, Matrix{Float64}, Vector{Float64}}
values:
2-element Vector{Float64}:
 -0.1362265186681017
  1.217133601568467
vectors:
2×2 Matrix{Float64}:
 -0.299717  0.780136
  0.954028  0.62561
  1. Algoritmos de Otimização

Vamos agora explorar um algoritmo otimização não condicional na Julia. Considere a segunte função: $y=-(x_{1}^2+x_{2}^2+x_{3}^{2})$

In [6]:
function f(x)
    return +(x[1]^2 + x[2]^2 + x[3]^2)
end
# Usando agora alguns pontos iniciais|
x0 = [1.0, 1.0, 1.0]
# Realizando agora a otimização
resultado = optimize(f, x0)
resultado
 * Status: success

 * Candidate solution
    Final objective value:     2.833427e-09

 * Found with
    Algorithm:     Nelder-Mead

 * Convergence measures
    √(Σ(yᵢ-ȳ)²)/n ≤ 1.0e-08

 * Work counters
    Seconds run:   0  (vs limit Inf)
    Iterations:    67
    f(x) calls:    123

Para ver os pontos que minimizam a função, basta usar o qualificador minimizer

In [7]:
resultado.minimizer
3-element Vector{Float64}:
 -5.0823878310249317e-5
  9.262030970117879e-6
 -1.2828696727829803e-5

Para ver o valor máximo que sua função adquire, baste usar o qualificador minimum

In [8]:
resultado.minimum
2.8334272839210848e-9

Um detalhe importante é que os algoritmos de otimização do pacote Optim minimizam sua função a ser otimizada. Então, é importante garantir que ela tem solução e que essa função entra no processo de otimização de forma correta. Como uma otimização não condicionada funcionaria. Usemos o caso de uma função de utilidade do tipo Cobb-Douglas simples para entender a dinâmica do código. Aqui, vamos usar o pacote JUMP que permite otimizações com restrições

In [9]:
# Criando o modelo (vazio)
modelo = Model(Ipopt.Optimizer)

# Definir as variáveis - não-negativas
@variable(modelo, x1 >= 0)
@variable(modelo, x2 >= 0)

# Define the objective function to maximize
@objective(modelo, Max, (x1^2 + x2^2))

# Add linear constraints
@constraint(modelo, 2*x1 + 5*x2 <= 100.0)

# Optimize the model
optimize!(modelo)
******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
 Ipopt is released as open source code under the Eclipse Public License (EPL).
         For more information visit https://github.com/coin-or/Ipopt
******************************************************************************

This is Ipopt version 3.14.10, running with linear solver MUMPS 5.5.1.

Number of nonzeros in equality constraint Jacobian...:        0
Number of nonzeros in inequality constraint Jacobian.:        2
Number of nonzeros in Lagrangian Hessian.............:        2

Total number of variables............................:        2
                     variables with only lower bounds:        2
                variables with lower and upper bounds:        0
                     variables with only upper bounds:        0
Total number of equality constraints.................:        0
Total number of inequality constraints...............:        1
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        1

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  1.9999960e-04 0.00e+00 7.29e-01  -1.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1  2.5067536e-02 0.00e+00 2.18e-01  -1.0 7.13e-01    -  8.20e-01 1.00e+00f  1
   2  2.6040386e-02 0.00e+00 6.02e-01  -1.0 6.06e-03   2.0 9.95e-01 1.00e+00f  1
   3  6.7549927e-02 0.00e+00 3.92e-01  -1.7 4.75e-01    -  6.92e-01 1.00e+00f  1
   4  6.9927597e-02 0.00e+00 2.84e-01  -1.7 8.51e-03   1.5 1.00e+00 1.00e+00f  1
   5  7.9112567e-02 0.00e+00 3.21e-01  -1.7 2.89e-02   1.0 1.00e+00 1.00e+00f  1
   6  1.5617712e-01 0.00e+00 5.58e-01  -1.7 1.51e-01   0.6 1.00e+00 1.00e+00f  1
   7  2.0927484e-01 0.00e+00 6.91e-01  -1.7 6.99e-02   1.0 1.00e+00 1.00e+00f  1
   8  4.5448940e-01 0.00e+00 1.02e+00  -1.7 3.95e-01   0.5 1.00e+00 5.68e-01f  1
   9  9.7151939e-01 0.00e+00 8.28e-01  -1.7 7.55e-01   0.0 1.00e+00 1.00e+00f  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  10  1.4369612e+00 0.00e+00 8.93e-01  -1.7 3.05e-01   0.5 1.00e+00 1.00e+00f  1
  11  4.1529868e+00 0.00e+00 1.63e+00  -1.7 1.67e+00  -0.0 1.00e+00 1.00e+00f  1
  12  5.8013089e+00 0.00e+00 1.93e+00  -1.7 7.42e-01   0.4 1.00e+00 1.00e+00f  1
  13  2.0054598e+01 0.00e+00 3.58e+00  -1.7 4.13e+00  -0.1 1.00e+00 1.00e+00f  1
  14  2.9306227e+01 0.00e+00 4.33e+00  -1.7 1.87e+00   0.4 1.00e+00 1.00e+00f  1
  15  1.0431182e+02 0.00e+00 8.17e+00  -1.7 1.17e+01  -0.1 8.15e-01 8.21e-01f  1
  16  1.6079098e+02 0.00e+00 1.01e+01  -1.7 4.94e+00   0.3 1.00e+00 1.00e+00f  1
  17  5.6248055e+02 0.00e+00 1.90e+01  -1.7 3.56e+01  -0.2 6.21e-01 6.20e-01f  1
  18  9.2200033e+02 0.00e+00 2.43e+01  -1.7 1.33e+01   0.3 1.00e+00 1.00e+00f  1
  19  2.4803885e+03 0.00e+00 3.98e+01  -1.7 1.16e+02  -0.2 4.65e-01 3.34e-01f  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  20  2.4997886e+03 0.00e+00 6.43e+01  -1.7 3.25e+01   0.2 1.00e+00 1.20e-02f  1
  21  2.4999829e+03 0.00e+00 5.29e+01  -1.7 1.24e+00    -  1.00e+00 3.14e-03f  1
  22  2.4999603e+03 0.00e+00 2.00e-07  -1.7 3.56e-04    -  1.00e+00 1.00e+00f  1
  23  2.4999998e+03 0.00e+00 1.50e-09  -3.8 3.94e-04    -  1.00e+00 1.00e+00f  1
  24  2.5000000e+03 0.00e+00 1.84e-11  -5.7 2.97e-06    -  1.00e+00 1.00e+00f  1
  25  2.5000001e+03 0.00e+00 5.68e-14  -8.6 3.68e-08    -  1.00e+00 1.00e+00f  1

Number of Iterations....: 25

                                   (scaled)                 (unscaled)
Objective...............:  -2.5000000524949887e+03    2.5000000524949887e+03
Dual infeasibility......:   5.6843418860808015e-14    5.6843418860808015e-14
Constraint violation....:   0.0000000000000000e+00    0.0000000000000000e+00
Variable bound violation:   9.9899763890181942e-09    9.9899763890181942e-09
Complementarity.........:   2.5060842553385023e-09    2.5060842553385023e-09
Overall NLP error.......:   2.5060842288585504e-09    2.5060842553385023e-09


Number of objective function evaluations             = 26
Number of objective gradient evaluations             = 26
Number of equality constraint evaluations            = 0
Number of inequality constraint evaluations          = 26
Number of equality constraint Jacobian evaluations   = 0
Number of inequality constraint Jacobian evaluations = 1
Number of Lagrangian Hessian evaluations             = 1
Total seconds in IPOPT                               = 0.138

EXIT: Optimal Solution Found.

Vamos agora extrair nossos objetos de interesse. O resultado indica que o consumidor só consome o bem 1, mais barato e que lhe produz tanta utilidade quanto o bem 1

In [11]:
value(x1),value(x2),objective_value(modelo)
(50.00000052494988, -9.989976389018194e-9, 2500.0000524949887)

Em algumas situações, é possível transferir um problema de otimização com restrições em algum outro problema de otimização. Logo, não é sempre necessário usar o pacote JUMP, bastando o próprio Optim. Ao mesmo tempo, há situações de otimizações numéricas mais complexas que o pacote JUMP não é suficiente, contudo há uma grande quantidade de pacotes para essa finalidade no escossistema da Julia

  1. Análise Exploratórias de Dados Espaciais

Agora, vamos usar o pacote SpatialDependece para analisar correlação espacial para algumas variáveis relacionadas a Belo Horizonte-MG. Criaremos matrizes espaciais e plotaremos alguns mapas neste exercício. Inicialmente, leiamos o shapefile necessário.

In [12]:
shape = Shapefile.Table("/Users/alanleal/Library/CloudStorage/OneDrive-Personal/Aulas/Julia 101 uma abordagem regional/Shapefiles/POP_DOMIC_BAIRRO_2010/POP_DOMIC_BAIRRO_2010.shp")
plot(shape)

Vamos agora criar uma matriz de pesos espacial

In [13]:
W = polyneigh(shape.geometry)
W
Spatial Weights 
Observations: 487 
Transformation: row
Minimum nunmber of neighbors: 0
Maximum nunmber of neighbors: 17
Average number of neighbors: 4.7803
Median number of neighbors: 5.0
Islands (isloated): 57
Density: 0.9816% 

Calculando agora o I de Moran

In [14]:
moran(shape.POPULACAO, W, permutations = 9999, rng = StableRNG(1234567))
Moran's I test of Global Spatial Autocorrelation
--------------------------------------------

Moran's I: 0.0603264
Expectation: -0.0020576

Randomization test with 9999 permutations.
 Mean: -0.0025178
 Std Error: 0.031551
 zscore: 1.9918235
 p-value: 0.0282

Plotando agora o I de Moran

In [15]:
plot(shape.POPULACAO, W, xlabel = "População")

Agora, calculando e plotando o LISA

In [16]:
lmpop = localmoran(shape.POPULACAO, W, permutations = 9999, rng = StableRNG(1234567))
plot(shape, lmpop, sig = 0.05, adjust = :fdr)

Mais detalhes, inclusive outros índices de (auto-)correlação espacial e diferentes matrizes espaciais, podem ser conferidos no seguinte link: https://javierbarbero.github.io/SpatialDependence.jl/stable/.

  1. Econometria Espacial - SAR

Nosso objetivo aqui é implementar um simples estimador da Econometria Espacial, qual seja o SAR. Vamos implementar três coisas inicialmente; (i) estimação pontual dos coeficientes de interesse; (ii) estimação intervalar dos coeficientes de interesse; (iii) efeitos diretos, indiretos e totais das observações

4.1. Leitura do Shapefile - Casos de Zika no Ceará e Mapas

Este shapefile foi obtido no seguinte link:https://geodacenter.github.io/data-and-lab/Ceara-Zika/

In [17]:
# Origem da base: https://geodacenter.github.io/data-and-lab/
ceara_zika=Shapefile.Table("/Users/alanleal/Library/CloudStorage/OneDrive-Personal/Aulas/Julia 101 uma abordagem regional/Shapefiles/ceara/ceara.shp")
plot(ceara_zika)

Num segundo momento, vamos plotar um mapa de cloropeth mais tradicional, qual seja um mapa de quantiles. Para tanto, criamos uma nova coluna identificando a qual quantil cada município do Ceará pertence.

In [18]:
df=DataFrame(ceara_zika)
quantiles = quantile(df.ln_gdp, 0:0.2:1)
function classify_quantiles(data, quantiles)
    return [findfirst(q -> x <= q, quantiles) for x in data]
end
df[!,:quantile_class] = classify_quantiles(df[!,:ln_gdp], quantiles)
plot(df.geometry, fill_z=reshape(df.quantile_class,1,nrow(df)), cbar=true, axis=false, ticks=false, size=(800, 600))
1 2 3 4 5 6

Agora, plotando o mesmo mapa cloropeth com quebras naturais (jenks)

In [19]:
# Transformando um vetor de missings e float64 em float64 apenas
mean_value = mean(skipmissing(df.ln_gdp))
df.ln_gdp = replace(df.ln_gdp, missing => mean_value)
plt_bounds=JenksClassification(5,df.ln_gdp).bounds
function classify_jenks(data, jenks)
    return [findfirst(q -> x <= q, jenks) for x in data]
end
df[!,:jenks_class] = classify_jenks(df[!,:ln_gdp], plt_bounds)
plot(df.geometry, fill_z=reshape(df.jenks_class,1,nrow(df)), cbar=true, axis=false, ticks=false, size=(800, 600))
100%: ARE=0.10351076, finished in 0.01s.
1 2 3 4 5 6

4.2. Criação de objetos de interesse

Inicialmente, criemos uma matriz normalizada de continguidade

In [20]:
W=polyneigh(ceara_zika.geometry) # matriz de contiguidade do fomato queen-default
# W = polyneigh(ceara_zika.geometry, criterion = :Rook) # matriz de contiguidade do fomato Rook
# W = dnearneigh(ceara_zika.geometry, threshold = 4.0) # matriz de inverso da distância para shapefile de PONTOS
# W = knearneigh(boston.geometry, k = 5) # matriz de k=5 vizinhos mais próximos para shapefile de PONTOS
W=Matrix(W) # transforma a matriz espacial em uma matrix usual
W
184×184 Matrix{Float64}:
 0.0  0.0  0.0       0.0  0.0  0.0  …  0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0  …  0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.166667  0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.2  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 ⋮                             ⋮    ⋱                  ⋮              
 0.0  0.0  0.0       0.0  0.0  0.0  …  0.25  0.25      0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.142857  0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.25  0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0  …  0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0
 0.0  0.0  0.0       0.0  0.0  0.0     0.0   0.0       0.0  0.0  0.0  0.0

Selecionando as variáveis para análise

In [21]:
n=184 # número de observações
y = ceara_zika.inc_zik_3q # jeito de selecionar uma variável do shapefile ceara_zika
df = DataFrame(
    constant=ones(n),
    ln_gdp = ceara_zika.ln_gdp,
    ln_pop=ceara_zika.ln_pop,
    mobility = ceara_zika.mobility,
    environ = ceara_zika.environ,
    sanitation = ceara_zika.sanitation
)
X = Matrix(select(df, [:constant,:ln_gdp,:ln_pop,:mobility, :environ, :sanitation]))
X
184×6 Matrix{Union{Missing, Float64}}:
 1.0  4.55599  4.02102  0.958  0.835  0.538
 1.0  4.83451  4.18577  0.941  0.644  0.728
 1.0  5.49065  4.76005  0.971  0.973  0.535
 1.0  5.31389  4.70893  0.975  0.885  0.687
 1.0  4.73446  4.2096   0.99   0.96   0.68
 1.0  4.57723  4.03226  1.0    0.932  0.798
 1.0  4.3909   3.83607  0.968  0.538  0.644
 1.0  4.88703  4.21376  0.972  0.905  0.573
 1.0  5.26288  4.59364  0.933  0.877  0.524
 1.0  4.45245  3.8441   0.956  0.926  0.752
 ⋮                                    ⋮
 1.0  4.71922  4.1586   0.95   0.859  0.564
 1.0  5.29099  4.50225  0.981  0.788  0.607
 1.0  4.46604  3.87766  0.976  0.837  0.625
 1.0  4.85375  4.2742   0.933  0.8    0.589
 1.0  5.14761  4.2959   0.943  0.906  0.612
 1.0  4.68884  4.11002  0.978  0.938  0.659
 1.0  4.99156  4.24534  0.98   0.917  0.586
 1.0  5.20945  4.58472  0.956  0.823  0.679
 1.0  5.3319   4.74001  0.973  0.77   0.557

Agora, criemos a função de log-verossimilhança do modelo SAR. Ele é dado por:

$l(\theta|y,X,W) \propto -(n/2)*log(\varepsilon'\varepsilon)+log(|I-\rho W|)$, com $\varepsilon=(I-\rho W)(y-X\beta)$. Com base nisso, criamos a seguinte função que avalia a log-verossimilhança do modelo SAR

In [22]:
function sar_likelihood(params)
  ρ, β = params[1], params[2:end]
  ε = (I(n) - ρ*W)*y-X*β
  loglik = ((-n/2)*log(ε'*ε))+log(det(I(n)-ρ*W))
  return -loglik # Negativo da log-verossimilhança para de fato maximizar essa função
end
sar_likelihood (generic function with 1 method)

Vejamos agora se a função de verossimilhança avalia os dados corretamente

In [23]:
sar_likelihood([0,0,0,0,0,0,0])
960.775240104947

Agora, vamos considerar os dados de Zika no Ceará para estimar um simples modelo espacial das variáveis que impactam os casos municipais de Zika no terceiro trimestre de 2019. Lembre-se que enquanto $\beta$ é livre, o parâmetro $\rho$ é limitado entre [-1,1], ou ainda entre $[min[-1/(min_{i}\{\lambda_{i}\})],1]$, com $min_{i}\{\lambda_{i}\}$ sendo o menor autovalor da matriz W de pesos espaciais. Ademais, no comando a seguir, temos quatro componentes, respectivamente: (i) parâmetros iniciais, para começo da busca; (ii) lower bounds, que indicam os valores mínimos assumidos por cada parâmetro; (iii) upper bounds, que indicam os valores máximos para cada parâmetro; (iv) result, que invoca a função optimize, que minimiza, do pacote Optim

In [24]:
initial_params = [0.5,0,0,0,0,0,0] # Initial values for ρ e β
lower_bounds = [-1, -Inf,-Inf,-Inf,-Inf,-Inf,-Inf]
upper_bounds = [1, Inf,Inf,Inf,Inf,Inf,Inf]
result = optimize(sar_likelihood,lower_bounds, upper_bounds,initial_params,Fminbox())
 * Status: success

 * Candidate solution
    Final objective value:     9.565912e+02

 * Found with
    Algorithm:     Fminbox with L-BFGS

 * Convergence measures
    |x - x'|               = 8.67e-08 ≰ 0.0e+00
    |x - x'|/|x'|          = 3.75e-09 ≰ 0.0e+00
    |f(x) - f(x')|         = 0.00e+00 ≤ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 0.00e+00 ≤ 0.0e+00
    |g(x)|                 = 9.39e-09 ≤ 1.0e-08

 * Work counters
    Seconds run:   1  (vs limit Inf)
    Iterations:    3
    f(x) calls:    133
    ∇f(x) calls:   133

Acessemos agora nossos coeficientes β, com a opção minimizer

In [25]:
β = result.minimizer
β
7-element Vector{Float64}:
   0.23790174678155787
  16.62706670088536
  -2.59958822275927
   1.2283227903363063
 -14.589927485596965
   5.229097057229507
   3.2474406362839607

Num segundo momento, calculemos agora a matriz de variância e covarância do SAR. Ela é dada por: $\hat{\omega}=\sigma^{2}(I-\rho W)^{-1}(I-\rho W')^{-1}$. Calculemos $\sigma^{2}$ inicialmente

In [26]:
haty=β[1]*W*y+X*β[2:end] #y estimado
sigma2=sum((y-haty).^(2))/(184-6) # erro amostral dos resíduos
sigma2
182.1539950249789

Por fim, calculando a matriz de variâncias e covariâncias dos parâmetros. Esse cálculo usa o cálculo de uma segunda derivada numericamente. Para detalhamento teórico, veja: https://www.researchgate.net/publication/229020633_Lecture_1_Maximum_likelihood_estimation_of_spatial_regression_models

In [27]:
σk = inv(ForwardDiff.hessian(sar_likelihood, β))
sqrt.(diag(σk))
7-element Vector{Float64}:
  0.11315758632823769
 39.88016539980641
  6.923082060731218
  8.970209233447832
 35.78905507302816
 11.160015889983043
 10.118056357643486

Vamos agora agora colocar os coeficientes com seus respectivos desvios-padrão numa única matriz

In [28]:
hcat(β,sqrt.(diag(σk)))
7×2 Matrix{Float64}:
   0.237902   0.113158
  16.6271    39.8802
  -2.59959    6.92308
   1.22832    8.97021
 -14.5899    35.7891
   5.2291    11.16
   3.24744   10.1181

4.3. Efeitos Diretos e Indiretos: Estimação Pontual

In [29]:
ψ=inv(I(n)-β[1]*W)
eft_direto=zeros(length(β)-1)
eft_indireto=zeros(length(β)-1)
eft_total=zeros(length(β)-1)
for i=2:length(β)
    eft_direto[i-1]=(1/n)*tr(inv(I(n)-β[1]*W))β[i]
    eft_total[i-1]=(1/n)*sum(sum(ψ[k,j]*β[i] for j=1:n) for k=1:n)
    eft_indireto[i-1]=eft_total[i-1]-eft_direto[i-1]
end

Vejamos agora esse três vetores:

In [30]:
Efeitos=hcat(eft_direto,eft_indireto,eft_total)
Efeitos
6×3 Matrix{Float64}:
  16.8187    4.99875    21.8175
  -2.62956  -0.781538   -3.41109
   1.24248   0.369282    1.61176
 -14.7581   -4.3863    -19.1444
   5.28938   1.57207     6.86145
   3.28488   0.976308    4.26118

4.4. Efeitos Diretos e Indiretos: Estimação Intervalar via Bootstrap

Agora, defininamos algumas funções de interesse antes. A função a seguir calcula os efeitos diretos, indiretos e totais, considerando um vetor de parâmetros $\beta_{1}$. Ela será usada continuamente na nossa função de bootstrap.

In [31]:
function efeitos(β1)
    ψ=inv(I(n)-β1[1]*W)
    eft_direto=zeros(length(β1)-1)
    eft_indireto=zeros(length(β1)-1)
    eft_total=zeros(length(β1)-1)
    for i=2:length(β1)
        eft_direto[i-1]=(1/n)*tr(inv(I(n)-β1[1]*W))*β1[i]
        eft_total[i-1]=(1/n)*sum(sum(ψ[k,j]*β1[i] for j=1:n) for k=1:n)
        eft_indireto[i-1]=eft_total[i-1]-eft_direto[i-1]
    end
    return hcat(eft_direto,eft_indireto,eft_total)
end
efeitos (generic function with 1 method)

Agora, definamos a função de Bootstrap

In [32]:
# Inicialmente, defina o número de bootstraps.
n_bootstrap = 50
# Criando objetos vazios para acumular com nossas estimativas pontuais
bootstrap_direct = []
bootstrap_indirect = []
bootstrap_total = []
y2=y
X2=X
for i in ProgressBar(1:n_bootstrap)
    # Reamostragem com reposição
    resample_indices = rand(1:length(y), length(y))
    Y_resample = y2[resample_indices]
    X_resample = X2[resample_indices, :]
    
    # Otimiza a função SAR em cada iteração do bootstrap
    X=X_resample
    y=Y_resample
    β1 = optimize(sar_likelihood,lower_bounds, upper_bounds,initial_params,Fminbox()).minimizer
    
    
    # Calcula os efeitos com base na função que definimos anteriormente
    efeitos_df=efeitos(β1)
    push!(bootstrap_direct, efeitos_df[:,1])
    push!(bootstrap_indirect, efeitos_df[:,2])
    push!(bootstrap_total, efeitos_df[:,3])
end

# Por fim, captura o desvio-padrão calculado via bootstrap
std_direct = std(bootstrap_direct)
std_indirect = std(bootstrap_indirect)
std_total = std(bootstrap_total)
0.0%┣                                              ┫ 0/50 [00:01<00:-38, -1s/it]
2.0%┣▉                                          ┫ 1/50 [00:02<Inf:Inf, InfGs/it]
4.0%┣██                                              ┫ 2/50 [00:03<02:33, 3s/it]
6.0%┣██▉                                             ┫ 3/50 [00:04<01:44, 2s/it]
8.0%┣███▉                                            ┫ 4/50 [00:06<01:30, 2s/it]
10.0%┣████▊                                          ┫ 5/50 [00:07<01:19, 2s/it]
12.0%┣█████▋                                         ┫ 6/50 [00:09<01:19, 2s/it]
14.0%┣██████▋                                        ┫ 7/50 [00:10<01:11, 2s/it]
16.0%┣███████▌                                       ┫ 8/50 [00:11<01:06, 2s/it]
18.0%┣████████▌                                      ┫ 9/50 [00:12<01:04, 2s/it]
20.0%┣█████████▏                                    ┫ 10/50 [00:13<00:59, 1s/it]
22.0%┣██████████▏                                   ┫ 11/50 [00:14<00:55, 1s/it]
24.0%┣███████████                                   ┫ 12/50 [00:15<00:52, 1s/it]
26.0%┣████████████                                  ┫ 13/50 [00:16<00:49, 1s/it]
28.0%┣████████████▉                                 ┫ 14/50 [00:17<00:46, 1s/it]
30.0%┣█████████████▉                                ┫ 15/50 [00:18<00:45, 1s/it]
32.0%┣██████████████▊                               ┫ 16/50 [00:19<00:43, 1s/it]
34.0%┣███████████████▋                              ┫ 17/50 [00:20<00:42, 1s/it]
36.0%┣████████████████▋                             ┫ 18/50 [00:21<00:40, 1s/it]
38.0%┣█████████████████▌                            ┫ 19/50 [00:22<00:38, 1s/it]
40.0%┣██████████████████▍                           ┫ 20/50 [00:23<00:37, 1s/it]
42.0%┣███████████████████▎                          ┫ 21/50 [00:24<00:36, 1s/it]
44.0%┣████████████████████▎                         ┫ 22/50 [00:25<00:34, 1s/it]
46.0%┣█████████████████████▏                        ┫ 23/50 [00:26<00:32, 1s/it]
48.0%┣██████████████████████                        ┫ 24/50 [00:27<00:31, 1s/it]
50.0%┣███████████████████████                       ┫ 25/50 [00:28<00:30, 1s/it]
52.0%┣████████████████████████                      ┫ 26/50 [00:29<00:28, 1s/it]
54.0%┣████████████████████████▉                     ┫ 27/50 [00:30<00:27, 1s/it]
56.0%┣█████████████████████████▊                    ┫ 28/50 [00:32<00:26, 1s/it]
58.0%┣██████████████████████████▊                   ┫ 29/50 [00:33<00:25, 1s/it]
60.0%┣███████████████████████████▋                  ┫ 30/50 [00:34<00:23, 1s/it]
62.0%┣████████████████████████████▌                 ┫ 31/50 [00:34<00:22, 1s/it]
64.0%┣█████████████████████████████▍                ┫ 32/50 [00:35<00:20, 1s/it]
66.0%┣██████████████████████████████▍               ┫ 33/50 [00:36<00:19, 1s/it]
68.0%┣███████████████████████████████▎              ┫ 34/50 [00:37<00:18, 1s/it]
70.0%┣████████████████████████████████▏             ┫ 35/50 [00:38<00:17, 1s/it]
72.0%┣█████████████████████████████████▏            ┫ 36/50 [00:40<00:16, 1s/it]
74.0%┣██████████████████████████████████            ┫ 37/50 [00:41<00:15, 1s/it]
76.0%┣███████████████████████████████████           ┫ 38/50 [00:42<00:14, 1s/it]
78.0%┣███████████████████████████████████▉          ┫ 39/50 [00:43<00:12, 1s/it]
80.0%┣████████████████████████████████████▉         ┫ 40/50 [00:44<00:11, 1s/it]
82.0%┣█████████████████████████████████████▊        ┫ 41/50 [00:45<00:10, 1s/it]
84.0%┣██████████████████████████████████████▋       ┫ 42/50 [00:46<00:09, 1s/it]
86.0%┣███████████████████████████████████████▋      ┫ 43/50 [00:47<00:08, 1s/it]
88.0%┣████████████████████████████████████████▌     ┫ 44/50 [00:48<00:07, 1s/it]
90.0%┣█████████████████████████████████████████▍    ┫ 45/50 [00:49<00:06, 1s/it]
92.0%┣██████████████████████████████████████████▎   ┫ 46/50 [00:50<00:04, 1s/it]
94.0%┣███████████████████████████████████████████▎  ┫ 47/50 [00:51<00:03, 1s/it]
96.0%┣████████████████████████████████████████████▏ ┫ 48/50 [00:53<00:02, 1s/it]
98.0%┣█████████████████████████████████████████████ ┫ 49/50 [00:54<00:01, 1s/it]
100.0%┣█████████████████████████████████████████████┫ 50/50 [00:55<00:00, 1s/it]
100.0%┣█████████████████████████████████████████████┫ 50/50 [00:55<00:00, 1s/it]
6-element Vector{Float64}:
 26.662380198845682
  2.7265856146621115
  3.4543689988386976
 28.3518226484923
  6.681105575085341
  3.5183713295663144

Efeitos Diretos

In [33]:
hcat(Efeitos[:,1],std_direct)
6×2 Matrix{Float64}:
  16.8187   26.1925
  -2.62956   2.76359
   1.24248   3.51387
 -14.7581   27.9545
   5.28938   6.63234
   3.28488   3.50714

Efeitos Indiretos

In [34]:
hcat(Efeitos[:,2],std_indirect)
6×2 Matrix{Float64}:
  4.99875   2.47909
 -0.781538  0.208176
  0.369282  0.163047
 -4.3863    2.34135
  1.57207   0.578201
  0.976308  0.293798

Efeitos Totais

In [35]:
hcat(Efeitos[:,3],std_total)
6×2 Matrix{Float64}:
  21.8175   26.6624
  -3.41109   2.72659
   1.61176   3.45437
 -19.1444   28.3518
   6.86145   6.68111
   4.26118   3.51837

Construamos agora uma tabela com todos esses efeitos

In [36]:
col_names = ["Variável","Efeitos Diretos", "Desvio-Padrão ED", "Efeitos Indiretos", "Desvio-Padrão EI", "Efeitos Totais", "Desvio-Padrão ET"]
df = DataFrame(hcat(["Constant","ln_gdp","ln_pop","mobility","environ","sanitation"],Efeitos[:,1],std_direct,Efeitos[:,2],std_indirect,Efeitos[:,3],std_total), col_names)
pretty_table(df, header = col_names, alignment = :c, formatters = ft_printf("%.2f", 1:6))
┌────────────┬─────────────────┬──────────────────┬───────────────────┬──────────────────┬────────────────┬──────────────────┐
│  Variável  │ Efeitos Diretos │ Desvio-Padrão ED │ Efeitos Indiretos │ Desvio-Padrão EI │ Efeitos Totais │ Desvio-Padrão ET │
├────────────┼─────────────────┼──────────────────┼───────────────────┼──────────────────┼────────────────┼──────────────────┤
│  Constant  │      16.82      │      26.19       │       5.00        │       2.48       │     21.82      │     26.6624      │
│   ln_gdp   │      -2.63      │       2.76       │       -0.78       │       0.21       │     -3.41      │     2.72659      │
│   ln_pop   │      1.24       │       3.51       │       0.37        │       0.16       │      1.61      │     3.45437      │
│  mobility  │     -14.76      │      27.95       │       -4.39       │       2.34       │     -19.14     │     28.3518      │
│  environ   │      5.29       │       6.63       │       1.57        │       0.58       │      6.86      │     6.68111      │
│ sanitation │      3.28       │       3.51       │       0.98        │       0.29       │      4.26      │     3.51837      │
└────────────┴─────────────────┴──────────────────┴───────────────────┴──────────────────┴────────────────┴──────────────────┘