Aula 22. Como fazer tabelas profissionais no R

Tutorial completo com o pacote gtsummary

Democratização

Esta aula foi construída para que você produza tabelas prontas para relatórios, artigos e apresentações com poucas linhas de código. Os dados usados são o conjunto CO2, disponível no R base. Todo o código está funcional e pronto para reproduzir.

Acompanhe o Café com R

Escaneia o QR Code e acessa o portfólio.

Projetos no YouTube

Inscreva-se já no canal Link.

Objetivos da aula

  • Compreender o que é o pacote gtsummary e quando usá-lo
  • Criar tabelas descritivas com tbl_summary()
  • Comparar grupos com add_p() e add_difference()
  • Apresentar resultados de modelos de regressão com tbl_regression()
  • Combinar tabelas com tbl_merge() e tbl_stack()
  • Exportar tabelas para Word, PDF e HTML

O pacote gtsummary

Clique na imagem e acesse a documentação.

gtsummary foi criado por Daniel D. Sjoberg e colaboradores. Produz tabelas descritivas e de resultados de modelos prontas para publicação.

  • Versão atual: 2.5.0
  • Detecta automaticamente variáveis contínuas, categóricas e dicotômicas
  • Integrado ao ecossistema {gt}, {broom} e {labelled}
  • Documentação: danieldsjoberg.com/gtsummary

Instalação

install.packages("gtsummary")
library(gtsummary)
library(tidyverse)

O conjunto de dados CO2

Conhecendo os dados

  • O dataset CO2 está disponível no R base. Registra um experimento de absorção de CO2 em plantas de capim-bermuda submetidas a diferentes tratamentos e concentrações de dióxido de carbono.
glimpse(CO2)
Rows: 84
Columns: 5
$ Plant     <ord> Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn2, Qn2, Qn2, Qn2, Qn2, …
$ Type      <fct> Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Queb…
$ Treatment <fct> nonchilled, nonchilled, nonchilled, nonchilled, nonchilled, …
$ conc      <dbl> 95, 175, 250, 350, 500, 675, 1000, 95, 175, 250, 350, 500, 6…
$ uptake    <dbl> 16.0, 30.4, 34.8, 37.2, 35.3, 39.2, 39.7, 13.6, 27.3, 37.1, …

Variáveis do dataset CO2

CO2 |>
  head(10)
   Plant   Type  Treatment conc uptake
1    Qn1 Quebec nonchilled   95   16.0
2    Qn1 Quebec nonchilled  175   30.4
3    Qn1 Quebec nonchilled  250   34.8
4    Qn1 Quebec nonchilled  350   37.2
5    Qn1 Quebec nonchilled  500   35.3
6    Qn1 Quebec nonchilled  675   39.2
7    Qn1 Quebec nonchilled 1000   39.7
8    Qn2 Quebec nonchilled   95   13.6
9    Qn2 Quebec nonchilled  175   27.3
10   Qn2 Quebec nonchilled  250   37.1

Dicionário das variáveis

Variável Tipo Descrição
Plant Fator Identificador da planta (84 observações, 12 plantas)
Type Fator Origem da planta: Quebec ou Mississippi
Treatment Fator Tratamento aplicado: nonchilled ou chilled
conc Numérica Concentração de CO2 no ambiente (mL/L)
uptake Numérica Absorção de CO2 pela planta (umol/m²/s)

Preparação dos dados

# Selecionar variáveis de análise e traduzir rótulos
co2 <- CO2 |>
  as_tibble() |>
  select(Type, Treatment, conc, uptake) |>
  mutate(
    Type      = factor(Type,
                       levels = c("Quebec", "Mississippi")),
    Treatment = factor(Treatment,
                       levels = c("nonchilled", "chilled"),
                       labels = c("Sem resfriamento", "Com resfriamento")))

Tabela descritiva com tbl_summary()

Passo 1 - Tabela descritiva básica: código

Table 1
# tbl_summary() detecta automaticamente o tipo de cada variável
# e aplica as estatísticas adequadas: mediana e IQR para contínuas,
# frequência e percentual para categóricas
co2 |>
  tbl_summary()

Passo 1 - Tabela descritiva básica: output

Table 2
Characteristic N = 841
Type
    Quebec 42 (50%)
    Mississippi 42 (50%)
Treatment
    Sem resfriamento 42 (50%)
    Com resfriamento 42 (50%)
