top of page
  • Foto do escritorFernanda Kelly

Pacote: qwraps2

Vou começar esse post com a seguinte pergunta:


Você já conhecia esse pacote? Ou já ouviu falar?

Se você não tem o hábito de acompanhar pessoas do universo da linguagem R em redes sociais ou comunidades como a R-Ladies do BR (ou do mundo) ou a curso-r , provavelmente você nunca ouviu falar desse pacote que é uma mão na roda quando o assunto é construção de tabelas estruturadas.


A sua essência é simples e é exatamente isso que usuários iniciantes do R carecem quando se é necessário construir uma tabela para artigos, dissertações e/ou teses que possuem um padrão pré-determinado.


Então vamos lá conhecer esse pacote pioneiro em construção de tabelas estruturadas...

O pacote qwraps2 foi desenvolvido e publicado no início de 2021 pelo bioestatístico Peter E. DeWitt e, assim como eu, ele conta que a necessidade de criar tabelas de resumo para análises de dados, rascunhos de manuscritos ou até mesmo trabalhar em sua própria dissertação, era de uma relevância sem tamanho. E detalhe, Peter conta que quase todos os documentos que ele produz é feito utilizando algum subconjunto do R, knitr, pandoc, LaTeX ou markdown, algo que facilita e MUITO o compartilhamento e visualização de qualquer trabalho. Ou seja,


Consequentemente as tabelas desse pacote são reproduzíveis em rmarkdown e LaTeX?

SIM! É exatamente isso que estou falando. Como cada caso é um caso, dependendo da sua escolha você deve definir o formato que é desejado como saída para a sua tabela. Essa essa escolha é feita através do parâmetro options na linha de comando anterior a tabela. Se for em rmarkdown, é uma boa prática colocar essa opção no preâmbulo. Veja abaixo a forma como você deve definir o formato de sua saída.


install.packages("qwraps2")
library(qwraps2)
options(qwraps2_markup = "markdown")
options(qwraps2_markup = "Latex")

E, para fins de exemplo, o pacote que vamos utilizar é o dados. Este pacote disponibiliza a tradução de conjuntos de dados em inglês originalmente disponíveis em outros pacotes do R e que, em especial, os dados traduzidos são utilizados nos exemplos do livro R for Data Science de Hadley Wickham & Garrett Grolemund. Logo, dentro deste pacote, vamos utilizar o banco de dados denominado por comuns que possui informações sobre modelos comuns de carros.

install.packages("dados")
install.packages("dplyr")
library(dados)
library(dplyr)

O quadro de dados pode ser observado abaixo.

> dplyr::glimpse(dados::comuns)
Rows: 347
Columns: 4
$ marca             <chr> "Acura", "Acura", "Acura", "Acura", "Acura", "Audi", "Audi", "Audi", "Audi", "Aud…
$ modelo            <chr> "Integra", "Legend", "MDX 4WD", "NSX", "TSX", "A4", "A4 Avant quattro", "A4 quatt…
$ total_modelos     <int> 42, 28, 12, 28, 27, 49, 49, 66, 20, 12, 46, 20, 30, 29, 23, 20, 12, 15, 21, 30, 2…
$ total_modelos_ano <int> 16, 10, 12, 14, 11, 19, 15, 19, 19, 12, 20, 15, 16, 16, 15, 14, 11, 11, 10, 14, 1…

Você conseguirá observar que há quatro variáveis neste banco de dados. Duas são categóricas, mas que de acordo com o dplyr::glimpse são variáveis do tipo character e há duas variáveis de classe integer que denotam a quantidade de cada modelo.


Se o formato escolhido para a saída for o LaTeX, sua saída será de acordo com o exemplo abaixo. Veja que a formatação da tabela já se encontra identada de acordo com a sua solicitação e, claro, de acordo com o banco de dados. É só copiar e colar na plataforma que você utiliza para renderizar o LaTeX sua tabela estará perfeita.


