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!