Pular para o conteúdo principal

Tutorial de tabelas de contingência em R

Neste tutorial, você vai aprender a criar tabelas de contingência e a testar e quantificar as relações visíveis nelas.
Atualizado 17 de set. de 2026  · 10 min lido

Explorar com IA

ChatGPTClaudePerplexity

Como criar uma tabela

Para começar, vamos pegar alguns dados. O pacote MASS traz informações sobre 93 carros à venda nos EUA em 1993. Eles estão no objeto Cars93 e incluem 27 variáveis para cada carro, algumas delas categóricas. Então vamos carregar o pacote MASS e ver os tipos de veículos presentes em cars93:

library(MASS)
Cars93$Type

##  [1] Small   Midsize Compact Midsize Midsize Midsize Large   Large  
##  [9] Midsize Large   Midsize Compact Compact Sporty  Midsize Van    
## [17] Van     Large   Sporty  Large   Compact Large   Small   Small  
## [25] Compact Van     Midsize Sporty  Small   Large   Small   Small  
## [33] Compact Sporty  Sporty  Van     Midsize Large   Small   Sporty
## [41] Sporty  Small   Compact Small   Small   Sporty  Midsize Midsize
## [49] Midsize Midsize Midsize Large   Small   Small   Compact Van    
## [57] Sporty  Compact Midsize Sporty  Midsize Small   Midsize Small  
## [65] Compact Van     Midsize Compact Midsize Van     Large   Sporty
## [73] Small   Compact Sporty  Midsize Large   Compact Small   Small  
## [81] Small   Compact Small   Small   Sporty  Midsize Van     Small  
## [89] Van     Compact Sporty  Compact Midsize
## Levels: Compact Large Midsize Small Sporty Van

Temos 6 tipos de carros ali. A função table mostra quantos há de cada tipo:

table(Cars93$Type)

##
## Compact   Large Midsize   Small  Sporty     Van
##      16      11      22      21      14       9

prop.table converte em frações:

prop.table(table(Cars93$Type))

##
##    Compact      Large    Midsize      Small     Sporty        Van
## 0.17204301 0.11827957 0.23655914 0.22580645 0.15053763 0.09677419

O mesmo com a origem dos carros:

table(Cars93$Origin)

##
##     USA non-USA
##      48      45

prop.table(table(Cars93$Origin))

##
##      USA  non-USA
## 0.516129 0.483871

Como criar uma tabela de contingência

Ótimo, vimos que nosso conjunto de dados tem números parecidos de carros dos EUA e de fora dos EUA e que os tipos mais comuns são Midsize e Small. Mas será que os modelos dos EUA e de fora diferem em tipo?

Vamos olhar os tipos de carros em relação à sua origem. Podemos usar table de novo, agora com dois argumentos. O primeiro vira a variável de linha e o segundo, a variável de coluna:

table(Cars93$Type, Cars93$Origin)

##          
##           USA non-USA
##   Compact   7       9
##   Large    11       0
##   Midsize  10      12
##   Small     7      14
##   Sporty    8       6
##   Van       5       4

Agora vimos o que todo mundo sabe: americanos adoram veículos grandes! A tabela acima mostra a distribuição conjunta de duas variáveis categóricas (Type e Origin). Tabelas assim são chamadas de tabelas de contingência.

Como obter marginais de uma tabela de contingência

As funções rowSums e colSums são autoexplicativas

(tab1<-table(Cars93$Type, Cars93$Origin))

##          
##           USA non-USA
##   Compact   7       9
##   Large    11       0
##   Midsize  10      12
##   Small     7      14
##   Sporty    8       6
##   Van       5       4

rowSums(tab1)

## Compact   Large Midsize   Small  Sporty     Van
##      16      11      22      21      14       9

colSums(tab1)

##     USA non-USA
##      48      45

Como obter percentuais de uma tabela de contingência

prop.table aninhado com table retorna as frequências

prop.table(table(Cars93$Type, Cars93$Origin))

##          
##                  USA    non-USA
##   Compact 0.07526882 0.09677419
##   Large   0.11827957 0.00000000
##   Midsize 0.10752688 0.12903226
##   Small   0.07526882 0.15053763
##   Sporty  0.08602151 0.06451613
##   Van     0.05376344 0.04301075

Para converter em percentuais, é só multiplicar por 100:

prop.table(table(Cars93$Type, Cars93$Origin))*100

