//styles, look here: https://cdnjs.com/libraries/highlight.js/9.12.0

November 29, 2018

1684 palavras 8 mins

Como eu rodei Stata dentro do R para replicar um paper

Nota prévia de leitura

Antes que você comece a ler essa minha pequena aventura, acho que é muito importante ressaltar que todos os posts aqui no blog são escritos diretamente no R, usando o pacote RMarkdown - mesmo quando usamos algo de python, Julia ou, nesse caso, Stata. O Daniel tem um post bom explicando o nosso workflow de maneira detalhada disponível preeliminarmente aqui.

O paper

Dia desses saiu a edição de Novembro da American Economic Review e nela um paper me chamou muito à atenção: The Costs of Patronage: Evidence from the British Empire, de Guo Xu. O resumo me deixou dando pulinhos de alegria:

“I combine newly digitized personnel and public finance data from the British colonial administration for the period 1854-1966 to study how patronage affects the promotion and incentives of governors. Governors are more likely to be promoted to higher salaried colonies when connected to their superior during the period of patronage. Once allocated, they provide more tax exemptions, raise less revenue, and invest less. The promotion and performance gaps disappear after the abolition of patronage appointments. Patronage therefore distorts the allocation of public sector positions and reduces the incentives of favored bureaucrats to perform.”

Tem tudo aí. Uma base de dados inédita, uma questão interessante, uma abordagem claramente econômica (incentivos, regras) e a quantificação do impacto de um comportamento. Se alguém arranjar um anão e uma garrafa de champagne vira uma festa daquelas. Pois, como eu tenho muita coisa pra fazer, prova para estudar e trabalho para entregar, decidi replicar nem que seja uma regressão desse paper tão bonito - e mais importante ainda - deixar uma base pronta para quem quiser replicar o resto.

Extraindo os dados

Se você está interessado em ter a base de dados maneira que o autor desse paper conseguiu, é fácil. O código a seguir puxa eles diretamente do repositório da AER.

library(readstata13) # importante para ler o arquivo com a base
library(tibble) # não tão importante, mas tibbles > data.frames

temp = tempfile() ## geramos um arquivo temporário
download.file("https://www.aeaweb.org/doi/10.1257/aer.20171339.data",
              destfile = temp, mode = "wb") # puxamos diretamente os dados do repositório da AEA

dados = unzip(temp, file = "analysis.dta") # descomprimimos
dados = as.tibble(read.dta13(dados)) #lemos como um tibble

dim(dados)
## [1] 4687   55
dados #exploramos para garantir que está tudo certo
## # A tibble: 4,687 x 55
##       sid  year                      aid log_salary_governor_gbp
##  * <fctr> <dbl>                   <fctr>                   <dbl>
##  1   Aden  1938    Bernard Rawdon Reilly                8.086411
##  2   Aden  1937    Bernard Rawdon Reilly                8.086411
##  3   Aden  1939    Bernard Rawdon Reilly                8.086411
##  4   Aden  1944        John Hathorn Hall                8.086411
##  5   Aden  1941        John Hathorn Hall                8.086411
##  6   Aden  1942        John Hathorn Hall                8.086411
##  7   Aden  1940        John Hathorn Hall                8.086411
##  8   Aden  1943        John Hathorn Hall                8.086411
##  9   Aden  1946 Reginald Stuart Champion                8.086411
## 10   Aden  1947 Reginald Stuart Champion                8.086411
## # ... with 4,677 more rows, and 51 more variables:
## #   log_rev_total_gbp <dbl>, log_rev_customs_gbp <dbl>,
## #   log_rev_internal_gbp <dbl>, log_exp_total_gbp <dbl>,
## #   log_exp_tax_gbp <dbl>, log_exp_pubworks_gbp <dbl>,
## #   log_population_imputed <dbl>, shared_ancestry <dbl>, both_arist <dbl>,
## #   both_oxbridge <dbl>, both_eton <dbl>, connected <dbl>,
## #   age_entry <dbl>, cambridge <int>, oxford <int>, eton <int>,
## #   peerage <dbl>, civilservant <dbl>, politician <dbl>, military <dbl>,
## #   ordinance_total <dbl>, ordinance_health <dbl>,
## #   ordinance_education <dbl>, ordinance_pubworks <dbl>,
## #   ordinance_welfare <dbl>, ordinance_tax <dbl>, ordinance_trade <dbl>,
## #   customs_exemptions <int>, social_unrest <dbl>, hansard_mention <dbl>,
## #   polarity <dbl>, award_highest <dbl>, log_dist_london <dbl>,
## #   area_tropics <dbl>, logem4 <dbl>, sum_d_pre_post <dbl>,
## #   post1930 <dbl>, tenure_aid <dbl>, landlocked <int>,
## #   quinquennial <dbl>, decade <dbl>, bilateral <dbl>, no_colonies <dbl>,
## #   duration <dbl>, state_aid <dbl>, min_year <dbl>, min_revenue <dbl>,
## #   full <dbl>, post1930_connected <dbl>, connected_year1930 <dbl>,
## #   tenure <dbl>

