Fazendo tabelas bonitas no R

Thu,27 Jan, 2022

Este post se originou do acompanhamento do vídeo do Richard Iannone (@riannone) no canal da RStudio Making Beautiful Tables with {gt}. Adaptei alguns detalhes no script que é apresentado. Algumas expressões eu traduzi de forma literal, então peço desculpas pelos erros eventuais.

Bom, para quem não conhece o pacote gt é uma ótima ajuda para montar aquela “Tabela 1” do artigo (ou todas as tabelas), colocar uma tabela em uma trabalho ou relatório. Vale a pena conferir. Aqui, vou mostrar o passo-a-passo apresentado pelo Rich no vídeo e, obviamente, alguns pacotes serão necessários como o gt e o palmerpenguins. Vou usar o novo pipe, então quem não tem a nova versão do R, utilizem o pipe do pacote magrittr mesmo.

library(gt)
library(palmerpenguins)

Iniciamos com uma tabela simples, visualizando uma parte do banco. Vejam como ela renderiza, a extensão e também as linhas, colunas…

penguins |> 
  head(10) |>
  kableExtra::kbl() |>
  kableExtra::kable_classic_2(full_width = F)
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
Adelie Torgersen 39.1 18.7 181 3750 male 2007
Adelie Torgersen 39.5 17.4 186 3800 female 2007
Adelie Torgersen 40.3 18.0 195 3250 female 2007
Adelie Torgersen NA NA NA NA NA 2007
Adelie Torgersen 36.7 19.3 193 3450 female 2007
Adelie Torgersen 39.3 20.6 190 3650 male 2007
Adelie Torgersen 38.9 17.8 181 3625 female 2007
Adelie Torgersen 39.2 19.6 195 4675 male 2007
Adelie Torgersen 34.1 18.1 193 3475 NA 2007
Adelie Torgersen 42.0 20.2 190 4250 NA 2007

Mas, e com o gt? Podemos colocar um título, subtítulo, como é? Sim, vamos colocar o título no cabeçalho da tabela. Notem que usarei uma função chamada md . Essa função é muito interessante para formatações em markdown. Poderíamos usar a função html que permite o uso de mais elementos. É claro que se você não conhece estas funções, é necessário um entendimento básico para a brincadeira ficar tão legal quanto foi para mim.

penguins |>
  dplyr::sample_frac(.05) |>
  gt::gt() |>
  gt::tab_header(
    title = gt::md("Banco de dados `penguins`"),
    subtitle = gt::md(
      "**Três** anos de observações de pinguins em *três* ilhas do arquipélago Palmer"
    )
  ) |>
  gt::opt_align_table_header(align = "left") 
Banco de dados penguins
Três anos de observações de pinguins em três ilhas do arquipélago Palmer
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
Adelie Dream 37.5 18.9 179 2975 NA 2007
Chinstrap Dream 46.1 18.2 178 3250 female 2007
Adelie Dream 40.6 17.2 187 3475 male 2009
Chinstrap Dream 49.2 18.2 195 4400 male 2007
Adelie Torgersen 38.5 17.9 190 3325 female 2009
Gentoo Biscoe 46.8 16.1 215 5500 male 2009
Adelie Biscoe 39.6 20.7 191 3900 female 2009
Adelie Dream 41.5 18.5 201 4000 male 2009
Adelie Dream 37.6 19.3 181 3300 female 2007
Gentoo Biscoe 55.1 16.0 230 5850 male 2009
Adelie Dream 36.0 17.9 190 3450 female 2007
Adelie Dream 36.4 17.0 195 3325 female 2007
Gentoo Biscoe 50.0 15.9 224 5350 male 2009
Gentoo Biscoe 46.4 15.0 216 4700 female 2008
Gentoo Biscoe 48.4 16.3 220 5400 male 2008
Gentoo Biscoe 45.7 13.9 214 4400 female 2008
Adelie Torgersen 38.6 17.0 188 2900 female 2009

Mas, como a tabela está extensa e estamos usando uma amostra dela para visualizar nossos dados, vamos seguir para algumas manipulações do banco. Assim, vamos utilizar funções do pacote do universo tidy (tidyverse) para isso, inclusive, para sumarizar nossos dados:

penguins |>
  dplyr::group_by(species) |>
  dplyr::summarize_at(
    .vars = c(
      "bill_length_mm",
      "bill_depth_mm",
      "flipper_length_mm",
      "body_mass_g"
    ),
    .funs = ~ mean(., na.rm = T)
  ) |>
  gt::gt()
species bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
Adelie 38.79139 18.34636 189.9536 3700.662
Chinstrap 48.83382 18.42059 195.8235 3733.088
Gentoo 47.50488 14.98211 217.1870 5076.016

Vou salvar este núcleo em um novo objeto para utilizamos nos próximos códigos.

tabela <- penguins |>
  dplyr::group_by(species) |>
  dplyr::summarize_at(
    .vars = c(
      "bill_length_mm",
      "bill_depth_mm",
      "flipper_length_mm",
      "body_mass_g"
    ),
    .funs = ~ mean(., na.rm = T)
  )

Agora, seguimos com mais alterações na nossa tabela para contemplar esses dados que sumarizamos. Notem que estou usando um truque com parênteses aqui. Este comando salva o que fizemos em um objeto chamado tabela e também plota o resultado na tela:

(tabela <- tabela |>
  gt::gt(rowname_col = "species") |>
  gt::tab_header(
    title = gt::md("Síntese do banco de dados `penguins`"),
    subtitle = gt::md(
      "**Três** anos de observações de pinguins em *três* ilhas do Arquipélago Palmer"
    )
  ) |>
  gt::opt_align_table_header(align = "left")) 
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
Adelie 38.79139 18.34636 189.9536 3700.662
Chinstrap 48.83382 18.42059 195.8235 3733.088
Gentoo 47.50488 14.98211 217.1870 5076.016

Bom, o nome das colunas está como o nome direto das variáveis. Vamos dar um nome à elas:

(tabela <- tabela |>
  gt::cols_label(
    bill_length_mm = gt::html("Comprimento<br>médio do<br>bico (<em>mm</em>)"),
    bill_depth_mm = gt::html("Profundidade<br>média do<br>bico (<em>mm</em>)"),
    flipper_length_mm = gt::html("Comprimento<br>médio da<br> nadadeira (<em>mm</em>)"),
    body_mass_g = gt::html("Média da<br>massa<br>corporal (<em>g</em>)"),
    
  ))
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (g)
Adelie 38.79139 18.34636 189.9536 3700.662
Chinstrap 48.83382 18.42059 195.8235 3733.088
Gentoo 47.50488 14.98211 217.1870 5076.016

Também vamos diminuir o número de dígitos das médias, que tal? Vamos fazer para as quatro colunas de dados e, por isso, podemos usar a função everything do pacote gt.

(tabela <- tabela |>
  gt::fmt_number(columns = gt::everything()))
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (g)
Adelie 38.79 18.35 189.95 3,700.66
Chinstrap 48.83 18.42 195.82 3,733.09
Gentoo 47.50 14.98 217.19 5,076.02


A coluna de média de massa corporal está em gramas. Vamos alterar para quilograma. Vamos mudar a label da coluna também. Não se preocupe com a linha anterior que definimos a label. O pacote substitui naturalmente. Perceba como alteramos a escala do valor. É um detalhe que serve somente para este tipo de dado em um contexto parecido (gramas para quilogramas).

(
  tabela <- tabela |>
    gt::fmt_number(columns = body_mass_g, scale_by = 1 / 1000) |>
    gt::cols_label(
      body_mass_g = gt::html("Média da<br>massa<br>corporal (<em>kg</em>)")
    )
)
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo 47.50 14.98 217.19 5.08


Podemos alterar um pouco a largura das colunas.

(
  tabela <- tabela |>
    gt::cols_width(
      bill_length_mm ~ gt::px(130),
      bill_depth_mm ~ gt::px(130),
      flipper_length_mm ~ gt::px(150),
      body_mass_g ~ gt::px(120)
    )
)
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo 47.50 14.98 217.19 5.08


Agora, vamos colocar a fonte dos dados. É comum que esta informação apareça como uma nota no rodapé da tabela. O pacote gt também possui uma função para isso. Fiquei impressionado pela quantidade de funções disponíveis para formatar a tabela.

(tabela <- tabela |>
   gt::tab_source_note(
     source_note = gt::md("Fonte: Banco de dados do pacote `palmerpenguis`")
   ))
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis


Se quiséssemos colocar alguma nota, alguma referência em alguma célula da tabela, para adicionar alguma informação ou esclarecer algum detalhe, é possível também. Vamos fazer uma referência na espécie Gentoo. Apenas como exercício.

(
  tabela <- tabela |>
    gt::tab_footnote(
      footnote = "A maior das três espécies do estudo.",
      locations = gt::cells_stub(rows = "Gentoo")
    )
)
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo1 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis

1 A maior das três espécies do estudo.


Perceba que podemos incluir várias notas de rodapé.