##          
##                 USA   non-USA
##   Compact  7.526882  9.677419
##   Large   11.827957  0.000000
##   Midsize 10.752688 12.903226
##   Small    7.526882 15.053763
##   Sporty   8.602151  6.451613
##   Van      5.376344  4.301075

Observe que esta é uma distribuição de probabilidade conjunta, pela qual podemos ver, por exemplo, que cerca de 7,5% dos carros são pequenos e de origem americana.

Com mais frequência, nos interessa a distribuição de uma variável dentro dos grupos formados por outra. Aqui, a distribuição dos tipos entre carros dos EUA e (separadamente) de fora dos EUA parece interessante. Para isso, usamos o argumento margin da função prop.table. Ele indica onde está a variável de agrupamento: nas linhas (margin=1) ou nas colunas (margin=2):

prop.table(table(Cars93$Type, Cars93$Origin), margin=2)*100

##          
##                 USA   non-USA
##   Compact 14.583333 20.000000
##   Large   22.916667  0.000000
##   Midsize 20.833333 26.666667
##   Small   14.583333 31.111111
##   Sporty  16.666667 13.333333
##   Van     10.416667  8.888889

Agora dá para ver facilmente que carros pequenos são duas vezes mais frequentes na parte não EUA do nosso conjunto de dados do que na parte EUA.

Repare também que os percentuais somam 100 nas colunas, enquanto na tabela de distribuição conjunta (aquela sem o argumento margin), 100 era a soma da tabela inteira.

(tab2<-prop.table(table(Cars93$Type, Cars93$Origin), margin=2)*100)

##          
##                 USA   non-USA
##   Compact 14.583333 20.000000
##   Large   22.916667  0.000000
##   Midsize 20.833333 26.666667
##   Small   14.583333 31.111111
##   Sporty  16.666667 13.333333
##   Van     10.416667  8.888889

colSums(tab2)

##     USA non-USA
##     100     100

Teste qui-quadrado

A pergunta mais comum ao analisar tabelas de contingência é se as variáveis de linha e coluna são independentes. A forma mais básica de responder é rodar um teste qui-quadrado. Ele é explicado em detalhes neste tutorial. Vamos verificar se Type e Origin são independentes:

chisq.test(Cars93$Type, Cars93$Origin)

## Warning in chisq.test(Cars93$Type, Cars93$Origin): Chi-squared
## approximation may be incorrect

##
##  Pearson's Chi-squared test
##
## data:  Cars93$Type and Cars93$Origin
## X-squared = 14.08, df = 5, p-value = 0.01511

Ao que parece, não são, mas também recebemos o aviso Chi-squared approximation may be incorrect. Isso acontece porque a estatística qui-quadrado segue a distribuição qui-quadrado apenas aproximadamente. Quanto mais observações temos, melhor é a aproximação. A função chisq.test mostra esse aviso sempre que uma das contagens esperadas for menor que 5 (para o que é uma "contagem esperada", veja o tutorial linkado acima).

Teste exato de Fisher

O teste exato de Fisher é uma alternativa ao qui-quadrado, usado principalmente quando a aproximação do qui-quadrado não é satisfatória. Vamos rodá-lo:

fisher.test(Cars93$Type, Cars93$Origin)

##
##  Fisher's Exact Test for Count Data
##
## data:  Cars93$Type and Cars93$Origin
## p-value = 0.007248
## alternative hypothesis: two.sided

Os resultados são bem parecidos com os do qui-quadrado, mas isso nem sempre acontece. Uma grande desvantagem do teste de Fisher é que, para tabelas (ou amostras) grandes, ele se torna computacionalmente ineficiente.

G-test

Outra alternativa é o chamado G-test. Sua estatística também é aproximadamente qui-quadrado, mas para amostras pequenas essa aproximação costuma ser melhor do que a do teste qui-quadrado. Para o G-test, podemos usar a função GTest do pacote DescTools. Os resultados novamente são semelhantes aos dois testes anteriores: Type e Origin não são independentes.

library(DescTools)
GTest(Cars93$Type, Cars93$Origin)

##
##  Log likelihood ratio (G-test) test of independence without
##  correction
##
## data:  Cars93$Type and Cars93$Origin
## G = 18.362, X-squared df = 5, p-value = 0.002526

Correção de Yates

