[PT-BR] Introdução à Inferência Estatística

Thanks rawpixel for the pic!

Em Data Science nos normalmente falamos muito sobre Análise de Dados Exploratória (Estatística Descritiva) mas existe um outro “mundo estatístico” muito útil para nós: o ramo da Inferência Estatística.

Inferência estatística é um ramo da Estatística cujo objetivo é fazer afirmações a partir de um conjunto de valores representativo (amostra) sobre um universo (população)[1], assume-se que a população é muito maior do que o conjunto de dados observados, a amostra. Tal tipo de afirmação deve sempre vir acompanhada de uma medida de precisão sobre sua veracidade. — Inferência estatística

Nesse artigo vamos usar os dados das salas de chat do freeCodeCamp para responder a seguinte pergunta: existe alguma diferença no padrão de menção em salas de cidades diferentes?

Vamos aprender um pocuo sobre Inferência Estatística e como extrair informação de textos usando a classe Matcher do spaCy. Primeiro a gente vai ver como extrair os dados e depois usar estatística para fazer inferências.

Extraindo informação usando o spacy.Matcher

A forma de usar o Matcher é similar a forma que usamos expressões regulares (inclusive podemos usar regex para criar padrões). Cada regra pode ter muitos padrões, e um padrão consiste em uma lista de dicionários, onde cada dicionário descreve um token.

// este padrão captura todos os tokens == 'hello' (lowercase)
{'LOWER': 'hello'}

Vamos criar exemplos de informações que podemos extrair das mensagens.

Saudações

Temos 4 padrões para a mesma regra ("SAUDACAO"):

matcher = Matcher(nlp.vocab)        

self.matcher.add("SAUDACAO", None, 
                [{"LOWER": "good"}, {"LOWER": "morning"}],   
                [{"LOWER": "good"}, {"LOWER": "evening"}],     
                [{"LOWER": "good"}, {"LOWER": "afternoon"}],     
                [{"LOWER": "good"}, {"LOWER": "night"}])

matches = matcher(text)

Padrão: Mensagens com pontuação

Podemos usar todos os token attributes disponíveis como padrões. No exemplos a seguir vamos capturar toda mensagem que possui pontuação:

matcher = Matcher(nlp.vocab)

self.matcher.add("PUNTUACAO", None, 
                [{"IS_PUNCT": True}])

matches = matcher(text)

Padrão: Como as pessoas estão se sentido?

Neste exemplo os padrões começam a ficar mais interessantes. Vamos capturar todos os lemmas do verbo ‘ser’ para detectar todas as conjugações do verbo. O matcher também permite que você use quantificadores, especificados com o operador 'OP'. Vamos capturar todos os advérbios depois do verbo ser (com 'OP': '*' podemos capturar todos eles).

Depois do advérbio existem várias possibilidades para a próxima palavra, por isso vamos usar o wildcard {}, que captura qualquer palavra.

matcher = Matcher(nlp.vocab)

self.matcher.add("FEELING", None, 
                [
                 {"LOWER": "i"}, {"LEMMA":"be"},   
                 {"POS": "ADV", "OP": "*"},    
                 {"POS": "ADJ"}
                 ])

matches = matcher(text)

Padrão: Menções

Não existe um atributo para uma palavra com @ no primeiro caractere (@nick_da_pessoa), então vamos criar um.

mention_flag = lambda text: bool(
.match(text))

IS_MENTION = nlp.vocab.add_flag(mention_flag)

self.matcher.add("MENTION", None, [{IS_MENTION: True}])

matches = matcher(text)

Eu criei um conjunto de dados com as menções desta forma:

[message, mention, sent_at, city]

Você pode acessar todo o código aqui.

Introdução à Inferência Estatística

Inferência Estatística consiste e usar análise de dados para deduzir propriedades a partir de uma distribuição de probabilidade (Statistical inference, tradução nossa).

Nós temos amostras de mensagens das salas de chat e queremos compará-las. Com Estatística de Teste podemos medir a probabilidade das amostras pertenceram à mesma distribuição. Aplicando isso ao nosso cenário, se a probabilidade das menções pertenceram à mesma distribuição for menor do que um limite (definido por nós) então nós podemos inferir que pessoas de cidades diferentes possuem padrões de menção diferentes.

Vamos definir uns conceitos para clarear um pouco as coisas (todas as definições foram tiradas da Wikipedia):