\begin{tabular}{l|l}
\hline
 & comuns (N = 347)\\
\hline
\bf{Years} & ~\\
\hline
~~ Total modelos – median (IQR) & 33 (20.00, 50.00)\\
\hline
~~ Total modelos por ano – mean (SD) & 14.88 (5.11)\\
\hline
\bf{Gender} & ~\\
\hline
~~ Marca – no/total no. (%) & 11/347 (3.17\%)\\
\hline
~~ Modelo – no/total no. (%) & 0.29\% (n = 347non-missing)\\
\hline
\end{tabular}

E se o formatato desejado é o rmarkdown, o estilo de saída é um pouco diferente, visto que no rmarkdown podemos utilizar de diversos outros pacotes para 'embelezar' essas saídas. Abaixo estamos utilizando o formato de saída mais simples.


                                         comuns (N = 347)
Vendas     
Total modelos – median (IQR)            33 (20.00, 50.00)   
Total modelos por ano – mean (SD)       14.88 (5.11)
Especificações
Marca – no/total no. (%)                11/347 (3.17%)
Modelo – no/total no. (%)               0.29% (n = 347non-missing)

Neste caso, um 'pulo do gato' que aprendi ao longo do trabalho, é especificar na chunk do rmarkdown o parâmetro results = "asis" para a saída ser renderizada corretamente. Depois faz um teste aí com e sem essa especificação e conta nos comentários o que você achou sobre a dica.


É sensacional né?

Que as saídas são sensacionais a gente já sabe, mas naturalmente você deve estar se perguntando tode estusiasmade...


Como eu faço isso?

Nosso primeiro passo é conhecer as funções que o pacote qwraps2 traz e entender o que cada uma faz. Vale ressaltar que o Peter desenvolveu esse pacote com o intuito dele ser o mais flexível e geral possível. Os quadros de dados podem ser resumidos com qualquer função, por exemplo, mean, median, até mesmo funções escritas por você. Então vamos iniciar com as funções mean_ci, mean_sd e median_iqr.


A nomemclatura das função são bem intuitivas, mas há alguns parâmetros importantes e que ajudam a trazer informações mais completas à sua tabela. A partir daqui todos os exemplos serão compilados no formato rmarkdown tá? Não vai se assustar quando for reproduzir.


A função mean_ci traz para nós a média e o intervalo de confiança. Para o intervalo de confiança podemos fazer pelo padrão normal ou pelo não paramétrico e, para isso, temos que identificar qual é o de nosso interesse informando-o no parâmetro qdist. O default utilizado para o padrão normal é o simétrico (100(1-alpha)%), ou seja, a saída é dada pela média, LCL e UCL. Veja no exemplo abaixo que esse intervalo pode ser solicitado de formas diferentes.


our_summary <-  
  list("Vendas" =
         list("Total modelos por ano – mean (CI)" = 
              ~ qwraps2::mean_ci(total_modelos,  qdist = stats::qnorm,
                                 na_rm = TRUE),
              "Total modelos por ano – mean (CI)" = 
              ~ qwraps2::mean_ci(total_modelos,  qdist = stats::qt,
                                 qdist.args = list(df = 31),
                                 na_rm = TRUE)))

A saída para o our_summary é de acordo com o padrão abaixo.


options(qwraps2_markup = "markdown")
table <- qwraps2::summary_table(comuns, our_summary)

Conseguiu pegar que você sempre vai precisar de um our_summary que contém as listas e especificações das variáveis e funções que você deseja aplicar?

É assim que o pacote qwraps2 funciona.


  1. Construa a lista de informações que você deseja extrair de cada variável;

  2. Solicite a sumarização de acordo com o banco de dados e a lista construída.


Vamos para a função mean_sd, que, geralmente, é a mais utilizada em artigos. A sua parametrização é um pouco diferente. Nós precisamos fornecer ao parâmetro denote_sd o que nós desejamos como formatação. Há duas opções, a opção pm e a opção paren. Você também pode utilizar a formatação do parâmetro digits para adequar o número de digitos após a vírgula de cada função. Esse parâmetro é semelhante ao base::round. Observe a construção do our_summary abaixo.