Em tabelas de contingência 2x2, o teste qui-quadrado pode ser aprimorado com a correção de continuidade de Yates. Ela simplesmente subtrai 0,5 de cada termo | Observado - Esperado | na estatística qui-quadrado. Se precisar, consulte novamente este tutorial. Além disso, o R aplica a correção de Yates automaticamente sempre que necessário. Vamos olhar a disponibilidade de versões com câmbio manual em carros dos EUA e de fora dos EUA:

(tab3<-table(Cars93$Man.trans.avail, Cars93$Origin))

##      
##       USA non-USA
##   No   26       6
##   Yes  22      39

chisq.test(tab3)

##
##  Pearson's Chi-squared test with Yates' continuity correction
##
## data:  tab3
## X-squared = 15.397, df = 1, p-value = 8.712e-05

A correção de Yates foi aplicada automaticamente, e o R nos avisou sobre isso.

Tabela tridimensional (ou com mais dimensões)

Vamos analisar Man.trans.avail, Origin e Type de uma vez. table divide o conjunto de dados segundo a variável fornecida como terceira:

table(Cars93$Man.trans.avail, Cars93$Origin, Cars93$Type)

## , ,  = Compact
##
##      
##       USA non-USA
##   No    2       0
##   Yes   5       9
##
## , ,  = Large
##
##      
##       USA non-USA
##   No   11       0
##   Yes   0       0
##
## , ,  = Midsize
##
##      
##       USA non-USA
##   No    9       4
##   Yes   1       8
##
## , ,  = Small
##
##      
##       USA non-USA
##   No    0       0
##   Yes   7      14
##
## , ,  = Sporty
##
##      
##       USA non-USA
##   No    0       0
##   Yes   8       6
##
## , ,  = Van
##
##      
##       USA non-USA
##   No    4       2
##   Yes   1       2

ftable dá uma visão mais compacta:

ftable(Cars93$Man.trans.avail, Cars93$Origin, Cars93$Type)

##              Compact Large Midsize Small Sporty Van
##                                                    
## No  USA            2    11       9     0      0   4
##     non-USA        0     0       4     0      0   2
## Yes USA            5     0       1     7      8   1
##     non-USA        9     0       8    14      6   2

Teste de Cochran-Mantel-Haenszel

Pelos resultados de table acima, deve estar claro que a relação entre Origin e Man.trans.avail varia conforme o Type. Para carros pequenos e esportivos, por exemplo, não há associação: todo carro pequeno ou esportivo, seja dos EUA ou de fora, tem versão manual. Por outro lado, a maioria dos carros midsize dos EUA não tem versão manual, enquanto a maioria dos não EUA desse tipo tem. Para considerar relações possivelmente diferentes entre estratos, pode-se usar o teste de Cochran-Mantel-Haenszel:

mantelhaen.test(Cars93$Man.trans.avail, Cars93$Origin, Cars93$Type)

##
##  Mantel-Haenszel chi-squared test with continuity correction
##
## data:  Cars93$Man.trans.avail and Cars93$Origin and Cars93$Type
## Mantel-Haenszel X-squared = 8.0153, df = 1, p-value = 0.004638
## alternative hypothesis: true common odds ratio is not equal to 1
## 95 percent confidence interval:
##   2.226531 76.891307
## sample estimates:
## common odds ratio
##          13.08438

O terceiro argumento passado a mantelhaen.test identifica os estratos. Compare os resultados acima com aqueles sem estratificação (exemplo da correção de Yates). A associação continua presente, mas a evidência é bem mais fraca.

Medidas de associação

Depois de identificar algumas associações entre variáveis, é hora de medir sua intensidade. Existem diversas medidas possíveis. Muitas delas estão descritas aqui. Agora vou focar nas duas mais usadas.

V de Cramer

V é baseada na estatística qui-quadrado:

$$ V = \sqrt{\frac{\chi^2/N}{\min(C-1, R-1)}}, $$ onde:

  • N é o total geral da tabela de contingência (soma de todas as células),
  • C é o número de colunas
  • R é o número de linhas.

V ∈ [0; 1]. Quanto maior o V, mais forte é a relação entre as variáveis. V = 0 pode ser interpretado como independência (já que V = 0 se e somente se χ2 = 0). A principal limitação do V é a falta de uma interpretação precisa. V = 0,6 indica associação forte, moderada ou fraca?

A função CramerV do DescTools pode calcular isso para a gente:

CramerV(Cars93$Type, Cars93$Origin)