Em termos gerais um teste de hipótese funciona assim:

  1. Nós assumimos que algo é verdade
  2. Depois nós tentamos provar que é impossível que o que dissemos em 1 seja verdade
  3. Se nós verificarmos que com os resultados realmente não é provável que 1 seja verdade, nós rejeitamos a hipótese

O Teste de hipóteses é um argumento de reductio ad absurdum adaptado para estatística. Na sua essência, uma hipótese é dita válida se o seu oposto for improvável”. — P-value

No nosso caso estamos lidando com variáveis categóricas (variáveis que podem assumir um valor a partir de um número fixo de possíveis valores). Por conta disso iremos usar a distribuição Qui-quadrado.

A distribuição χ2 ou qui-quadrado é uma das distribuições mais utilizadas em inferência estatística, principalmente para realizar testes de χ2. Este teste serve para avaliar quantitativamente a relação entre o resultado de um experimento e a distribuição esperada para o fenômeno. Isto é, ele nos diz com quanta certeza os valores observados podem ser aceitos como regidos pela teoria em questão. — Qui-quadrado

“Os estatísticos identificaram várias distribuições comuns, conhecidas como distribuições de probabilidade. A partir dessas distribuições é possível calcular a probabilidade de se obter um determinado valor baseado na frequência em que este valor ocorre na distribuição.” — Discovering Statistics Using R, tradução nossa

O teste qui-quadrado para homogeneidade

Queremos saber se a distribuição de menções é a mesma para cada cidade. Primeiro nós assumimos que as menções pertencem à mesma população e somamos todas a menções de cada cidade. Esta distribuição (todas as menções de todas as cidades) deve ser a mesma para cada cidade caso elas realmente pertençam à mesma população.

Nós não podemos provar que as distribuições são diferentes usando estatística, mas podemos rejeitar a hipótese delas serem iguais.

“Precisamos da hipótese nula porque não podemos provar a hipótese experimental usando estatística, mas podemos rejeitar a hipótese nula. Se os dados te dão confiança para rejeitar a hipótese nula então temos suporte para considerar a hipótese alternativa. No entanto, esteja ciente que se nós rejeitarmos a hipótese nula, isso não prova a hipótese experimentar, apenas nós dá suporte para considerá-laDiscovering Statistics Using R, tradução nossa

Esse é um ponto muito importante. Não estamos provando que a hipótese alternativa é verdade, estamos afirmando que com este nível de significância é provável que ela seja verdadeira.

“Então em vez de falarmos sobre aceitar ou rejeitar uma hipótese (o que alguns livros te mandam fazer) nós devemos falar sobre ‘as chances de se obter os resultados que obtivemos assumindo que a hipótese nula é verdadeira’.” — Discovering Statistics Using R, Tradução nossa

Em sua essência, quando coletamos dados para testar teorias nós só podemos falar em termos da probabilidade de se obter um determinado conjunto de resultados. (Field, Andy). E para julgar isso nós utilizamos o valor-p.

Vamos definir nosso nível de significância em 5% (limite do valor-p em 0.05).

Ok, agora podemos voltar ao teste.

Os dados

Vamos usar os dados de toda a atividade nas salas do freeCodeCamp no Gitter. O conjunto de dados pode ser encontrado aqui.

Os dados depois que extraimos as menções

Nosso sample possui mensagens de São Francisco, Toronto, Boston, Belgrade, Londres e São Paulo enviadas entre 16/08/2015 e 16/08/2016 (1 ano de mensagens).

Condições para o teste qui-quadrado para homogeneidade

Precisamos dessas condições para realizar o teste:

  1. O método utilizado para criar o conjunto de amostras é o de amostras aleatórias simples
  2. O valor esperado para todas as amostras é maior ou igual a 5

Vamos assumir a primeira condição é válida (1 ano de dados para cada cidade). Para verificar se a segunda condição é válida precisamos analisar os dados.

Explorando os dados

Eu criei um arquivo JSON com os dados, vamos usar a biblioteca jsonlite do R para explorar o conteúdo:

> library(jsonlite)

> df <- fromJSON("experiment_sample_data.json")

> library(mosaic)

> mentiontable <- tally(~city+mention, data=df, margins=T)
> mentiontable
              mention