our_summary1 <-  
  list("Vendas" =
         list("Total modelos por ano – mean (SD)" =
              ~ qwraps2::mean_sd(total_modelos_ano,
                                 show_n = "ifNA",                                                                          
                                 na_rm = TRUE),
              "Total modelos por ano – mean (SD)" =
              ~ qwraps2::mean_sd(total_modelos_ano,                                              
                                 denote_sd = "pm",
                                 na_rm = TRUE),
              "Total modelos por ano – mean (SD)" =
               ~ qwraps2::mean_sd(total_modelos_ano,
                                  denote_sd = "paren",                                                                       
                                  na_rm = TRUE),
              "Total modelos por ano – mean (SD)" = 
              ~ qwraps2::mean_sd(total_modelos_ano,
                                 denote_sd = "paren",                                                                          
                                 digits = getOption("qwraps2_frmt_digits",
                                                    2), 
                                 na_rm = TRUE)))

O resultado do our_summary segue abaixo.


                                     comuns (N = 347)
Vendas   
Total modelos por ano – mean (SD)    14.88 ± 5.11   
Total modelos por ano – mean (SD)    14.88 ± 5.11   
Total modelos por ano – mean (SD)    14.88 (5.11)   
Total modelos por ano – mean (SD)    14.88 (5.11)

É lindo né?

Já a função median_iqr não tem muito mistério e, por isso, aqui vou explicar o parâmetro show_n que também pode ser utilizado em qualquer outra função do pacote. Quando utilizamos o na.rm, é importante trazer a informação ao leitores que se houver alguma observação como NA (vazia) para aquela mensuração o n é menor do que o esperado. Para isso, podemos utilizar o parâmetro show_n com as especificações ifNA, always ou never.


our_summary2 <-  
  list("Vendas" =
         list("Total modelos – median (IQR)" =
              ~ qwraps2::median_iqr(total_modelos,                          
                                    show_n = "ifNA",                         
                                     na_rm = TRUE)))

O resultado do our_summary segue abaixo. Observe que, como não há valors faltantes, essa especificação não é demonstrada.


                               comuns (N = 347)
Vendas     
Total modelos – median (IQR)  33 (20.00, 50.00)

Mas se inserirmos em show_n com a especificação always, o resultado será o seguinte.

                               comuns (N = 347)
Vendas     
Total modelos – median (IQR)  347; 33 (20.00, 50.00)

Agora, vamos juntar todos os our_summary construídos até o momento?

Há duas formas de fazer isso. A primeira é construir apenas um com todas as listas desejadas, visto que uma vez construída qualquer manejo é de fácil acesso.


our_summary <-  
  list("Vendas" =
         list("Total modelos – median (IQR)" = 
               ~ qwraps2::median_iqr(total_modelos,
                                     na_rm = TRUE),
              "Total modelos por ano – mean (CI)" = 
              ~ qwraps2::mean_ci(total_modelos,
                                 qdist = stats::qnorm,
                                  na_rm = TRUE),
              "Total modelos por ano – mean (SD)" =
               ~ qwraps2::mean_sd(total_modelos_ano,                      
                                  denote_sd = "pm",                                                         
                                  na_rm = TRUE),
              "Total modelos por ano – mean (SD)" = 
              ~ qwraps2::mean_sd(total_modelos_ano,                      
                                 denote_sd = "paren",                                                     
                                 na_rm = TRUE)))

Já a segunda é empilhar todas elas conforme exemplo abaixo.


table1 <- qwraps2::summary_table(comuns, our_summary1)
table2 <- qwraps2::summary_table(comuns, our_summary2)
base::cbind(table1, table2)

Tentou aí?

Se tentou a segunda opção, você viu que o retorno foi um erro.


Error in cbind(deparse.level, ...) : Not all row groups are identical.

