5 - Muitas Variáveis e os Waffles Espúrios

Nosso objetivo nessa parte é começar o processo da construção de modelos de regressões múltiplas e, assim, também começaremos a criar as bases para o framework de inferências causais.

Para iniciarmos a discussão iremos introduzir um exemplo empírico, ou seja, um exemplo baseado na experiência e observações, sejam elas baseadas em algum método (metódicas) ou não.

Waffle House

Fonte

Aula - Statistical Rethinking Winter 2019 Lecture 05

A waffle house é uma cadeia de restaurantes com mais de \(2000\) locais em \(25\) estados nos EUA (mapa amarelo abaixo). A maioria das suas localizações está no Sul do país e é um item da cultural e regional norte americana. (wikipedia). Uma das particularidades dessa rede de restaurantes é que eles trabalham 24 horas. Nunca fecham! Essa é a proposta de negócio deles.

Um outro fato importante e desagradável que se surge no sul dos EUA são os Furacões. Esses são causados pelas depressões tropicais climáticas do pais. Os restaurantes da rede Waffles são um dos únicos estabelecimentos que continuam abertos nesses períodos turbulentos. Exceto quando esses furacões atingem uma de suas lojas.

A rede é tão confiável que governadores dos EUA criaram o waffle house index, internamente na FEMA (agência federal de gestão de emergências). Usada como uma métrica informal com o nome da rede de restaurantes, pretendem determinar o efeito das tempestades, como uma escala auxíliar para o planejamento de recuperação de um desastre. (waffle house index)

waffle house map

Imagem - https://www.scottareynhout.com/blog/2017/10/7/waffle-house-map

Além dos desastres naturais, existem também muitas outras coisas acontecendo em grande escala no sul dos EUA, tal como divórcios!

No gráfico abaixo temos a indicação da quantidade de divórcios nos EUA. Observe o sul do país e compare com o mapa acima.

Waffle House contry-level-marriage-divorce-data-2010

Imagem - https://www.bgsu.edu/ncfmr/resources/data/original-data/county-level-marriage-divorce-data-2010.html

Percebeu?

Em ambos os mapas existem uma grande concentração, no extremo do sul do mapa, de restaurantes da rede Waffle House e, subindo mais ao norte do país, temos quantidades cada vez menores. O mesmo ocorre no mapa das taxas de divórcios, quando olhamos para os mesmos locais no mapa.

Podemos então fazer uma estimativa: esses dados estão correlacionados entre si. E podemos nos ser levados a pensar que quanto maior a concentração de restaurantes na região maior seria seria a taxa de divórcios.

E por que isso acontece?

Pelo seguinte motivo: Por nada!

Isso mesmo, nada!!!

Não existe nada que tenha uma relação direta na qual a quantidade de restaurantes da rede em alguma determinada região influencie casais a brigarem e tomarem a decisão de se separar!

É estranho. É cômico. É intrigante. Isso é uma Correlação Espúria!

Correlações Espúrias

Essas são as correlações espúrias, ou seja, correlações sem certeza; que não é verdadeira nem real; é hipotética!

Muita coisa está relacionada com as outras no mundo real. Isso é a Natureza!

Por exemplo, se quisermos, por qualquer motivo que seja, arrumar um “argumento” para enfraquecer a imagem da rede Waffle House, “podemos” usar essas correlações espúrias com um dos argumentos. Assim, nós iriamos expor na mídia a seguinte manchete:

Breaking News:

Pesquisadores [da universidade xyz] indicam que: o aumento do número de restaurantes da rede Waffle House na região implica num aumento assustador no número de divórcios nessa mesma região!

Isso soa estranho, eu sei! Esse é apenas um exemplo extremo.

Mas, lá no fundo, esse tipo de pensamento não soa tão estranho no dia-a-dia…

Existem diversas correlações espúrias no mundo. Muita coisa tem correlação com muitas outras coisas.

Entretanto:

Essas correlações não implicam causalidade.

Mas para entendermos melhor, vamos ver mais alguns exemplos sobre essas correlações espúrias:

  • O consumo de queijo tem uma correlação de \(94.7\%\) com os acidentes fatais com o emaranhado do lençol de cama.

  • O consumo per capita de frango apresenta uma correlação de \(89\%\) com a importação de petróleo.

  • Os acidentes por afogamentos em piscina tem a correlação de \(66\%\) com o número de filmes lançados pelo Nicolas Cage, por ano. Veja graficamente essa correlação abaixo:

Tyler Vigen spurious correlations

Percebeu?

Se o consumo de frango diminuísse, a importação provavelmente não sofreria nenhum impacto por essa causa. E, caso o consumo de queijo diminuir, também não haverá uma diminuição nos acidentes fatais das pessoas que estão dormindo em suas camas. E, como é esperado, se o Nicolas Cage se aposentar dos cinemas, os acidentes por afogamento continuarão constantes.

Tip

Correlação não implica causalidade!

Por fim, ter mais lojas da rede Waffle House não causa mais divórcios na região.


Mais correlações espúrias, tais como essas acima, podem ser encontradas no site do Tyler Vigen.


Entendido essa parte, vamos ao objetivo desse capítulo.

Regressão Múltiplas

Vamos ver como construir um modelo de regressão linear novamente. Mas dessa vez iremos ver também como se faz com múltiplas variáveis e quais são suas implicações.

Prós e contras das múltiplas variáveis:

  • A parte boa desse tipo de modelo é que as regressões múltiplas podem não só revelar correlações espúrias como também podem revelar associações escondidas que nós não faríamos normalmente, ou seja, não teríamos visto essas associações usando o modelo com uma simples variável preditora.

  • Mas, por outro lado, podemos também adicionar variáveis explicativas aos modelos que contenham correlações espúrias à regressão múltipla e, podem também, esconder as algumas das reais associações que existem.

Então, como essas coisas geralmente não são bem explicadas, vamos detalhar todo o processo de construção de uma regressão múltipla, a seguir.

Quando construímos um modelo usando uma regressão múltipla, nós devemos ter uma estrutura mais profusa para pensar sobre as nossas decisões. Apenas jogar todas as variáveis explicativas dentro da regressão múltipla, como usualmente é feito, é o segredo para o fracasso da análise e nós não queremos fazer isso!

Para isso precisamos de uma estrutura mais ampla e rica para conseguirmos pensar e tomar melhores decisões. Essa estrutura é o framework de inferência casual.

Nessa parte iremos aprender do básico sobre inferência casual:

  • Grafos acíclicos direcionados (DAGs - Directed acyclic graphs)

  • Fork, pipes, colliders…

  • Critério de Backdoor.

Já sabemos que o Waffle House não causam os divórcios. Mas o que causa os divórcios?

Já vimos no mapa acima que divórcios do sul do país tem uma taxa bem mais alta do que no restante mais ao norte. Existem muitos esforços para tentar identificar as causas e as taxas de divórcios. Sabemos que no sul tem uma predominância religiosa quando comparada ao restante do país. Isso deixa os cientistas com certas desconfianças.

Existem assim muitas coisas que estão correlacionadas com a taxa de divórcios. Uma delas é a taxa de casamentos. Também podemos usar essas informações para cada um dos outros \(50\) estados do país. Todos eles têm uma correlação positiva da taxa de casamentos com a taxa de divórcios.

Um ponto importante nessa correlação é que só pode acontecer um divórcio se houver o casamento!

Mas a correlação entre essas taxas podem ser também correlações espúrias. Assim como a correlação entre homicídos e divórcios são uma correlação espúria.

Pois uma taxa alta de casamentos pode indicar que a sociedade vê o casamento de modo favorável e isso pode significar taxas de divórcios mais baixas. Não necessariamente faz sentido, mas pode ser que as taxas de casamentos e de divórcios sejam correlações espúrias também.

Note

  • Correlação não implica Causalidade

  • Causalidade não implica Correlação

  • Causalidade implica em correlação condicional

Assim, precisamos mais do que apenas simples modelos! É necessário um estrutura mais robusta.

Mas, o que causa os divórcios?

Vamos descobrir isso…

Existe outra variável que também é correlacionada com a variável taxa de divórcio, é a variável idade mediana das pessoas que se casam, em cada estado. Mas diferente da taxa de casamento, essa variável apresenta uma correlação negativa.