## [1] 0.3890967

Mais uma vez: isso é uma associação forte, moderada ou fraca?

Lambda de Goodman e Kruskal

A lambda de Goodman e Kruskal é um exemplo de medida baseada na redução proporcional da variação. Esse tipo de medida busca imitar o R2 — o coeficiente de determinação da regressão linear:

  • assume valores em [0, 1]
  • expressa a fração da variação explicada pela variável independente.

Usando a variável de coluna como independente, a fórmula da lambda de Goodman e Kruskal é: $$\lambda = \frac{L - \sum_j L_j}{L},$$ onde:

  • Lj é a soma das frequências não modais na j-ésima coluna,
  • L é a soma das frequências não modais na coluna "total"

O melhor é ver com um exemplo. Vamos olhar Type e Origin, sendo esta a independente:

table(Cars93$Type, Cars93$Origin)

##          
##           USA non-USA
##   Compact   7       9
##   Large    11       0
##   Midsize  10      12
##   Small     7      14
##   Sporty    8       6
##   Van       5       4

A moda da coluna EUA é 11, então L1 = 7 + 10 + 7 + 8 + 5 = 37. A moda da coluna não EUA é 14, então L2 = 9 + 0 + 12 + 6 + 4 = 31. A coluna "Total" é

rowSums(table(Cars93$Type, Cars93$Origin))

## Compact   Large Midsize   Small  Sporty     Van
##      16      11      22      21      14       9

A moda é 22, então L = 16 + 11 + 21 + 14 + 9 = 71 e $$\lambda = \frac{71 - (37+31)}{71}=0.042$$

Podemos usar a função Lambda do pacote DescTools em vez disso:

Lambda(Cars93$Type, Cars93$Origin, direction='row')

## [1] 0.04225352

O parâmetro direction define onde está a variável dependente (row ou column).

Origin explica cerca de 4% da variação em Type. Repare pela fórmula que a lambda define variação como a dicotomia entre pertencer e não pertencer ao maior grupo.

Vale notar que a lambda é zero sempre que a categoria modal de cada coluna é a mesma. Veja esta tabela:

(lambdaTab<-cbind(c(0,100), c(49,51)))

##      [,1] [,2]
## [1,]    0   49
## [2,]  100   51

chisq.test(lambdaTab)

##
##  Pearson's Chi-squared test with Yates' continuity correction
##
## data:  lambdaTab
## X-squared = 62.279, df = 1, p-value = 2.981e-15

A categoria modal é a mesma em cada coluna (é a segunda linha). Assim, a lambda deve ser zero apesar da associação significativa e visível:

Lambda(lambdaTab, direction='row')

## [1] 0

Se você quiser se aprofundar em estatística com R, faça o curso Statistical Modeling in R (Part 1) da DataCamp.

Tópicos
R
Ciência de dados

Cursos de R

Curso

Introdução ao R

4 h
3.1M
Domine os conceitos básicos de análise de dados em R, incluindo vetores, listas e quadros de dados, e pratique o R com conjuntos de dados reais.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado

Tutorial

Tutorial de regressão linear no R

Neste tutorial, você aprenderá os fundamentos de um modelo estatístico muito popular: a regressão linear.

Eladio Montero Porras

15 min

Tutorial

Teste de qui-quadrado em planilhas

Neste tutorial, você aprenderá a realizar o teste qui-quadrado em planilhas.
Avinash Navlani's photo

Avinash Navlani

10 min

Tutorial

Testes T no tutorial do R: Saiba como realizar testes T

Determine se há uma diferença significativa entre as médias dos dois grupos usando t.test() no R.
Abid Ali Awan's photo

Abid Ali Awan

10 min

Tutorial

Criação de modelos de redes neurais (NN) em R

Neste tutorial, você aprenderá a criar um modelo de rede neural no R.
Abid Ali Awan's photo

Abid Ali Awan

10 min

Tutorial

Como criar tabelas de datas no Power BI Tutorial

Saiba como criar tabelas de datas no Power BI com este tutorial visual passo a passo.
Kafaru Simileoluwa's photo

Kafaru Simileoluwa

12 min

multiple linear regression

Tutorial

Regressão linear múltipla no R: Tutorial com exemplos

Uma visão geral completa para entender as regressões lineares múltiplas no R por meio de exemplos.
Zoumana Keita 's photo

Zoumana Keita

12 min

Ver MaisVer Mais