Neste caso de empilhamento, o our_summary deve ser igual.


Como assim igual?

Para explicar esse 'deve ser igual' eu vou ter que te explicar mais uma opção brilhante que nós temos no pacote qwraps2. Essa opção é a utilização do dplyr::group_by. Nós conseguimos empilhar o mesmo our_summary para diversos arranjos, poupando tempo e linhas de código. Veja o exemplo abaixo.


our_summary <-  
  list("Vendas" =
         list("Total modelos – median (IQR)" =
              ~ qwraps2::median_iqr(total_modelos,                           
                                    show_n = "ifNA",                         
                                    na_rm = TRUE)))

table1 <- qwraps2::summary_table(dplyr::group_by(comuns, marca),
                                 our_summary)
table2 <- qwraps2::summary_table(dplyr::group_by(comuns, modelo), 
                                 our_summary)
base::cbind(table1, table2)

Nós empilhamos duas tabelas com o mesmo our_summary, porém cada uma com o seu respectivo agrupamento. Faz aí e me conta nos comentários sobre o que achou desse formato de empilhamento.


Está entendendo essa belezura de pacote?

Se não, volte ao início e RESPIRA (não pira). Todos os códigos que foram utilizados até o momento são reproduzíveis. Agora abre um arquivo .R ou um .Rmd aí no seu RStudio e faz o famoso copia e cola nos códigos e vai anotando o que cada parâmetro e função traz como resultado. Vai com calma que vai dar tudo certo. Mas, se você entendeu, segue o baile que vamos falar sobre mais funções super úteis do pacote qwraps2, as funções n_perc, n_perc0 e perc_n.


Essas funções salvam a vida do universitário e do epidemiologista. Vamos iniciar com n_perc. Essa função é simples, como todas as outras. Há em sua essência os parâmetros show_denom que já vimos anteriormente e o show_symbol que é bem intuitivo. O parâmetro show_symbol apenas indica se o % deve ou não aparecer no resumo. Veja o exemplo a seguir.


our_summary <-  
      list(
       "Especificações" =
          list("Marca – no/total no. (%)" = 
                ~ qwraps2::n_perc(marca == "Audi",
                                  show_symbol = FALSE,                                                              
                                  show_denom = "ifNA",
                                  na_rm = TRUE),
               "Marca – no/total no. (%)" = 
               ~ qwraps2::n_perc0(marca == "Audi",
                                  show_symbol = TRUE,
                                  show_denom = "always",
                                  na_rm = TRUE),
               "Marca – no/total no. (%)" = 
               ~ qwraps2::perc_n(marca == "Audi",
                                 show_symbol = TRUE,
                                 show_denom = "never",
                                 na_rm = TRUE)))

E como de costume,


table <- qwraps2::summary_table(comuns, our_summary)

Veja o resultado.


                                          comuns (N = 347)
Especificações   Marca – no/total no. (%) 11 (3.17)
                 Marca – no/total no. (%) 11/347 (3%) 
                 Marca – no/total no. (%) 3.17% (n = 347)

Na função n_perc nós temos exatamente o número de observações que contém a marca igual a Audi e entre parentêses a porcentagem do n dentro da população de 347 observações sem o símbolo de % já que utilizamos show_symbol = FALSE. Já no segundo utilizamos a função n_perc0 em conjunto com show_symbol = TRUE e como resultado a função n_perc0 já arredonda a porcentagem para 0 dígitos após a vírgula e temos o símbolo de % devido a solicitação igual a TRUE. Na terceira saída, temos exatamente o contrário da primeira, nós temos a porcentagem e em parentêses o n da população.


Notou a diferença de cada especificação de parâmetro e das funções?

Quando o argumento é um factor ou character, devemos especificar para estas funções qual o fator que estamos sumarizando. No exemplo acima, nós sumarizamos com o marca == "Audi", mas você pode inserir todas as marcas individualmente e inclusive pode agrupá-las como você bem entender. Tudo isso dentro da função. Como a identação do pacote é ~ qwraps2, você pode e deve utilizar quando for necessário lógicas e as diversas condições que existem no R.