import numpy as np
import pandas as pd
import stan
import nest_asyncio
import matplotlib.pyplot as plt
plt.style.use('default')
# Definindo o plano de fundo cinza claro para todos os gráficos feitos no matplotlib
plt.rcParams['axes.facecolor'] = 'lightgray'
# Desbloqueio do asyncIO do jupyter
nest_asyncio.apply()
def HPDI(posterior_samples, credible_mass):
    
    # Calcula o maior intervalo de probabilidades a partir de uma amostra
    
    # Fonte: https://stackoverflow.com/questions/22284502/highest-posterior-density-region-and-central-credible-region
    sorted_points = sorted(posterior_samples)
    ciIdxInc = np.ceil(credible_mass * len(sorted_points)).astype('int')
    nCIs = len(sorted_points) - ciIdxInc
    ciWidth = [0]*nCIs
    
    for i in range(0, nCIs):
        ciWidth[i] = sorted_points[i + ciIdxInc] - sorted_points[i]
        HDImin = sorted_points[ciWidth.index(min(ciWidth))]
        HDImax = sorted_points[ciWidth.index(min(ciWidth))+ciIdxInc]

    return(HDImin, HDImax)
# ====================================
#   Lendo os dados da Waffle House
# ====================================

df = pd.read_csv('./data/WaffleDivorce.csv', sep=';')
df.head()
Location Loc Population MedianAgeMarriage Marriage Marriage SE Divorce Divorce SE WaffleHouses South Slaves1860 Population1860 PropSlaves1860
0 Alabama AL 4.78 25.3 20.2 1.27 12.7 0.79 128 1 435080 964201 0.45
1 Alaska AK 0.71 25.2 26.0 2.93 12.5 2.05 0 0 0 0 0.00
2 Arizona AZ 6.33 25.8 20.3 0.98 10.8 0.74 18 0 0 0 0.00
3 Arkansas AR 2.92 24.3 26.4 1.70 13.5 1.22 41 1 111115 435450 0.26
4 California CA 37.25 26.8 19.1 0.39 8.0 0.24 0 0 0 379994 0.00
# ============================================
#  Plotando os dados das taxas de divórcios
# ============================================
fig, [ax1, ax2] = plt.subplots(1, 2, figsize=(17, 9))

plt.suptitle('Associações Espúrias?')

ax1.scatter(df.Marriage.values ,df.Divorce.values)
ax1.grid(ls='--', color='white', linewidth=0.4)
ax1.set_xlabel('Taxa de Casamento')
ax1.set_ylabel('Taxa de Divórcio')

ax2.scatter(df.MedianAgeMarriage.values ,df.Divorce.values)
ax2.grid(ls='--', color='white', linewidth=0.4)
ax2.set_xlabel('Mediana da Idade dos Noivos')
ax2.set_ylabel('Taxa de Divórcio')

plt.show()
_images/parte_5_21_0.png

Vamos construir modelos lineares simples para os dois gráficos acima.

# ========================================================
#  Construindo um modelo linear simples:
#
#    taxa de divórcio ~ alpha + beta * taxa_casamento
# ========================================================
divorce_model1 = """
    data {
        int N;
        vector[N] divorce_rate;
        vector[N] marriage_rate;
    }
    
    parameters {
        real alpha;
        real beta;
        real<lower=0, upper=50> sigma;
    }
    
    model {
        divorce_rate ~ normal(alpha + beta * marriage_rate, sigma);
    }
"""

my_data = {
    'N': len(df.Divorce),
    'marriage_rate': df.Marriage.values,
    'divorce_rate': df.Divorce.values,
}

posteriori1 = stan.build(divorce_model1, data=my_data)
fit1 = posteriori1.sample(num_chains=4, num_samples=1000)

alpha_1 = fit1['alpha'].flatten()
beta_1 = fit1['beta'].flatten()
sigma_1 = fit1['sigma'].flatten()
# ==============================================================
# Construindo um modelo linear simples:
#
#  taxa divorcio ~ alpha + beta * mediana da idade dos noivos
# ==============================================================

stan_model2 = """
    data {
        int N;
        vector[N] divorce_rate;
        vector[N] median_age_marriage;
    }
    
    parameters {
        real alpha;
        real beta;
        real<lower=0, upper=50> sigma;
    }
    
    model {
        divorce_rate ~ normal(alpha + beta * median_age_marriage, sigma);
    }
"""

my_data = {
    'N': len(df.Divorce),
    'divorce_rate': df.Divorce.values,
    'median_age_marriage': df.MedianAgeMarriage.values,
}

posteriori2 = stan.build(stan_model2, data=my_data)
fit2 = posteriori2.sample(num_chains=4, num_samples=1000)

alpha_2 = fit2['alpha'].flatten()
beta_2 = fit2['beta'].flatten()
sigma_2 = fit2['sigma'].flatten()
# ====================================================================
#  Plotando os dados das taxas de divórcios e suas estimativas médias
# ====================================================================
fig, [ax1, ax2] = plt.subplots(1, 2, figsize=(17, 9))

plt.suptitle('Associações Espúrias?')

# Calculando as regiões de HPDI para mu - Taxa de casamentos com 0.92 de credibilidade
range_mu = np.sort(df.Marriage)
posteriori_mu_aux = np.array([[alpha_1 + beta_1 * marriage for marriage in range_mu]])[0]
posteriori_mu_HPDI = np.array([[HPDI(posteriori_marriage, 0.92) for posteriori_marriage in posteriori_mu_aux]])[0]

# Gráfico de Divórcio x Casamento
ax1.scatter(df.Marriage.values ,df.Divorce.values)
ax1.fill_between(range_mu, 
                 posteriori_mu_HPDI[:, 0], posteriori_mu_HPDI[:, 1], 
                 color='gray', alpha=0.4)
ax1.plot(df.Marriage.values, 
         alpha_1.mean() + beta_1.mean() * df.Marriage.values,
         color='black')

ax1.grid(ls='--', color='white', linewidth=0.4)
ax1.set_xlabel('Taxa de Casamento')
ax1.set_ylabel('Taxa de Divórcio')
ax1.set_title('Correlação Positiva \n Espúria?')

# ------------

# Calculando as regiões de HPDI para mu - Mediana para idade de casamentos com 0.92 de credibilidade
range_mu = np.sort(df.MedianAgeMarriage)
posteriori_mu_aux = np.array([[alpha_2 + beta_2 * mediaAgeMarriage for mediaAgeMarriage in range_mu]])[0]
posteriori_mu_HPDI = np.array([[HPDI(posteriori_mediaAgeMarriage, 0.92) for posteriori_mediaAgeMarriage in posteriori_mu_aux]])[0]

# Gráfico de Divórcio x Mediana da Idade dos Casamentos
ax2.scatter(df.MedianAgeMarriage.values ,df.Divorce.values)
ax2.fill_between(range_mu, 
                 posteriori_mu_HPDI[:, 0], posteriori_mu_HPDI[:, 1], 
                 color='gray', alpha=0.4)

ax2.plot(df.MedianAgeMarriage.values, 
         alpha_2.mean() + beta_2.mean() * df.MedianAgeMarriage.values,
         color='black')

ax2.grid(ls='--', color='white', linewidth=0.4)
ax2.set_xlabel('Mediana da Idade dos Noivos')
ax2.set_ylabel('Taxa de Divórcio')
ax2.set_title('Correlação Negativa \n Espúria?')

plt.show()
_images/parte_5_25_0.png

Observando os dois gráficos acima, quais dessas correlações são causas plausíveis?

No gráfico da esquerda, sabemos que a taxa de casamento tem uma associação direta com a taxa de divórcio, pois, para se divorciar é necessário antes ter casado. Mas essa interpretação dos fatos pode estar contaminada pelo nosso senso comum e, assim, uma associação espúria, nos seria válida. Um dos motivos pra isso acontecer é que em locais que possuem maiores incentivos religiosos e podem implicar em maiores incentivos para casamentos, podendo, assim, não haver uma causalidade direta com o divórcio.

Já no gráfico à direita, a taxa de divórcio está associada com mediana das idades dos noivos, nos apresentando uma associação negativa. Isso pode nos levar a pensar que jovens, ao se casarem, tomam decisões muito emotivas e que isso também levaria a altas taxas de divórcios. Ou pode ser que essa seja uma associação espúria e nossas hipóteses e nosso senso comum não tenha sentido.