city            NO YES
  Belgrade     184  45
  Boston       383 121
  London       278  98
  SanFrancisco 156  51
  SaoPaulo     153 132
  Toronto      379  81

Agora precisamos introduzir outro conceito da Estatística: tabelas de contingência.

Com o método chisq.test podemos criar tabelas de contingência e realizar o teste qui-quadrado. Vamos calcular o valor esperado para esta amostra.

Valor esperado = (soma dos dados na linha)×(soma dos dados na coluna) / valor total dos dados.

Seguindo esta fórmula, o valor esperado para o número de mensagens com menções (mention=YES) para a cidade de São Paulo é:

285*407/1557 = 74,49903

O valor expected de chisq.test nos mostra os valores esperados dado que a hipótese nula seja verdadeira pra todas as cidades:

> chisq.test(mentiontable)$expected
               mention
city                 NO       YES
  Belgrade     170.3333  58.66667
  Boston       374.8821 129.11790
  London       279.6739  96.32606
  SanFrancisco 153.9694  53.03057
  SaoPaulo     211.9869  73.01310
  Toronto      342.1543 117.84571

Todos os valores esperados são maiores que 5, então podemos realizar o teste.

O teste qui-quadrado

Nós assumimos que as distribuições do número de menções em todas as cidades são iguais, então a coluna ‘total’ é a estimativa desta distribuição:

> tally(~mention, data=df)
mention
  NO  YES 
1150  407 matcher = Matcher(nlp.vocab)

O teste qui-quadrado é usado para determinar se existe uma diferença significativa entre a frequência esperada e a frequência observada.

Para cada célula, a frequência esperada é subtraída da frequência observada, este valor é elevado ao quadrado e dividido pela frequência esperada. Os valores para todas as células são somados. Esta soma é o valor do teste qui-quadrado. — The chi-square test, tradução nossa

Com o valor do teste qui-quadrado e o grau de liberdade (quantidade_de_linhas - 1 × quantidade_de_colunas - 1) podemos calcular a probabilidade de termos esse valor ‘por acaso’.

        Pearson's Chi-squared test

data:  mentiontable
X-squared = 84.667, df = 5, p-value < 2.2e-16

O valor-p é menor que o alpha (0.05), então nós rejeitamos a hipótese nula. Isto quer dizer que com esses valores de menções para cada cidade é pouco provável que as cidades apresentem as mesmas distribuições de menções.

Nós também podemos examinar a fonte das diferenças nos padrões de menções.

Examinando o residual para encontrar a fonte das diferenças

Já que temos os valores esperados para cada cidade é possível calcular o residual: (observado - esperado) / sqrt(esperado)

O residual é a medida da variação entre o valor esperado e o valor observado, o que mostra a direção da variação (se o valor esperado foi maior ou menor, o que é interessante para interpretar os resultados). Esta medida se apresenta como uma distribuição normal, com valores de variações altas maiores que 2 ou 3. — Intermediate Statistics with R, tradução nossa

mosaicplot(mentiontable, shade=T)

Mosaic plot

Para as cidades de São Paulo e Toronto o número de mensagens com menção e sem menção está a 2.4 desvios padrão distante do valor esperado. As salas de chat de São Paulo tem mais pessoas mencionando as outras do que é esperado, e nas salas de Toronto existem menos pessoas mencionando outras do que é esperado.

Um próximo passo seria identificar a fonte dessas diferenças. Talvez por causa do número de pessoas em cada sala de chat? Ou talvez as pessoas já se conheçam e por isso elas conversam mais entre si?

Considerações finais

Na Estatística de Inferência nós fazemos deduções a partir das propriedades de uma distribuição de probabilidade. Quando você tem variáveis categóricas é possível utilizar o teste qui-quadrado para encontrar a probabilidade da distribuição ser a mesma para duas ou mais populações (ou subgrupos de uma população).

Os passos para o teste de hipótese são:

  1. Assumir a a hipótese nula é verdadeira
  2. Tentar provar que é impossível que a hipótese nula seja verdadeira
  3. Se de fato o teste mostrar que é poupo provável que a hipótese nula seja verdadeira, rejeitamos a hipótese nula.

Além disso também vimos como a classe Spacy.Matcher é útil para extrair informações de textos. Neste artigo fizemos o teste extraindo as menções para no código podemos ver outros exemplos de padrões que podem ser extraídos.

É isso! Obrigada pela leiturara! 😄