Veja o exemplo abaixo.


our_summary <-  
      list(
       "Especificações" =
          list("Marca – no/total no. (%)" = 
                ~ qwraps2::n_perc(marca == "Audi" | marca == "Ford",
                                  show_symbol = FALSE,                                                              
                                  show_denom = "ifNA",
                                  na_rm = TRUE),
               "Marca – no/total no. (%)" = 
               ~ qwraps2::n_perc0(marca == "Audi" & modelo == "A8",
                                  show_symbol = TRUE,
                                  show_denom = "always",
                                  na_rm = TRUE),
               "Marca – no/total no. (%)" = 
               ~ qwraps2::perc_n(marca == "Audi" != modelo == "A8",
                                 show_denom = "never",
                                 na_rm = TRUE)))


Quando digo que essas funções salvam, é porque no decorrer de um trabalho, criar várias variáveis para sumarizar demanda muito trabalho e memória. E daí você vai me perguntar...


Fer, eu só uso o dplyr::summarise, e agora?

Simples de resolver. Esse pacote é tão magnífico que você pode utilizar ali no dplyr::summarise as funções do pacote qwraps2.


dados::comuns %>% 
dplyr::summarise("Marca – no/total no. (%)" =
                  qwraps2::n_perc(marca == "Audi",
                                  show_symbol = TRUE,
                                  show_denom = "always",
                                  na_rm = TRUE))

Viu só como é simples? A única questão é a classe de cada resultado. Se você está utilizando o qwraps2::summary_table, a classe desse resultado é "qwraps2_summary_table" "matrix" "array" e, se você está utilizando dplyr::summarise, a classe desse resultado é "tbl_df" "tbl" "data.frame". Ou seja, dependendo da finalidade, um é melhor que o outro em vários aspectos. Um exemplo que gosto de dar é se seu interesse é que essa tabela seja automatizada para um excel, é melhor utilizar a classe "data.frame". Isso vai te poupar muito tempo. Mas, se você não quer automatizar esse resultado, o famoso copia e cola para o excel dá super certo. E detalhe: a formatação da tabela vai junto com o ctrl+c.


Fer, como é que essas tabelas ficam visualmente?

Para ilustrar melhor, fiz a junção de todos os exemplos acima em apenas um our_summary. Veja o resultado na imagem abaixo.





E só tem essas funções?

Claro que não. Nós temos as funções min, max, gsd, gvar, qroc e confusion_matrix para modelagens e várias outras que ajudam a construir tabelas de resultados para modelos.


O que isso significa?

Que além de sumarizar o que é de interesse, o pacote qwraps2 também possui funções para construir tabelas de modelagens com p-value e estimadores automaticamente. Você também consegue alterar todos os nomes da tabela e adicionar cabeçalho. Esse pacote é um grande universo e, por isso, não dá pra trazer todas as bençãos que esse pacote possui em só post. Um site muito bom para aprender e visualizar um pouco mais as tabelas e função são o R-bloggers e também o rdrr . Neles há muitos exemplos.


Outra coisa que gostaria de pontuar é que certamente você também já ouviu falar do pacote hypado do momento para construção de tabelas chamado gtsummary . O gtsummary foi publicado em janeiro de 2023 e é sim um pacote muito bom, mas o qwraps2 está aí na ativa desde 2021 e foi nessa época que o conheci e, por isso, sigo usando. E como você viu aí acima, o qwraps2 é um ótimo pacote e tem suporte para utilizar com todos os pacotes do universo tidyverse, ele só não é tão conhecido e utilizado como o gtsummary no momento.


Espero que tenha gostado e que passe a utilizar essa belezura de pacote para construir suas tabelas.


Até mais!


Fernanda Kelly R. Silva | Estatística





Posts recentes

Ver tudo
bottom of page