Temos um painel que cobre boa parte do século XIX e início do XX. As informações são bem detalhadas, desde nome do governador da colônia, onde ele estudou, se era militar e até distância da colônia até Londres.

library(dplyr)

dados %>%
  group_by(decade) %>%
  summarise(idade = round(mean(age_entry, na.rm = TRUE), digits = 2), 
            politico = round(mean(politician, na.rm = TRUE), digits = 2), 
            militar = round(mean(military, na.rm = TRUE), digits = 2))
## # A tibble: 13 x 4
##    decade idade politico militar
##     <dbl> <dbl>    <dbl>   <dbl>
##  1   1854 43.52     0.19    0.45
##  2   1860 41.96     0.20    0.38
##  3   1870 41.92     0.16    0.37
##  4   1880 43.97     0.12    0.39
##  5   1890 45.61     0.13    0.44
##  6   1900 45.26     0.07    0.44
##  7   1910 46.38     0.07    0.44
##  8   1920 49.82     0.06    0.40
##  9   1930 50.36     0.01    0.35
## 10   1940 48.55     0.04    0.38
## 11   1950 47.01     0.00    0.29
## 12   1960 48.65     0.02    0.25
## 13     NA 45.48     0.16    0.54

Queria, em particular, reproduzir a tabela 2 em que salários de governadores de colônias são explicadas por modelos com alguns termos de efeitos fixos e variáveis de conectividade com o encarregado dos apontamentos. Essas variáveis incluem ancentrais comuns, ser aristocrata e ter estudado em Eton, Oxford ou Cambridge junto com quem fez os apontamentos.

A loucura do Stata, ou como eu virei um herege

Acabei fuçando os arquivos para reprodução que a própria AER fornece e achei o código original em Stata - que por sinal poderia ser executado no próprio RMarkdown com a ajuda do pacote Statamarkdown e vou mostrar como fazer isso.

Primeiro carregamos a biblioteca, ainda em R:

library(Statamarkdown)

Depois armazenamos um objeto com nome stataexe o endereço do Stata no seu computador. Se tiver instalado, pode usar a função Statamarkdown::find_stata() para isso.

stataexe <- "C:/Users/Pedro/Desktop/Meus Dados/Stata14/StataMP-64.exe"

Agora você avisa o knitr que está realmente fazendo essa loucura de rodar Stata no RStudio:

knitr::opts_chunk$set(engine.path = stataexe)

Voi lá:

use "analysis.dta", replace

sum log_salary_governo, detail
                  ln governor salary (gbp)
-------------------------------------------------------------
      Percentiles      Smallest
 1%     5.703783       5.521461
 5%      6.39693       5.521461
10%     6.684612       5.521461       Obs               4,325
25%     7.495542       5.521461       Sum of Wgt.       4,325

50%     8.086411                      Mean           7.957734
                        Largest       Std. Dev.      .7848033
75%     8.517193       9.545025
90%     8.853665       9.546813       Variance       .6159162
95%     8.922658       9.546813       Skewness      -.7752303
99%      9.21034       9.546813       Kurtosis       3.013498

Para de fato rodar o código original, precisamos do pacote reghdfe, do Sergio Correia - disponível no Github. Então vamos instala-lo com o código sugerido pelo próprio criador:

* Install ftools (remove program if it existed previously)
cap ado uninstall ftools
net install ftools, from("https://raw.githubusercontent.com/sergiocorreia/ftools/master/src/")

* Install reghdfe 4.x
cap ado uninstall reghdfe
net install reghdfe, from("https://raw.githubusercontent.com/sergiocorreia/reghdfe/master/src/")

* Install boottest for Stata 11 and 12
if (c(version)<13) cap ado uninstall boottest
if (c(version)<13) ssc install boottest

* Install moremata (sometimes used by ftools but not needed for reghdfe)
cap ssc install moremata