conc
    95 12 (14%)
    175 12 (14%)
    250 12 (14%)
    350 12 (14%)
    500 12 (14%)
    675 12 (14%)
    1000 12 (14%)
uptake 28 (18, 37)
1 n (%); Median (Q1, Q3)

Passo 2 - Personalizando estatísticas: código

Table 3
# statistic: define qual estatística usar por tipo de variável
# digits: controla casas decimais
# missing: "no" remove a linha de missing da tabela
co2 |>
  tbl_summary(
    statistic = list(
      all_continuous()  ~ "{mean} ({sd})",
      all_categorical() ~ "{n} ({p}%)"),
    digits    = list(all_continuous() ~ 1),
    missing   = "no") |>
  bold_labels()

Passo 2 - Personalizando estatísticas: output

Table 4
Characteristic N = 841
Type
    Quebec 42 (50%)
    Mississippi 42 (50%)
Treatment
    Sem resfriamento 42 (50%)
    Com resfriamento 42 (50%)
conc
    95 12 (14%)
    175 12 (14%)
    250 12 (14%)
    350 12 (14%)
    500 12 (14%)
    675 12 (14%)
    1000 12 (14%)
uptake 27.2 (10.8)
1 n (%); Mean (SD)

Passo 3 - Adicionando rótulos: código

Table 5
# label: renomeia variáveis na tabela sem alterar o dataset
co2 |>
  tbl_summary(
    label = list(
      Type      ~ "Origem",
      Treatment ~ "Tratamento",
      conc      ~ "Concentração de CO2 (mL/L)",
      uptake    ~ "Absorção de CO2 (umol/m²/s)"),
    statistic = list(
      all_continuous()  ~ "{median} ({p25}, {p75})",
      all_categorical() ~ "{n} ({p}%)"),
    missing = "no") |>
  bold_labels()

Passo 3 - Adicionando rótulos: output

Table 6
Characteristic N = 841
Origem
    Quebec 42 (50%)
    Mississippi 42 (50%)
Tratamento
    Sem resfriamento 42 (50%)
    Com resfriamento 42 (50%)
Concentração de CO2 (mL/L)
    95 12 (14%)
    175 12 (14%)
    250 12 (14%)
    350 12 (14%)
    500 12 (14%)
    675 12 (14%)
    1000 12 (14%)
Absorção de CO2 (umol/m²/s) 28 (18, 37)
1 n (%); Median (Q1, Q3)

Comparação entre grupos

Passo 4 - Comparando grupos com by=: código

Table 7
# by: estratifica a tabela por uma variável categórica
# add_n(): adiciona coluna com total de observações não ausentes
# add_p(): realiza teste estatístico e adiciona coluna de p-valor
# add_overall(): adiciona coluna com estatísticas gerais
co2 |>
  tbl_summary(
    by      = Type,
    label   = list(
      Treatment ~ "Tratamento",
      conc      ~ "Concentração de CO2 (mL/L)",
      uptake    ~ "Absorção de CO2 (umol/m²/s)"),
    statistic = list(
      all_continuous()  ~ "{median} ({p25}, {p75})",
      all_categorical() ~ "{n} ({p}%)"),
    missing = "no") |>
  add_n() |>
  add_p() |>
  add_overall() |>
  bold_labels() |>
  modify_header(label = "**Variável**")

Passo 4 - Comparando grupos com by=: output

Table 8
Variável N Overall
N = 841
Quebec
N = 421
Mississippi
N = 421
p-value2
Tratamento 84


>0.9
    Sem resfriamento
42 (50%) 21 (50%) 21 (50%)
    Com resfriamento
42 (50%) 21 (50%) 21 (50%)
Concentração de CO2 (mL/L) 84


>0.9
    95
12 (14%) 6 (14%) 6 (14%)
    175
12 (14%) 6 (14%) 6 (14%)
    250
12 (14%) 6 (14%) 6 (14%)
    350
12 (14%) 6 (14%) 6 (14%)
    500
12 (14%) 6 (14%) 6 (14%)
    675
12 (14%) 6 (14%) 6 (14%)
    1000
12 (14%) 6 (14%) 6 (14%)
Absorção de CO2 (umol/m²/s) 84 28 (18, 37) 37 (30, 40) 19 (14, 28) <0.001
1 n (%); Median (Q1, Q3)
2 Pearson’s Chi-squared test; Wilcoxon rank sum test