tabela |>
  gt::tab_footnote(
    footnote = "O tamanho da nadadeira foi medida por uma fita métrica.",
    locations = gt::cells_column_labels(columns = flipper_length_mm)
  ) |>
  gt::tab_footnote(
    footnote = "A fita métrica sofreu alguns estragos com o frio.",
    locations = gt::cells_column_labels(columns = flipper_length_mm)
  )
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)1,2
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo3 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis

1 O tamanho da nadadeira foi medida por uma fita métrica.

2 A fita métrica sofreu alguns estragos com o frio.

3 A maior das três espécies do estudo.


Inclusive, podemos definir quais marcadores serão usados. Isso é muito útil em tabelas para artigos científicos que queremos, às vezes, destacar um valor de p, um intervalo de confiança.

(
tabela |>
    gt::tab_footnote(
      footnote = "Medições realizadas com uma fita métrica.",
      locations = gt::cells_column_labels(columns = flipper_length_mm)
    ) |>
    gt::tab_footnote(
      footnote = "A fita métrica sofreu alguns estragos com o frio.",
      locations = list(
        gt::cells_body(columns = flipper_length_mm),
        gt::cells_column_labels(columns = bill_length_mm)
        
      )
    ) |>
    gt::opt_footnote_marks(marks = c("*", "**", "&"))
)
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Comprimento
médio do
bico (mm)*
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)**
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95* 3.70
Chinstrap 48.83 18.42 195.82* 3.73
Gentoo& 47.50 14.98 217.19* 5.08
Fonte: Banco de dados do pacote palmerpenguis

* A fita métrica sofreu alguns estragos com o frio.

** Medições realizadas com uma fita métrica.

& A maior das três espécies do estudo.

Nossa tabela ainda não possui um rótulo para a primeira coluna, que apresentas espécies dos pinguins. Esta alteração é simples e possui as mesmas configurações possíveis mostradas acima.

(tabela <- tabela |>
  gt::tab_stubhead(label = gt::md("*Espécies* de pinguim")))
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Espécies de pinguim Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo1 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis

1 A maior das três espécies do estudo.


Além disso, podemos alterar a fonte e seus estilos.

tabela |>
  gt::opt_table_font(font = gt::google_font("Montserrat"),
                     weight = 600,
                     style = "italic")
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Espécies de pinguim Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo1 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis

1 A maior das três espécies do estudo.


Outra forma de destaque disponível no pacote gt é o uso de cores em linhas ou colunas. No próximo exemplo, definimos que a última linha e em todas as colunas desta linha, o destaque será com um tom de azul, cor da fonte branca e um estilo mais forte.

(tabela |>
  gt::tab_style(
    locations = gt::cells_body(columns = gt::everything(),
                               rows = "Gentoo"),
    style = list(
      gt::cell_fill(color = "#5599FF"),
      gt::cell_text(color = "white", weight = "bold")
    )
  ))
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Espécies de pinguim Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo1 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis

1 A maior das três espécies do estudo.


Já neste exemplo, utilizamos algo mais sofisticado, digamos assim, para definir uma escala de cores para uma coluna específica.

tabela |>
  gt::data_color(
    columns = bill_depth_mm,
    colors = scales::col_numeric(
      palette = c("red","orange","green"),
      domain = NULL
    )
  )
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Espécies de pinguim Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo1 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis

1 A maior das três espécies do estudo.


Cada coluna pode ter um estilo de cores diferentes.

tabela |>
  gt::data_color(
    columns = bill_depth_mm,
    colors = scales::col_numeric(
      palette = c("red","orange","green"),
      domain = NULL
    )
  ) |>
    gt::data_color(
    columns = bill_length_mm,
    colors = scales::col_numeric(
      palette = c("red","orange","green"),
      domain = c(30, 50)
    )
  )
Síntese do banco de dados penguins
Três anos de observações de pinguins em três ilhas do Arquipélago Palmer
Espécies de pinguim Comprimento
médio do
bico (mm)
Profundidade
média do
bico (mm)
Comprimento
médio da
nadadeira (mm)
Média da
massa
corporal (kg)
Adelie 38.79 18.35 189.95 3.70
Chinstrap 48.83 18.42 195.82 3.73
Gentoo1 47.50 14.98 217.19 5.08
Fonte: Banco de dados do pacote palmerpenguis

1 A maior das três espécies do estudo.

E finalizamos junto com o vídeo. Se desejarem saber um pouco mais sobre o banco de dados de pinguins veja aqui. Assim como o site do pacote gt aqui. Inclusive tem umas figuras legais sobre a estrutura que o pacote enxerga na tabela:

Bom trabalho!