Assim, mesmo que nossas justificativas sejam bastante verossímeis a respeito do comportamento da Natureza do evento, não podemos afirmar que essas associações não sejam espúrias.

Se a identificação de associações espúrias, como no exemplo de filmes do Nicolas Cage, pareciam óbvias de serem identificadas, agora, essas não parecem ser tão simples de perceber sua veracidade.

Portanto, é necessário construirmos um ferramental que nos permita identificar com mais facilidade e, com um maior grau de certeza, se essas associações são reais ou são espúrias.

O que queremos fazer agora é colocar essas duas variáveis no mesmo modelo e entender o que isso faz e por que nos revela, quase certamente, que determinada variável é uma impostora.

Múltiplas causas de divórcios

Aprender a taxa casamentos de uma cidade

Fonte: Hong Kong - wikipedia

Como entender o mecanismo das Causas Naturais de divórcio numa cidade grande

O que nós queremos saber é qual o valor de uma variável preditora, uma vez que nós conhecemos os valores das outras variáveis preditoras corretamente?

Todas variáveis preditoras são ,em alguma extensão, correlacionadas com as outras variáveis preditoras e, também, com a variável resposta. Entretanto, elas têm correlações parciais que são reveladoras de informações adicionais dessa estrutura de correlação.

Para entendermos melhor, vamos exemplificar com nosso exemplo sobre divórcios.

Nós gostariamos de aprender mais sobre o valor da taxa de casamentos, uma vez que já conhecemos a mediana da idade da taxa dos noivos.

Entendendo os grafos um pouco mais perto.

DAG’s são o acrônimo para a expressão inglês Directed Acyclic Graph, o que é portugês é conhecido como Grafos Acíclicos Direcionados (ver mais em wikipedia).

# =======================================
#   Construindo o desenho de um DAG 
# =======================================

# -----------------------------------------------------
# Obs: Os próximos grafos irei esconder os códigos, 
#      para ficar visualmente melhor. Mas, se quiser,
#      os códigos podem ser encontrados no github.
# -----------------------------------------------------

plt.figure(figsize=(17, 7))

plt.xlim(0, 1)
plt.ylim(0, 1)

size=40

plt.annotate('A', (0.2, 0.8), fontsize=size)
plt.annotate('M', (0.8, 0.8), fontsize=size)
plt.annotate('D', (0.495, 0.2), fontsize=size)

plt.title('Grafo dos Divórcios')

# Edge: M <---> D
plt.annotate("", 
             xytext=(0.79, 0.79), xy=(0.53, 0.265) ,
             arrowprops=dict(arrowstyle="-|>,  head_width=1, head_length=1.2"))

# Edge: A <---> M
plt.annotate("", 
             xytext=(0.24, 0.82), xy=(0.77, 0.82),
             arrowprops=dict(arrowstyle="-|>,  head_width=1, head_length=1.2"))

# Edge: A <---> D
plt.annotate("", 
             xytext=(0.24, 0.77), xy=(0.48, 0.26),
             arrowprops=dict(arrowstyle="-|>,  head_width=1, head_length=1.2"))


# Remover os valores dos eixos
plt.yticks([])
plt.xticks([])

plt.show()
_images/parte_5_34_0.png

DAG’s são ferramentas heurísticas usadas no framework de inferências causais. Elas não como ferramentas mecânicas analíticas que já vimos, mas são incrivelmente úteis para nos auxiliar a pensar melhor e eventualmente também nos permite entender muito melhor os modelos mecânicos.

As setas do DAG apontam apenas em uma direção e essa direções indica uma relação de causalidade entre as variáveis analisadas, ou seja, indica que uma variável tem influência direta sobre a outra.

A ausência de ciclos significa que não existem loops na causalidade. Mas esses loops podem acontecer sobre o tempo, o que tornaria a análise também uma série temporal!

A representação desses problemas reais em estruturas como DAG’s podem ser, realmente, muito grandes. Pois, assim, podemos descrever a estrutura no tempo \(T\), \(\{T_1, T_2, ...\}\), e assim por diante.

Nós, normalmente rotulamos os grafos por dois nomes: nós (nodes) e arestas (edge). (Iremos aqui, por conveção, usar os nomes em inglês.)

Note

Os nodes (nós) são as variáveis. Já os edges (as arestas) representam a relação causal entre os nodes.

As associações que foram levadas em consideração, podem ser incluídas dentro dos modelos de redes bayesianas, que são considerados parte do conjunto de ferramentas de Machine Learning. Esses modelos também não levam em consideração a causalidade, pois não existe um mecanismo interno nesses modelos que leve em consideração a direção da causalidade.

Já nos DAG’s, tal mecanismo, existe!

Note

Esse mecanismo nos permite a capacidade de enxergar, através das lentes probabilísticas, a influência da causa e efeito em diferentes eventos! Isso não é medir uma associação. É uma medida para a causalidade!

E isso, faz toda a diferença!

Também veremos qual é a diferença entre as duas abordagens comnetadas…

Bons Grafos

Queremos saber a diferença entre esses dois grafos abaixo. O da esquerda tem um caminho direto de \(A <-> M <-> D\) e, no outro gráfico, uma suposição de que temos que a relação causal entre a taxa de casamento (\(M\)) e a taxa de divórcio (\(D\)) não existe mais.

_images/parte_5_40_0.png

Regressões Lineares, em princípio, podem nos dizer a diferença entre essas duas coisas. Mas uma regressão bivariada já não pode mais. Elas podem nos dar apenas algum conhecimento da associação entre casamentos e divórcios, mas não podem nos dizer qual a diferença entre esses dois *DAG's* acima.

Mas, porque não?

Por que, A tem influência tanto na taxa de casamentos (\(A <-> M\)) quanto em D (\(A <-> D\)), isso gera uma correlação entre os eventos taxa de casamento (\(M\)) e taxa de divórcio (\(D\)), mesmo que uma não influencie na outra, como no gráfico à direita. (Elegante essa explicação, hein!)

É um modo bonito de dizer:

Warning

Correlação não é Casualidade!

Essa mesmo estrutura do DAG’s, pode ser modelada com as correlações espúrias do início do capítulo, waffle House vs taxa de divórcios

_images/parte_5_42_0.png

A correlação que existe entre essas duas variáveis como foi visto anteriormente, tem sua origem na causa de algum outro evento que não conhecemos, rotulado por (\(?\)). Só sabemos que seu comportamento tem uma influência direta sobre a magnitude do efeito dos divórcios e da quantidade de lojas da rede de restaurantes tem por região.

Então, se estamos tentando medir a ligação entre a taxa de casamentos (\(M\)) e a taxa de divórcios (\(D\)) usando, como sugestão, a estrutura o grafo da direita (da figura acima), teremos uma grande confusão. Pois mais adiante uma definição melhor sobre essa confusão.

Vamos rotular essa estrutura com a seguinte notação:

\[ M <-> D | A \]

Significa que podemos conhecer a associação que \(M\) e \(D\) tem entre si, (\(M <-> D\)), condicionado (\(|\), caracter chamado de pipe) a \(A\).

Quando conhecemos o valor de \(A\), então eu tenho um valor extra, uma associação externa, entre as duas variáveis. E é isso que a regressão múltipla pode pode nos dizer.

\[ D_i \sim Normal(\mu_i, \sigma)\]
\[ \mu_i = \alpha + \beta_M M_i + \beta_A A_i \]

Onde:

  • \(D_i\): Taxa de divórcio

As taxas médias dos casamentos:

  • \(\beta_M\): A ‘peso’ da taxa de casamentos

  • \(M_i\): A taxa média de casamentos

As as medianas das idades nos casamentos:

  • \(\beta_A\): A ‘peso’ da mediana das idades dos casamentos

  • \(A_i\): Mediana das idades dos casamentos

Podemos ver que esse modelo tem uma certa diferença dos modelos que vimos anteriormente. Existe um \(\beta\) para cada uma das variáveis preditoras.

Essa regressão linear, é um tipo especial de rede bayesiana, associa a variável resposta (\(D_i\)) a uma distribuição gaussiana. E sua média (\(\mu_i\)) é dada pela variáveis já conhecidas e um desvio padrão (\(\sigma\)).

Então, nossa média será descrita como \(\mu_i\), onde \(i\), significa que condicionaremos os valores de \(\mu_i\) para cada \(i\). Aqui, no exemplo, cada \(i\) indica um novo estado do sul dos EUA. E, assim, cada condicionamento será descrito pelos ‘pesos’ multiplicado as suas próprias variáveis preditoras.