Passo 5 - Diferença entre grupos com add_difference(): código

Table 9
# add_difference(): calcula a diferença entre grupos e seu IC 95%
# Complementa o p-valor com a magnitude da diferença
co2 |>
  select(Type, conc, uptake) |>
  tbl_summary(
    by      = Type,
    label   = list(
      conc   ~ "Concentração de CO2 (mL/L)",
      uptake ~ "Absorção de CO2 (umol/m²/s)"),
    statistic = list(
      all_continuous() ~ "{mean} ({sd})"),
    missing = "no") |>
  add_difference() |>
  bold_labels()

Passo 5 - Diferença entre grupos: output

Table 10
Characteristic Quebec
N = 421
Mississippi
N = 421
Difference2 95% CI2 p-value2
Concentração de CO2 (mL/L)

0.00 -0.43, 0.43
    95 6 (14%) 6 (14%)


    175 6 (14%) 6 (14%)


    250 6 (14%) 6 (14%)


    350 6 (14%) 6 (14%)


    500 6 (14%) 6 (14%)


    675 6 (14%) 6 (14%)


    1000 6 (14%) 6 (14%)


Absorção de CO2 (umol/m²/s) 34 (10) 21 (8) 13 8.8, 16 <0.001
1 n (%); Mean (SD)
2 Standardized Mean Difference; Welch Two Sample t-test
Abbreviation: CI = Confidence Interval

Resultados de modelos com tbl_regression()

Passo 6 - Modelo de regressão linear: código

Table 11
# Modelo: absorção de CO2 explicada por origem, tratamento e concentração
modelo_linear <- lm(
  uptake ~ Type + Treatment + conc,
  data = co2)

# tbl_regression() detecta automaticamente o tipo de modelo
# e formata coeficientes, IC 95% e p-valores
tbl_regression(
  modelo_linear,
  label = list(
    Type      ~ "Origem",
    Treatment ~ "Tratamento",
    conc      ~ "Concentração de CO2 (mL/L)")) |>
  bold_labels() |>
  bold_p(t = 0.05)

Passo 6 - Regressão linear: output

Table 12
Characteristic Beta 95% CI p-value
Origem


    Quebec
    Mississippi -13 -15, -10 <0.001
Tratamento


    Sem resfriamento
    Com resfriamento -6.9 -9.5, -4.2 <0.001
Concentração de CO2 (mL/L) 0.02 0.01, 0.02 <0.001
Abbreviation: CI = Confidence Interval

Passo 7 - Adicionando estatísticas do modelo: código

Table 13
# add_glance_source_note(): adiciona R², AIC e outras métricas do modelo
# no rodapé da tabela
tbl_regression(
  modelo_linear,
  label = list(
    Type      ~ "Origem",
    Treatment ~ "Tratamento",
    conc      ~ "Concentração de CO2 (mL/L)")) |>
  add_glance_source_note(
    label    = list(r.squared ~ "R²", p.value ~ "p do modelo"),
    include  = c(r.squared, AIC, p.value)) |>
  bold_labels() |>
  bold_p(t = 0.05)

Passo 7 - Métricas do modelo: output

Table 14
Characteristic Beta 95% CI p-value
Origem


    Quebec
    Mississippi -13 -15, -10 <0.001
Tratamento


    Sem resfriamento
    Com resfriamento -6.9 -9.5, -4.2 <0.001
Concentração de CO2 (mL/L) 0.02 0.01, 0.02 <0.001
Abbreviation: CI = Confidence Interval
R² = 0.684; AIC = 551; p do modelo = <0.001

Combinando tabelas

Passo 8 - Modelos lado a lado com tbl_merge(): código

Table 15
# Dois modelos: um para Quebec, outro para Mississippi
mod_quebec <- lm(uptake ~ Treatment + conc,
                 data = filter(co2, Type == "Quebec"))

mod_miss   <- lm(uptake ~ Treatment + conc,
                 data = filter(co2, Type == "Mississippi"))

# tbl_merge() apresenta os dois modelos em colunas separadas
list(
  tbl_regression(mod_quebec,
                 label = list(Treatment ~ "Tratamento",
                              conc      ~ "Concentração (mL/L)")),
  tbl_regression(mod_miss,
                 label = list(Treatment ~ "Tratamento",
                              conc      ~ "Concentração (mL/L)"))) |>
  tbl_merge(
    tab_spanner = c("**Quebec**", "**Mississippi**")) |>
  bold_labels()

