Fazendo tabelas bonitas no R
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!