Prioris

Agora, nós iremos padronizar a variável taxa de divórcio (\(D_i\)), a variável taxa de casamento (\(M_i\)) e também a variável mediana das idades dos casamentos.

Assim, como esperamos, podemos escrever a priori para \(\alpha\) da seguinte forma:

\[\alpha \sim Normal(0, 0.2)\]

Pois esperamos que \(\alpha\) esteja próximo de zero!

E as prioris para os \(\beta\)’s da seguinte forma:

\[ \beta_M \sim Normal(0, 0.5)\]
\[ \beta_A \sim Normal(0, 0.5)\]
\[ \sigma \sim Exponential(1) \]

Pois todos os valores foram padronizados, e assim esperamos que a estimativa à priori seja próxima de zero.

# =============================
#   Padronizando as variáveis 
# =============================
M_stdr = (df.Marriage - df.Marriage.mean())/df.Marriage.std() 
D_stdr = (df.Divorce - df.Divorce.mean())/df.Divorce.std()
A_stdr = (df.MedianAgeMarriage - df.MedianAgeMarriage.mean())/df.MedianAgeMarriage.std()
# ===========================================================
#  Priori preditiva para Mediana das Idades dos Casamentos
# ===========================================================

range_A = np.linspace(A_stdr.min(), A_stdr.max())  # Range de valores da média de idade dos casamentos padronizados

qtd_amostras = 50  # Quantidade de amostras da priori preditiva

# Amostrando os valores de 𝜇𝑖 da priori
𝛼 = np.random.normal(0, 0.2, qtd_amostras)
𝛽_A = np.random.normal(0, 0.5, qtd_amostras)
𝜎 = np.random.exponential(1, qtd_amostras)  # Priori 𝜎 ~ Exp(1)
# ========================================
#   Gráfico da Priori Preditiva para 𝜇𝑖
# ========================================

𝜇 = [ 𝛼 + 𝛽_A * A_i for A_i in range_A ]  # Calculando 𝜇𝑖 = 𝛼 + 𝛽𝐴 * 𝐴𝑖, para todo 𝐴𝑖

# Plotando o gráfico 
plt.figure(figsize=(17, 9))

# Relembrando o DAG: A <-> D!
# Plotando as retas da posteriori para 𝜇𝑖
plt.plot(range_A, 𝜇, color='darkblue', linewidth=0.2)

plt.title("Priori Preditiva de $\mu_i$")
plt.xlabel("( $A$ ) Média das Idades dos Casamentos (padronizadas)")
plt.ylabel("( $D$ ) Taxa de Divórcio (padronizadas)")

plt.grid(ls='--', color='white', alpha=0.4)

# Ajustando os limites da visuzalização do gráfico.
plt.ylim((-2, 2))
plt.xlim((-2, 2))

plt.show()
_images/parte_5_51_0.png

Temos no gráfico acima a representação da nossa distribuição à priori do modelo de regressão linear. Sorteamos \(50\) linhas dessa distribuição. Essa é a distribuição à priori preditiva de \(\mu_i\). Ou seja, isso é oque nosso modelo pensa sobre o problema, antes de darmos os dados à ele.

Temos que sempre fazer a simulação da priori preditiva. Só assim será possível entender uma importante característica presente em todos os modelo. Temos observar se a amplitude da priori faz sentido. Quando nós dermos os dados para o modelo, é necessário que as estimativas do modelo resida sobre distribuição à priori. Caso contrário, teremos uma priori ruim. Em cenários mais simples, e com muitos dados, esse efeito pode até passar despercebido e ajustar bem. Porém em cenários mais complexos, uma priori ruim poderá ser desastrosa!

Note

Sempre verifique a priori.

Vamos andar pelo gráfico para entendermos melhor suas partes. Iniciando pela Média das Idades dos Casamentos padronizada ($A$ padronizada) que é uma gaussiana, centrada em \(0\) e com desvio padrão\(\frac{1}{2}\):

\[A \sim Normal(0, 0.5)\]

No gráfico nós estamos observando todos os valores dentro do intervalo \([-2, \mbox{ } 2]\). Esse intervalo corresponde ao \(2^o\) desvio padrão! Isso deve corresponder a grande maioria dos casos que os valores da Mediana das Idades dos Casamentos (padronizadas) irá ocorrer.

Esse pensamento também é válido para a variável Taxa de Divórcio ($D$ padronizada). Seus valores também estão delimitados pelo intervalo \([-2, \mbox{ } 2]\), representando o \(2^o\) desvio padrão.

Esse modelo nos diz é qual a taxa de divórcio padronizada,(\(D\)), que é uma variável z-score, quando já conhecemos os valores da Média das Idades dos Casamentos (\(A\)), que também está padronizada (outra z-score). Assim temos duas variáveis padronizadas, z-scores, explicando uma à outra.

Agora, se as linhas da regressão linear não morar no espaço delimitado pela priori, então a escolhemos uma priori ruim! Por que isso é impossível! Se após dermos os dados pra o modelo, a inclinação da curva for maior que a delimitada pela região da priori, temos uma priori ruim.

Ainda podemos discutir se a escolha da priori irá governar todo o range da taxa de divóricio. Provavelmente não é verdade. Em capítulos posteriores, quando falarmos sobre overfitting (ou em português, teremos um sobreajuste). Isso acontece pois a priori não é apertada o suficiente para trabalharmos segurança. Nesse caso podemos pensar em prioris flat, que podem ser justificada cientificamente, tal como uma priori flat implícita nas análises frequentistas.

Nosso modelo

Esse é nosso modelo, descrito na primeira linha. Na segunda linha temos dois termos lineares, lembrando que o termo linear significa aditivo. Esses dois termos irá gerar um plano. A seguir, teremos nossas prioris. Um destaque para a priori do \(\sigma\), de agora em diante não vamos mais usar \(Uniform\) mas sim a distribuição \(Exponential\), pois ela tem boas propriedades. Falaremos mais sobre isso adiante.

\[D_i \sim Normal(\mu_i, \sigma) \]
\[ \mu_i = \alpha + \beta_M M_i + \beta_A A_i \]
\[ \alpha \sim Normal(0, 0.2) \]
\[ \beta_M \sim Normal(0, 0.5) \]
\[ \beta_A \sim Normal(0, 0.5) \]
\[ \sigma \sim Exponential(1) \]

O uso da distribuição \(Exponential\) como priori para \(\sigma\) tem vantagens tais como, sempre tem valores positivos, para valores maiores a sua probabilidade decresce e para defini-lá precisamos indicar qual será seu valor médio.

# =====================================
#  Estimando o modelo linear proposto
# =====================================

stan_model_divorce = """
    data {
        int N;
        vector[N] divorce_rate;
        vector[N] marriage_rate;
        vector[N] median_age;
    }
    
    parameters {
        real alpha;
        real beta_M;
        real beta_A;
        real<lower=0> sigma;
    }

    model{
        alpha ~ normal(0, 0.2);
        beta_M ~ normal(0,0.5);
        beta_A ~ normal(0, 0.5);
        sigma ~ exponential(1);
        
        divorce_rate ~ normal(alpha + 
                              beta_M * marriage_rate + 
                              beta_A * median_age, 
                              sigma);
    }
"""

my_data = {
    'N': len(D_stdr.values), 
    'divorce_rate': D_stdr.values,
    'marriage_rate': M_stdr.values,
    'median_age': A_stdr.values
}

posteriori_divorce = stan.build(stan_model_divorce, data=my_data)
fit_divorce = posteriori_divorce.sample(num_chains=4, num_samples=1000)

alpha = fit_divorce['alpha'].flatten()
beta_M = fit_divorce['beta_M'].flatten()
beta_A = fit_divorce['beta_A'].flatten()
sigma = fit_divorce['sigma'].flatten()
# Iremos usar esses valores mais tarde nesse material, quando formos fazer a verificação.
# Assim,irei renomear as variáveis da posteriori.