Passo 8 - Modelos lado a lado: output

Table 16
Characteristic
Quebec
Mississippi
Beta 95% CI p-value Beta 95% CI p-value
Tratamento





    Sem resfriamento

    Com resfriamento -3.6 -7.8, 0.62 0.093 -10 -13, -7.2 <0.001
Concentração (mL/L) 0.02 0.02, 0.03 <0.001 0.01 0.01, 0.02 <0.001
Abbreviation: CI = Confidence Interval

Passo 9 - Exportando a tabela: código

Table 17
library(flextable)

# Criar a tabela
tabela_final <- co2 |>
  tbl_summary(
    by      = Type,
    label   = list(
      Treatment ~ "Tratamento",
      conc      ~ "Concentração de CO2 (mL/L)",
      uptake    ~ "Absorção de CO2 (umol/m²/s)"),
    missing = "no") |>
  add_p() |>
  bold_labels()

# Exportar para Word (.docx)
tabela_final |>
  as_flex_table() |>
  flextable::save_as_docx(path = "tabela_co2.docx")

# Exportar para HTML
tabela_final |>
  as_gt() |>
  gt::gtsave(filename = "tabela_co2.html")

Passo 9 - Formatos de exportação

Formato Função Pacote necessário
Word (.docx) as_flex_table() + save_as_docx() flextable
HTML as_gt() + gtsave() gt
PDF (LaTeX) as_kable_extra() kableExtra
RTF as_flex_table() + save_as_rtf() flextable

tbl_summary() x tbl_regression()

Quando usar cada função

Situação Função indicada
Descrever as variáveis do dataset tbl_summary()
Comparar dois ou mais grupos tbl_summary(by = ) + add_p()
Apresentar coeficientes de regressão tbl_regression()
Comparar dois modelos lado a lado tbl_merge()
Empilhar tabelas de subgrupos tbl_stack()
Reportar estatísticas no texto inline_text()

Funções de formatação mais usadas

Função O que faz
bold_labels() Negrito nos rótulos das variáveis
bold_levels() Negrito nos níveis das variáveis categóricas
bold_p() Negrito nos p-valores significativos
italicize_labels() Itálico nos rótulos
modify_header() Renomeia cabeçalhos das colunas
add_overall() Adiciona coluna com estatísticas gerais
add_n() Adiciona coluna com total de observações
add_p() Adiciona coluna de p-valor com teste adequado
add_difference() Adiciona diferença entre grupos com IC 95%
add_glance_source_note() Adiciona métricas do modelo no rodapé

Boas práticas

  • Sempre rotule as variáveis com label = antes de compartilhar a tabela. Nomes de variáveis como conc ou uptake não comunicam para o leitor
  • Use add_p() com consciência: o p-valor informa sobre significância, não sobre magnitude. Combine com add_difference() para reportar os dois
  • Documente as escolhas de estatística: se você usou média em vez de mediana, registre o motivo nos comentários do código
  • Exporte para o formato do seu relatório: Word para relatórios corporativos, HTML para Quarto, PDF via LaTeX para artigos

Erros comuns

Situação Problema Solução
Tabela não aparece no Quarto Output não compatível com o formato Usar as_gt() para HTML e as_flex_table() para Word
add_p() retorna aviso de tamanho de amostra Grupos pequenos para o teste padrão Especificar test = list(all_continuous() ~ "wilcox.test")
Rótulos não aparecem Argumento label com nome errado Verificar se o nome da variável está correto dentro de list()

Resumo dos 9 passos

Passo Função O que produz
1 tbl_summary() Tabela descritiva básica
2 statistic = + digits = Estatísticas personalizadas
3 label = + bold_labels() Rótulos profissionais
4 by = + add_p() + add_overall() Comparação entre grupos
5 add_difference() Diferença com IC 95%
6 tbl_regression() Tabela de regressão
7 add_glance_source_note() Métricas do modelo
8 tbl_merge() Modelos lado a lado
9 as_flex_table() + as_gt() Exportação

Referências

Obrigada!

Continue praticando e explorando!

Esta aula é parte do projeto Café com R. É open source. Use, compartilhe e adapte.

Siga o Café com R

Que cada gole desperte uma nova ideia. Que cada script abra uma nova conversa. Que o Café com R se torne um ponto de encontro nosso.