ftools, compile
reghdfe, compile

Agora sim podemos estimar os modelos com o código original em Stata, que estima todas as especificações da tabela 2. Vou colocar * antes das linhas que seguem a primeira para que o documento não renderize todas as tabelas.

use "analysis.dta"

reghdfe log_salary_governor_gbp no_colonies shared_ancestry if full==1, absorb(aid year duration) vce(cluster bilateral)

* reghdfe log_salary_governor_gbp no_colonies both_arist if full==1, absorb(aid year duration) vce(cluster bilateral)

* reghdfe log_salary_governor_gbp no_colonies both_eton if full==1, absorb(aid year duration) vce(cluster bilateral)

* reghdfe log_salary_governor_gbp no_colonies both_oxbridge if full==1, absorb(aid year duration) vce(cluster bilateral)

* reghdfe log_salary_governor_gbp no_colonies shared_ancestry both_arist both_eton both_oxbridge if full==1, absorb(aid year duration) vce(cluster bilateral)

* reghdfe log_salary_governor_gbp no_colonies connected if full==1, absorb(aid year duration) vce(cluster bilateral)
> b(aid year duration) vce(cluster bilateral)
(MWFE estimator converged in 26 iterations)

HDFE Linear regression                            Number of obs   =      3,510
Absorbing 3 HDFE groups                           F(   2,   1517) =      22.90
Statistics robust to heteroskedasticity           Prob > F        =     0.0000
                                                  R-squared       =     0.9253
                                                  Adj R-squared   =     0.9108
                                                  Within R-sq.    =     0.0962
Number of clusters (bilateral) =      1,518       Root MSE        =     0.2376

                          (Std. Err. adjusted for 1,518 clusters in bilateral)
------------------------------------------------------------------------------
             |               Robust
log_salary~p |      Coef.   Std. Err.      t    P>|t|     [95% Conf. Interval]
-------------+----------------------------------------------------------------
 no_colonies |   .2207025   .0350703     6.29   0.000     .1519111    .2894939
shared_anc~y |   .1034715   .0470474     2.20   0.028     .0111866    .1957563
       _cons |   7.496656   .0664925   112.74   0.000     7.366229    7.627083
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
         aid |       456           0         456     |
        year |       110           1         109     |
    duration |         7           1           6    ?|
-----------------------------------------------------+
? = number of redundant parameters may be higher

> id year duration) vce(cluster bilateral)
> d year duration) vce(cluster bilateral)
> b(aid year duration) vce(cluster bilateral)
> _eton both_oxbridge if full==1, absorb(aid year duration) vce(cluster bilater
> al)
> d year duration) vce(cluster bilateral)

Encontrei duas dificuldades:

  • O console sai no documento então para ver o resultado de uma linha de código, é preciso compilar o documento todo e conferir a saída lá

  • Cada chunk de código em Stata gera uma sessão nova, então é preciso importar os dados sempre que abrir um chunk novo

E como fazer isso no R?

Vamos usar o pacote lfe, que implementa um método muito similar com a função felm. Como não conheço nem localizei implementação funcional para R de erros-padrão agrupados então a estimação não é estritamente a mesma, mas o espírito está lá.

library(lfe)

modelo1 = felm(log_salary_governor_gbp ~ no_colonies + shared_ancestry | aid + year + duration, 
               data = dados,
               subset = (full == 1))

summary(modelo1)

Call:
   felm(formula = log_salary_governor_gbp ~ no_colonies + shared_ancestry |      aid + year + duration, data = dados, subset = (full == 1)) 

Residuals:
     Min       1Q   Median       3Q      Max 
-1.26531 -0.07417  0.00110  0.08623  0.96266 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
no_colonies      0.22070    0.01289  17.123  < 2e-16 ***
shared_ancestry  0.10347    0.02425   4.267 2.04e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.2376 on 2937 degrees of freedom
Multiple R-squared(full model): 0.9253   Adjusted R-squared: 0.9108 
Multiple R-squared(proj model): 0.09618   Adjusted R-squared: -0.07985 
F-statistic(full model):63.63 on 572 and 2937 DF, p-value: < 2.2e-16 
F-statistic(proj model): 156.3 on 2 and 2937 DF, p-value: < 2.2e-16 
*** Standard errors may be too high due to more than 2 groups and exactDOF=FALSE

Observe que o código que estima o modelo1 é praticamente o mesmo que estima as outras especificações, basta somente adicionar ou retirar variáveis explicativas na fórmula.