alpha_check = fit_divorce['alpha'].flatten()
beta_M_check = fit_divorce['beta_M'].flatten()
beta_A_check = fit_divorce['beta_A'].flatten()
sigma_check = fit_divorce['sigma'].flatten()
def resume_posteriori(var, confidence_HPDI=0.93, rounded=2):
    """
    Return the summary of posteriori data
    """
    posteriori = []

    confi_HPDI = HPDI(var, confidence_HPDI)
    
    posteriori.append(var.mean())
    posteriori.append(var.std())
    posteriori.append(confi_HPDI[0])
    posteriori.append(confi_HPDI[1])

    return np.round(np.array([posteriori]), rounded)[0]
def describe_posteriori(vars_post, confidence_HPDI=0.93, plot=True):
    
    post = []
    
    for var_ in vars_post:
        post.append(resume_posteriori(eval(var_), confidence_HPDI))
    
    hpdi_min_label = str(100 * round(1 - confidence_HPDI, 3)) + '%'
    hpdi_max_label = str(100 * round(confidence_HPDI, 3)) + '%'
        
    post = pd.DataFrame(post,
                        index=vars_post,
                        columns=['Mean', 'Std', hpdi_min_label, hpdi_max_label])
    
    if plot:
        
        plt.figure(figsize=(17, len(post) + 1))
    
        plt.title('Estimativas das Posterioris')
        
        min_axis_ = post.iloc[:, 2:4].min().min()
        max_axis_ = post.iloc[:, 2:4].max().max()

        for i in range(len(post)):
            plt.plot([min_axis_*1.5, max_axis_*1.5], [i, i], ls='--', color='gray')
            plt.plot([post.iloc[i, 2], post.iloc[i, 3]], [i, i], color='blue')
            plt.plot(post.iloc[i, 0], i, 'ko')
            plt.annotate(post.index[i], (min_axis_*1.5, i+0.2), color='blue')
            

        if min_axis_ < 0 and max_axis_ > 0:
            plt.axvline(0, ls='--', color='red', alpha=0.6)

        plt.ylim((-1, len(post)+1))
        plt.grid(ls='--', color='white', alpha=0.4)
        
        ax = plt.gca()
        ax.axes.yaxis.set_visible(False)
        
        plt.show()
    
    return post
# =====================================
#  Descrevendo os dados da posteriori
# =====================================

vars_post = ['alpha', 'beta_M', 'beta_A', 'sigma']
describe_posteriori(vars_post, 0.945, plot=False)
Mean Std 5.5% 94.5%
alpha -0.00 0.10 -0.18 0.20
beta_M -0.06 0.16 -0.38 0.23
beta_A -0.61 0.16 -0.91 -0.29
sigma 0.83 0.09 0.67 0.99

As informações da tabela acima contém um resumo das posterioris estimadas. Como esperado \(\alpha\) tem a média \(0\). Pois tinha que ser assim conforme a construção do nosso modelo.

Já a estimativa para do \(\beta_M\), taxa de casamento, é levemente negativa e o desvio padrão está entre \(2\) a \(3\) vezes a sua média. Podemos olhar para o intervalo de HPDI de \(89\%\) no qual essa valores estão entre \(-0.36\) e \(0.26\).

Talvez exista algum efeito, ou talvez não tenha nenhum efeito dependendo da direção. Nós apenas não sabemos muito bem o que pensar sobre essa variável, pois ela não apresenta uma relação consistente, não existe uma associação consistente na regressão multipla entre a taxa de casamentos e a taxa de divórcios.

Agora, a mediana das idades do casamentos tem uma estimativa média de \(-0.6\) com o desvio padrão também de \(0.16\), temos que a massa da posteriori está inteiramente abaixo de zero! Assim, realmente temos uma associção negativa entre a media das idades dos casasmentos e a taxa de divórcios.

Mas agora nós já sabemos que não temos um impacto diretamente causal entre taxa de casamentos e a taxa de divórcios. Isso estava mascarado pois idade mediana é uma causa comum entre as duas outras variáveis.

#  Legendas
# ===========

# beta_2 == D ~ A   
# beta_A ==> D ~ A + M 

# beta_M ==> D ~ A + M  
# beta_1 ==> D ~ M  

vars_all = ['beta_1', 'beta_M', 'beta_2', 'beta_A'] 
describe_posteriori(vars_all, 0.945, plot=True)
_images/parte_5_61_0.png
Mean Std 5.5% 94.5%
beta_1 0.18 0.06 0.06 0.30
beta_M -0.06 0.16 -0.38 0.23
beta_2 -0.88 0.18 -1.21 -0.55
beta_A -0.61 0.16 -0.91 -0.29

Quando observando as tabelas acima e comparando com o gráfico, temos que a taxa de casamentos (\(\beta_M\)), resultante da regressão multipla, tem a sua distribuição à posteriori, com \(89\%\) de HPDI, contendo o zero. Portanto provávelmente a variável taxa de casamento (\(\beta_M\)) não tem impacto casual direto na taxa de divórcio.

Ele foi mascarado por que a mediana das idades dos casamentos (\(A\)) é uma variável que influência tanto a taxa de divórcio quanto a taxa de casamentos.

No gráfico acima fica claro que o modelo beta_1, no modelo \(D ~ M\), nos diz que taxa de casamento tem uma associação positiva com a taxa de divórcio, o que era de se esperar pelos nossos entendimentos sobre como poderia funcionar esse evento.

Entretanto, como a regressão múltipla podemos ver que a posteriori atinge o zero, e assim não podemos perceber que provavelmente não existe uma associação entre a taxa de casamentos e a taxa de divórcios.

Isso é uma boa coisa que a regressão múltipla pode fazer por nós! Eu acredito que essa é realmente a relação causal que temos aqui.

Assim, nosso DAG provavelmente seria dessa forma:

_images/parte_5_63_0.png

Assim, como não temos uma relação forte da taxa de casamentos (\(M\)) com a variável taxa de divórcios (\(D\)), a representação de nosso DAG, ou seja, a representação da causalidade, poderá ser mapeada de acordo com o gráfico acima.

Mas vamos explorar um pouco melhor o significado dessa abordagem.

Regressão Múltipla

  • Uma vez que nós conhecemos \(A\), mediana das idades dos casamentos, existe apenas um pequeno valor adicional de conhecimento sobre \(D\) que está contido em \(M\), a taxa de casamento.

Então podemos interpretar isso da seguinte forma, ao conhecermos os valores de \(A\) implica que o conhecimento de \(M\) não vai nos ajudar muito mais a entender \(D\). Isso coincide com DAG acima, onde não há relação causal da taxa de casamentos para a taxa de divórcios.

  • Uma vez que nós conhecemos a \(M\), taxa de casamentos, há muito valor em conhecer também \(A\), mediana das idades dos casamentos.

Isso funciona na outra direção, pois a mediana das idades dos casamentos (\(A\)) é uma causa comum da taxa de casamentos (\(M\)).

  • Se nós não conhecermos a mediana das idades dos casamentos (\(A\)) em algum estado, ainda é útil conhecer a taxa de casamentos (\(M\)).

Saber sobre a taxa de casamentos (\(M\)) é útil e importante pois isso pode nos dá informações adicionais. Essa informação vem de outra relação causal e não nos dá uma informação causal direta entre as variáveis.

Note

Esse é nosso negócio aqui. A inferência! Descobrir a diferença entre essas coisas.

Se nós apenas quisermos fazer uma previsão e não nos importarmos com a inferência causal, a taxa de casamento é útil e ajudará a prever as coisas. Mas, por outro lado, não nos ajuda a fazer interevenções no mundo real, porque se quisermos mudar a taxa de divórcios (\(D\)) nos estado manipulando a taxa de casamento (\(M\)) não teria nenhum efeito.

Simplesmente por que não é assim que as coisas funcionam. A maquinaria de causalidade natural dos divórcios não apresenta essa ligação entre da taxa de casamentos para a taxa de divórcios.

Assim, para efeito de políticas públicas, é necessário focar nas alterações na mediana da idade dos casamentos (\(M\)) para verificar os efeitos na taxa de divórcios (\(D\)).

Assim precisamos ser claros se queremos apenas prever as coisas. Se quisermos também inteferir as relações causais, devemos fazer a previsão das relações causais para que possamos fazer as intervenções. Uma intervenção requer um verdadeiro entendimento da causalidade do sistema.

Warning

Uma previsão não tem o poder de fazer tais intervenções causais.

Isso é o grande terror da ciência, podemos fazer previsões realmente boas sem entendermos nada! Lembra dos modelos geocêntricos. Modelo estatísticos corretos não são suficientes para descobrir relações causais, então precisamos de algo extra!

Predição da Posteriori

Como visualizamos os modelos como este acima? Existem muitas diferentes formas de mostrar isso. Vamos ver algumas formas e exemplos rapidamente. Porém a maneira mais útil de visualizar um particular modelo depende do modelo em particular e do objeto de estudo que estamos tentando observar. Não existe uma forma genérica para visualizar qualquer tipo de gráfico.

Vamos apresentar alguns exemplos:

  1. Plot dos resíduos dos preditores: Muito útil para entender como a regressão funciona, assim iremos fazer isso apenas uma vez nesse curso. Esse tipo de visualização é muito bom para entender o funcionamento da regressão mas não é tão bom para comunicar os resultados.

  2. Plot contrafactuais: Chama-se contrafactuais pois nós imaginamos como manipular qualquer uma das variáveis sem alterar quaisquer outras variárveis. Com isso faremos as previsões para saber como o modelo se comporta com essas manipulações.

  3. Plot da Predição da Posteriori: É basicamente o mesmo que já fizemos antes, porém veremos alguns pontos diferentes mais adiante.

Plot dos Resíduos dos Preditores

  • Objetivo: Mostrar a associação de cada um dos preditores com o resultado, “controlado” por outras preditores.

A associação de uma variável com o resultado parece ter o controle em outras variáveis preditoras, então dentro da maquinaria do modelo. Queremos com isso entender como o modelo enxerga essas coisas internamente. Isso é o que queremos fazer, calcular esses estados intermediários mesmo que eles não sejam visíveis no modelo, para termos uma boa intuição do que está acontecendo internamente no modelo.

  • Nos dá uma intuição muito boa.

Útil para termos uma intuição do modelo e como está o seu funcionamento interno.

  • Nunca analise os resíduos.

Não existem motivos lógicos para fazer uma regressão da variável resposta, (\(D \sim residuos\), por exemplo), isso nos dá uma resposta errada. Pois nos dará estimativas erradas e tendênciosas.

Receita:

  1. Faça uma regressão de uma variável com as outras variáveis.

    • No nosso exemplo, teremos a idade mediana dos casamentos, (\(M\)) explicando a taxa de casamento, (\(A\)), ambas padronizadas.

  2. Calcule os resíduos dos preditores.

    • Assim podemos encontrar a variância extra que sobrou depois dessa associação, esses são os resíduos.

  3. Faça uma regressão da variáveis resposta com os resíduos encontrados no passo anterior.

# =============================================
#   Lendo os dados da Waffle House (novamente)
# =============================================

df = pd.read_csv('./data/WaffleDivorce.csv', sep=';')
df.head()
Location Loc Population MedianAgeMarriage Marriage Marriage SE Divorce Divorce SE WaffleHouses South Slaves1860 Population1860 PropSlaves1860
0 Alabama AL 4.78 25.3 20.2 1.27 12.7 0.79 128 1 435080 964201 0.45
1 Alaska AK 0.71 25.2 26.0 2.93 12.5 2.05 0 0 0 0 0.00
2 Arizona AZ 6.33 25.8 20.3 0.98 10.8 0.74 18 0 0 0 0.00
3 Arkansas AR 2.92 24.3 26.4 1.70 13.5 1.22 41 1 111115 435450 0.26
4 California CA 37.25 26.8 19.1 0.39 8.0 0.24 0 0 0 379994 0.00
# ==========================================
#   Relembrando: Padronizando as variáveis 
# ==========================================
M_stdr = (df.Marriage - df.Marriage.mean())/df.Marriage.std() 
D_stdr = (df.Divorce - df.Divorce.mean())/df.Divorce.std()
A_stdr = (df.MedianAgeMarriage - df.MedianAgeMarriage.mean())/df.MedianAgeMarriage.std()

Para evitar a reescrever muito código, irei ao longo do texto criando alguns funções para facilitar a leitura.

Segue abaixo a primeira função, ela irá plotar uma priori para um modelo linear normalmente distribuído.

# ===================
#  Plotando a priori
# ===================

def plot_priori_lm_normal(N, alpha, beta):
    """
    Plot à priori to linear model using normal distribution.
    
    Modelo:
    =======
    y ~ normal(mu_i, sigma)
    mi = alpha + beta * x
    
    Prioris:
    ========
    alpha ~ normal(alpha[alpha_mean], alpha[alpha_std], N)
    beta ~ normal(beta[beta_mean], beta[beta_std], N)
    sigma ~ exponential(1)
    
    Params:
    =======
    N: int
    alpha: [alpha_mean, alpha_std]
    beta: [beta_mean, beta_std]
    """
    # Prioris
    alpha = np.random.normal(alpha[0], alpha[1], N)
    beta = np.random.normal(beta[0], beta[1], N)   

    # Os dados originais estão padronizados
    x = np.linspace(-3, 3, 100)

    # Modelo linear para as prioris
    y = [alpha + beta * x_i for x_i in x]

    # Plotando a priori
    plt.figure(figsize=(17,9))

    plt.plot(x, y, color='darkblue', linewidth=0.1)

    plt.title('Plot das Prioris normalizadas')
    plt.xlabel('(x) - Eixo X')
    plt.ylabel('(y) - Eixo y')

    plt.xlim((-3, 3))
    plt.ylim((-3, 3))

    plt.grid(ls='--', color='white', alpha=0.4)

    plt.show()

Abaixo, a função irá encapsular todo o código de uma regressão linear bayesiana univariada.

# ================================
#  Regressão Linear - Univariada 
# ================================

def plot_lm_posteriori(var_y, var_x, alpha, beta, N=200, plot_residuos=True, 
                       title_xaxis=False, title_yaxis=False):
    # Modelo linear para as prioris
    # Os dados originais estão padronizados

    # Os dados originais estão padronizados
    x = np.linspace(-3, 3, 100)
    
    # Cáculo dos y e y_mean dado x
    y = [alpha + beta * x_i for x_i in x]
    y_mean = alpha.mean() + beta.mean() * x

    # Plotando a priori
    plt.figure(figsize=(17,9))

    plt.plot(x, y, color='darkblue', linewidth=0.1, alpha=0.5)
    plt.plot(x, y_mean, color='black', linewidth=2)
    plt.plot(var_x, var_y, 'o', color='red', markersize=5)

    if plot_residuos:
        plt.plot([var_x, var_x], 
                 [var_y, alpha.mean() + beta.mean() * var_x],
                 '-', markersize=3, color='darkred', alpha=0.3)

    plt.title('Posteriori Plot dos Resíduos dos Preditores')
    plt.xlabel(title_xaxis if title_xaxis else '(x) - Eixo x')
    plt.ylabel(title_yaxis if title_yaxis else '(y) - Eixo y')

    plt.xlim((-3, 3))
    plt.ylim((-3, 3))

    plt.grid(ls='--', color='white', alpha=0.4)

    plt.show()

    

def lm(var_y, var_x, alpha, beta, exp=1, num_chains=4, filter_n=500, num_samples=5000, plot=True, 
       plot_residuos=True, title_xaxis=False, title_yaxis=False):
    
    if not (len(var_x) == len(var_y)):  # TODO: Use except to raise an error.
        print('Erro: As dimensões das variáveis não são iguais.')
        return False
    
    stan_model = """ 
        data {
            int<lower=0> N;
            vector[N] variavel_resposta;
            vector[N] variavel_explicativa;
            real alpha_mean;
            real alpha_std;
            real beta_mean;
            real beta_std;
            real exp;
        }

        parameters {
            real alpha;
            real beta;
            real<lower=0> sigma;
        }

        model {
            alpha ~ normal(alpha_mean, alpha_std);
            beta ~ normal(beta_mean, beta_std);
            sigma ~ exponential({exp});

            variavel_resposta ~ normal(alpha +  beta  * variavel_explicativa, sigma);
        }
    """

    data = {
        'N': len(var_y),
        'variavel_resposta': var_y,
        'variavel_explicativa': var_x,
        'alpha_mean': alpha[0],
        'alpha_std': alpha[1],
        'beta_mean': beta[0],
        'beta_std': beta[1],
        'exp': exp,
    }
    
    posteriori = stan.build(stan_model, data=data)
    fit = posteriori.sample(num_chains=num_chains, num_samples=num_samples)

    alpha_ = fit['alpha'].flatten()
    beta_ = fit['beta'].flatten()
    
    # Filtra a quantidade de dados para o plot
    if filter_n > 0:
        alpha_ = alpha_[len(alpha_) - filter_n: ]
        beta_ = beta_[len(beta_) - filter_n: ]
    
    if plot:
        plot_lm_posteriori(var_y, var_x, alpha_, beta_, plot_residuos=plot_residuos, 
                           title_xaxis=title_xaxis, title_yaxis=title_yaxis)
    
    residuos = var_y - (alpha_.mean() + beta_.mean() * var_x)
    
    return alpha_, beta_, residuos
# ====================================
#  Plotando a priori para verificar 
# ====================================
# Prioris
alpha_divorce = [0, 0.3]
beta_divorce = [0, 0.4]
N = 200

# Plot da Priori - Modelo linear
plot_priori_lm_normal(N, alpha_divorce, beta_divorce)
_images/parte_5_77_0.png
# ==================
#  Posteriori M ~ A
# ==================

posteriori_MA = lm(var_y=M_stdr.values, var_x=A_stdr.values, alpha=alpha_divorce, beta=beta_divorce,
                   title_yaxis= "Taxa de casamentos (M)",
                   title_xaxis= "Mediana das Idades dos Casamentos (A)")
# =============================
#  Posteriori D ~ residuos_MA
# =============================
residuos_MA = posteriori_MA[2]

posteriori_MA = lm(D_stdr.values, residuos_MA, alpha_divorce, beta_divorce, plot_residuos=False,
                   title_xaxis="Taxa de Divórcio (D)", 
                   title_yaxis="Resíduos da Taxa de Casamentos (A-residual)")
# ===================
#  Posteriori A ~ M
# ===================

posteriori_AM = lm(var_y=A_stdr.values, var_x=M_stdr.values, alpha=alpha_divorce, beta=beta_divorce, 
                   title_yaxis= "Mediana das Idades dos Casamentos (A)",
                   title_xaxis= "Taxa de casamentos (M)")
# =============================
#  Posteriori D ~ residuos_AM
# =============================
residuos_AM = posteriori_AM[2]  # Resíduos

# posteriori_AM = lm(D_stdr.values, residuos_AM, alpha_divorce, beta_divorce, plot_residuos=False)
posteriori_AM = lm(D_stdr.values, residuos_AM, alpha_divorce, beta_divorce, plot_residuos=False,
                   title_xaxis="Taxa de Divórcio (D)", 
                   title_yaxis="Resíduos da Idade da Mediana dos Casamentos (M-residual)")

Podemos observar que nos gráficos acima (o (\(2^o\)) gráfico Posteriori D ~ residuos_MA e o (\(4^o\)) gráfico Posteriori D ~ residuos_AM) que a regressão dos seus respectivos resíduos explicando a taxa de divórcio.

Existe uma forte correlação negativa no gráfico Posteriori D ~ residuos_AM, o que já ocorre no gráfico Posteriori D ~ residuos_MA. Isso ocorre por que assim existe um valor considerável da taxa de casamentos (M) que explica


(TODO: Comparar com a aula, 38:01 - Parece que estão invertidos.)

‘Controles’ estatísticos

Quando pensamos em controles estatíticos, já nos vem a mente os Designs de Experimentos, no qual configuramos quais as variáveis são possívelmente causas nos estudos observados. Não fazemos isso, não há nada ético que indique que taxa de casamento (\(M\)) é causa de algo. Isso não é ético com as outras pessoas nem conosco fazer essas coisas.

Então, nós estávemos presos a estudos observacionais para uma quantidade muito grande de problemas importantes e o que nós queremos fazer é inferencias causais no entanto, e as regressões múltiplas nos oferecem uma forma para fazer isso, apenas quando combinamos com uma ideia clara sobre quais são as relações causais entre os modelos.

Controle estatístico significa condicionar apenas as informações em uma variável e verificar se existe alguma informação valiosa!

Mas, para interpretar o efeito o efeito que acontece apartir desses controles é necessário um framework, ou seja, uma estrutura adequada tal como os DAGs ou qualquer outra coisa. Iremos ver exemplos que ao controlar algo estamos na verdade criando um confound (uma confusão). Podemos criar confound tanto quanto removê-los, nesse caso iremos remover achando que iremos ter uma resposta certa, mais tarde.

  • Regressão linear múltipla responde a questão: O quão cada preditor está associado com o resultado, uma vez que conhecemos TODOS os outros preditores?

  • Usamos modelo para construir a saída esperada - não mágica!

  • Não ser arrogante: A taxa de casamento ainda pode estar associada com a taxa de divórcio para algum subconjunto de estados.

  • Não podemos fazer uma forte inferência causal apenas com as médias, é preciso os dados indivíduais.

Gráficos contrafactuais

Os gráfico contrafactuais são mostrados quando mantemos todas as variáveis fixas e manipulamos apeans uma variável de interesse. Assim, veremos a linha de regressão mudando seu comportamento.

O objetivo é explorar as implicações dos resultados do modelo.

- Fixar os outros preditores

- Calcular a predição através do valor dos preditores

Nos gráficos vemos como o modelo vê as coisa internamente, ou seja, como ele vê as relações preditivas, assumindo que podemos brincar de Deus e definir os valores preditos para qualquer relação. Claro, qualquer relação que não esteja no mundo real.

Manipular uma dessas variáveis também irá manipular os valores das outras variáveis. Assim, se manipularmos a idade mediada dos casamentos também estaremos manipulando a taxa de casamento. Porém o contrário não é verdadeiro.

Com esses gráficos podemos definir qualquer valor que goste e ver como o modelo irá reagir a essa alteração. Isso é muito útil para saber o acontece no modelo, mas isso ainda não é inferencia causal.

Checagem da predição da posteriori

  • Objetivo: Calcular as predições implicíta para os casos observados.

    • Checar o ajuste do modelo - golems podem cometer erros.

    • Encontrar falhas na modelagem, estimular novas ideias.

Para saber a aproximação da posteriori funcionou corretamente. As vezes os ajustes da posteriori nos diram que o ajuste está muito ruim entre as previsões posteriores do modelo e os dados brutos. Isso que iremos fazer, comparar esses dois conjuntos de dados, se forem semelhantes, nós podemos ter errado algo, nosso computador pode ter errado algo ou ambos.

Precisamos rever e ajustar, as estimativas dos modelos podem estar erradas pois as vezes a natureza do modelo é complicada deixando um ambiente hostil para vida humana e temos que lutar para existir, lutando contra a entropia.


  • Sempre sobre a média da distribuição posteriori.

    • Usar apenas a média da posteriori nos leva a excesso de confiança.

    • Abraçar a incerteza.

A outra coisa que podemos fazer é olhar para os casos que não se encaixam muito bem e tentar descobrir como fazer inferências causais mais robustas.

def plot_comparative_obseved_posteriori():
    list_compared = []
    
    # Para ficar parecido com os resutados da aula, usar o credible_mass menor que 0.68 (muito demorado!)
    
    for i in range(len(M_stdr.values)):
        predict_divorce = np.random.normal(alpha_check + beta_M_check * M_stdr.values[i] + beta_A_check * A_stdr.values[i], sigma_check[i])
        hpdi = HPDI(predict_divorce, credible_mass=0.89)
        mean_posteriori = np.mean(predict_divorce)
        list_compared.append([D_stdr.values[i], mean_posteriori, hpdi[0], hpdi[1]])
    
    list_compared = pd.DataFrame(list_compared, columns=['Observed', 'Predicted', 'HPDI_min', 'HPDI_max'])
        
    plt.figure(figsize=(17, 9))
    
    # Plot mean
    plt.plot(list_compared.Observed, list_compared.Predicted, 'o', ms=7)
    
    for i in range(len(list_compared)):
        plt.plot([list_compared.Observed[i], list_compared.Observed[i]],  
                 [list_compared.HPDI_min[i], list_compared.HPDI_max[i]],
                 lw=0.4, color='blue')
        
    plt.plot(np.linspace(-2, 2), np.linspace(-2, 2), '--', color='darkblue', linewidth=3, alpha=0.5)

    plt.title('Valores da predição comparada com o observado')
    plt.xlabel('Divórcio Observado')
    plt.ylabel('Divórcio Predito')

    plt.grid(ls='--', color='white', linewidth=0.4)
    plt.show()

    
plot_comparative_obseved_posteriori()
_images/parte_5_90_0.png

O gráfico acima irá comparar os valores preditos contidos na posteriori com os valores observados. Para cada um dos estados, calculamos o intervalo de \(89\%\) de credibilidade (HPDI) e sua média.

A linha tracejada é a identidade. Para estados que tem a média em cima da linha, temos um ajuste perfeito. Já para estados longe dessa linha, como o estado ID (Idaho) o valor predito falha!

Uma das possíveis explicações do porque estamos prevendo altos valores na taxa de divóricos em um estado que temos como evidência uma taxa bem mais baixa é, a grande presença de frequentadores da A Igreja de Jesus Cristo dos Santos dos Últimos Dias, conhecidos como os Mórmons. Casamentos de pessoas dessa religião tendem a serem mais duradouros por seus motivos religiosos.

Mas para outros estados que estão mal ajustados, é necessário olhar mais de perto e entender melhor o que está acontecendo.

Associações Mascaradas

Vamos ver outra coisa interessante que a regressão pode fazer. Uma delas é conseguir revelar as correlações espúrias, como acabamos de ver na sessão anterior, com a estratificação dos controles estatísticos.

Outra coisa interessante é a de que quando temos 2 preditores influenciando na variável resposta, cada em diferentes direções, eles acabam mascarando um ao outro. Precisamos saber qual o real efeito total da causalidade, chamamos isso de associação mascarada.

  • As vezes a associação entre o resultado e o preditor pode ser mascarada por uma outra variável.

  • Necessitamos de ambos preditores para ver a sua influência causal.

  • Tende a surgir quando:

    • Outro preditor associado com a resposta que atua na direção oposta.

    • Ambos os preditores tem uma associação entre si.

E como consequência, na natureza eles escondem o efeito um dos outros. E, se não medirmos ambos, podemos ser levados a acreditar que nenhum dele importa tanto quando realmente importam.

  • Ruído nos preditores também poderá mascarar a associação (residuals confounding).

Outro tipo de associação mascarada é quando ocorre que nossas medidas contém muitos erros, assim podemos não ver qual o efeito real que está acontecendo.

Milk and Brain

Milk and Brain

Dados pode ser encontrado aqui

Na imagem acima temos três primatas, um lêmure e dois macacos. Estamos interessado, com esse conjunto de dados, em saber a associação que existe entre a energia do leite (\(\frac{Kcal}{gramas}\)), quão energético é o leite que chegam aos seus filhotes e quão inteligente (\(\%\) neocortex) cada primata é, usando como particular método de metrificação de inteligência, qual o percentual do neocortex em relação a todo o cérebro).

Uma das hipótese que temos é que, para primatas que sejam mais inteligentes, é necessário maior quantidade de energia no leite materno.

# =======================
#  Milk and Brain - Data
# =======================
milk_full = pd.read_csv('./data/milk.csv', sep=';')
milk_full
clade species kcal.per.g perc.fat perc.protein perc.lactose mass neocortex.perc
0 Strepsirrhine Eulemur fulvus 0.49 16.60 15.42 67.98 1.95 55.16
1 Strepsirrhine E macaco 0.51 19.27 16.91 63.82 2.09 NaN
2 Strepsirrhine E mongoz 0.46 14.11 16.85 69.04 2.51 NaN
3 Strepsirrhine E rubriventer 0.48 14.91 13.18 71.91 1.62 NaN
4 Strepsirrhine Lemur catta 0.60 27.28 19.50 53.22 2.19 NaN
5 New World Monkey Alouatta seniculus 0.47 21.22 23.58 55.20 5.25 64.54
6 New World Monkey A palliata 0.56 29.66 23.46 46.88 5.37 64.54
7 New World Monkey Cebus apella 0.89 53.41 15.80 30.79 2.51 67.64
8 New World Monkey Saimiri boliviensis 0.91 46.08 23.34 30.58 0.71 NaN
9 New World Monkey S sciureus 0.92 50.58 22.33 27.09 0.68 68.85
10 New World Monkey Cebuella pygmaea 0.80 41.35 20.85 37.80 0.12 58.85
11 New World Monkey Callimico goeldii 0.46 3.93 25.30 70.77 0.47 61.69
12 New World Monkey Callithrix jacchus 0.71 38.38 20.09 41.53 0.32 60.32
13 New World Monkey Leontopithecus rosalia 0.71 36.90 21.27 41.83 0.60 NaN
14 Old World Monkey Chlorocebus pygerythrus 0.73 39.17 14.65 46.18 3.47 NaN
15 Old World Monkey Miopithecus talpoin 0.68 40.15 18.08 41.77 1.55 69.97
16 Old World Monkey M fuscata 0.72 53.05 13.00 33.95 7.08 NaN
17 Old World Monkey M mulatta 0.97 55.51 13.17 31.32 3.24 70.41
18 Old World Monkey M sinica 0.79 48.90 13.91 37.19 7.94 NaN
19 Old World Monkey Papio spp 0.84 54.31 10.97 34.72 12.30 73.40
20 Ape Nomascus concolor 0.48 15.96 12.52 71.52 7.59 NaN
21 Ape Hylobates lar 0.62 34.51 12.57 52.92 5.37 67.53
22 Ape Symphalangus syndactylus 0.51 26.42 13.46 60.12 10.72 NaN
23 Ape Pongo pygmaeus 0.54 37.78 7.37 54.85 35.48 71.26
24 Ape Gorilla gorilla gorilla 0.49 27.18 16.29 56.53 79.43 72.60
25 Ape G gorilla beringei 0.53 30.59 20.77 48.64 97.72 NaN
26 Ape Pan paniscus 0.48 21.18 11.68 67.14 40.74 70.24
27 Ape P troglodytes 0.55 36.84 9.54 53.62 33.11 76.30
28 Ape Homo sapiens 0.71 50.49 9.84 39.67 54.95 75.49
# ================
#   Descrições
# ================

milk_full.describe()
kcal.per.g perc.fat perc.protein perc.lactose mass neocortex.perc
count 29.000000 29.000000 29.000000 29.000000 29.000000 17.000000
mean 0.641724 33.990345 16.403448 49.606207 14.726897 67.575882
std 0.161402 14.286670 4.846878 14.055174 24.770469 5.968612
min 0.460000 3.930000 7.370000 27.090000 0.120000 55.160000
25% 0.490000 21.220000 13.000000 37.800000 1.620000 64.540000
50% 0.600000 36.840000 15.800000 48.640000 3.470000 68.850000
75% 0.730000 46.080000 20.770000 60.120000 10.720000 71.260000
max 0.970000 55.510000 25.300000 71.910000 97.720000 76.300000
# =====================
#  Filtrando os dados 
# =====================

milk = milk_full[['kcal.per.g', 'neocortex.perc']].copy()
milk['log(mass)'] = np.log(milk_full[['mass']])
milk.head()
kcal.per.g neocortex.perc log(mass)
0 0.49 55.16 0.667829
1 0.51 NaN 0.737164
2 0.46 NaN 0.920283
3 0.48 NaN 0.482426
4 0.60 NaN 0.783902
# =====================
#  Leite dos Primatas
# =====================

pd.plotting.scatter_matrix(milk[['kcal.per.g', 'log(mass)', 'neocortex.perc']], 
                           figsize=(17, 9), marker='o', color='red',
                           hist_kwds={'bins': 20, 'rwidth': 0.9, 'color': 'red', 'alpha': 0.1}, 
                           s=100)
plt.show()
_images/parte_5_100_0.png

O que vemos aqui é uma forte correlação positiva entre o \(\%\) neocortex e a magnetude da massa. Ou seja, quanto mais massa o primata tiver, maior também será o percentual do seu cortex, e não apenas o cérebro como um todo.

A seguir vamos fazer a regressão entre essas duas variáveis.

52:51

No próximo capítulo, iremos descrever apenas os códigos.

Comecei a ler o livro do início e todas as explicações a seguir podem ser encontradas no próprio livro.

Como dito anteriormente, esse material irá fornecer todos os códigos em linguagem \(Python3\) usando diretamente a biblioteca \(Stan\) em conjunto com a wrapper \(PyStan\).

Para usuários do \(R\), exite um Wrapper para \(RStan\) para uma possível codificação sem utilizar a biblioteca \(Rethinking\) do próprio livro.


Dúvidas ou sugestões acesse a parte inicial desse material para entrar em contato.