Construindo Tabelas

Comunicando os nossos Dados

Tabelas Estáticas

Até agora, trabalhamos com as tabelas padrões fornecidas pelo R e a opção df_print: paged no cabeçalho. É só digitar o nome do tibble num chunk e no resultado vai aparecer uma tabela com navegação por página e colunas. Mas existem várias funções de ‘formatação’ que traduz o conteúdo de um tibble para uma tabela bonita no documento final. Elas funcionam como qualquer outra função do R e ficam no final de nosso fluxo de pipes.

A primeira função de formatação de tabelas se chama kable e fica num pacote se chama knitr, então temos que instalar (uma vez só, e depois comentado) e abrir o pacote:

Agora, seguimos qualquer tibble com %>% kable() e o R vai produzir uma tabela estática. Tabelas estáticas são simples e limpas, mas geram um risco grande - uma tabela grande será impressa na íntegra. Com um banco de dados de milhares de linhas, isso gera um arquivo enorme e difícil de navegar. Então temos que tomar cuidado: é a sua responsibilidade filter ou slice a sua tabela anteriormente para que o resultado final tenha o tamanho desejado.

flights %>% filter(dest=="ANC") %>%
  kable()
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight tailnum origin dest air_time distance hour minute time_hour
2013 7 6 1629 1615 14 1954 1953 1 UA 887 N587UA EWR ANC 418 3370 16 15 2013-07-06 16:00:00
2013 7 13 1618 1615 3 1955 1953 2 UA 887 N572UA EWR ANC 404 3370 16 15 2013-07-13 16:00:00
2013 7 20 1618 1615 3 2003 1953 10 UA 887 N567UA EWR ANC 418 3370 16 15 2013-07-20 16:00:00
2013 7 27 1617 1615 2 1906 1953 -47 UA 887 N559UA EWR ANC 388 3370 16 15 2013-07-27 16:00:00
2013 8 3 1615 1615 0 2003 1953 10 UA 887 N572UA EWR ANC 434 3370 16 15 2013-08-03 16:00:00
2013 8 10 1613 1615 -2 1922 1953 -31 UA 887 N559UA EWR ANC 411 3370 16 15 2013-08-10 16:00:00
2013 8 17 1740 1625 75 2042 2003 39 UA 887 N528UA EWR ANC 404 3370 16 25 2013-08-17 16:00:00
2013 8 24 1633 1625 8 1959 2003 -4 UA 887 N534UA EWR ANC 428 3370 16 25 2013-08-24 16:00:00

Uma alternativa de filter ou slice que é útil para apresentar uma amostra aleatória da sua tabela (e também para realizar amostras aleatórias em estudos empíricas) é sample_n(). Escolhemos o número de observações desejadas, e o R vai selecionar aleatoriamente este número de linhas da tabela (Observe que as observações mudam cada vez que rodamos/compilamos o nosso documento):

flights %>% sample_n(4) %>%
  kable()
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight tailnum origin dest air_time distance hour minute time_hour
2013 11 24 1856 1845 11 2150 2152 -2 DL 2391 N941DL JFK TPA 137 1005 18 45 2013-11-24 18:00:00
2013 7 8 904 905 -1 1152 1225 -33 DL 874 N341NB LGA MIA 147 1096 9 5 2013-07-08 09:00:00
2013 9 7 1552 1600 -8 1821 1847 -26 B6 543 N627JB EWR PBI 132 1023 16 0 2013-09-07 16:00:00
2013 10 2 1447 1430 17 1542 1533 9 EV 4696 N16981 EWR MHT 37 209 14 30 2013-10-02 14:00:00

Habilidade Básica de Programação: Aleatoriedade reproduzível

É comum usar funções aleatórias em R para gerar distribuições, simulações e amostras. A função sample_n() seleciona linhas aleatórias cada vez que rodamos o código ou compilar o relatório com ‘knit’. Isto é útil, mas impede um dos nossos objetivos - a reprodutibilidade, dado que o relatório final fica diferente depois de cada compilação e não temos condições de saber se a variação seja por causa de um erro no código ou por causa da aleatorização.

A solução é a ‘aleatoriedade reproduzível’: Imediatamente antes de roder sample_n() (fora do pipe, numa linha única), especificamos um ‘seed’. Um seed é como um identificador de uma sequência aleatória. Ele é associado a uma seleção aleatória, mas fixa - ou seja, toda vez que rodarmos o código, a sequência aleatória produzida será a mesma. Para especificar um seed aribtrário, eu sempre gosto de usar um CEP, mas pode escolher qualquer número; não significa nada:

set.seed(05508)
flights %>% sample_n(4) 

Execute este código (tudo o código, incluindo o set.seed) umas vezes e confirme que o resultado não muda.

Compile o seu script atual com ‘Knit’: como fica a tabela? O número de linhas não é problemático, mas as colunas saiam do lado direito da página… Também é a nossa responsibilidade limitar o número de colunas para incluir apenas elas que cabem no espaço da tabela final com select. Dificilmente queremos ver todas as colunas de uma tabela.

flights %>% sample_n(8) %>%
  select(month, day, dep_time, carrier, flight, origin, dest) %>%
  kable()
month day dep_time carrier flight origin dest
4 5 1256 US 1459 LGA CLT
5 22 610 UA 303 JFK SFO
8 19 1031 B6 2602 JFK BUF
9 12 1056 EV 5309 LGA BGR
10 28 1625 EV 4684 EWR SDF
1 6 604 AA 1837 LGA MIA
8 8 1415 B6 213 JFK LGB
12 15 1015 MQ 3644 LGA ATL

A tabela no relatório final melhorou? Sim. Mas porque investimos todo este esforço? Exige mais trabalho para limitar as linhas e colunas, e não temos as botões para navegar que recebemos com a opção df_print: paged… Há três motivos para trabalhar com tabelas estáticas:

  1. A tabela é apropriada para publicações em que interatividade não é possível, e funciona melhor com relatórios em Word e PDF (que vamos ver em breve). Compile o seu script para Word (‘Knit to Word’ com a flecha ao lado de ‘Knit’) para ver o resultado. Deve ser muito melhor. Lembre-se que um artigo ou dissertação publicada exige tabelas estáticas.

  2. A necessidade de selecionar as observações e colunas necessárias nos motiva a pensar bem sobre o desenho e a estruta mais apropriados para a nossa tabela. Incluir a tabela inteira cada vez gera documentos grandes e pesados.

  3. A função kable é muito mais flexível do que as tabelas padrões de R. Podemos personalizar todos os elementos da tabela. Vejamos alguns exemplos abaixo.

Em primeiro lugar, podemos especificar o título da tabela com a opção caption:

flights %>% sample_n(8) %>%
  select(month, day, dep_time, carrier, flight, origin, dest) %>%
  kable(caption="Tabela de 8 voos aleatórios")
Table 1: Tabela de 8 voos aleatórios
month day dep_time carrier flight origin dest
4 12 2037 EV 5667 EWR ROC
9 25 1849 UA 693 LGA ORD
3 1 2250 B6 112 JFK BUF
2 28 906 UA 235 EWR MIA
5 23 NA EV 4576 EWR GRR
12 30 1508 UA 249 EWR TPA
10 26 834 9E 3841 JFK PHL
7 29 2057 DL 1854 LGA FLL

Mais profissional. Em segundo lugar, o kable deixa mais fácil formatar os resultados númericos - para arredondar os números com as tabelas padrões temos que manualmente usar mutate para mexer com os valores atuais da tabela. Mas é melhor manter os números ‘corretos’ e completos no tibble, e só ajustar a apresentação na tabela final. Conseguimos realizar uma formatação do número de dígitos com kable com a opção digits, que define o número de casas decimais. A nossa tabela até agora só contém variáveis numéricas do tipo ’integer (integral), então vamos calcular velocidade para ilustrar isso:

flights %>% sample_n(8) %>%
  mutate(velocidade=distance/air_time) %>%
  select(month, day, dep_time, carrier, flight, origin, dest, velocidade) %>%
  kable(digits=1)
month day dep_time carrier flight origin dest velocidade
7 10 853 B6 15 JFK SFO 7.7
2 13 1010 US 75 EWR PHX 7.7
10 13 1603 9E 3523 JFK ORD 6.7
12 7 1659 UA 1199 EWR LAX 6.2
10 25 958 EV 4255 EWR CHS 6.1
3 6 1542 UA 1200 EWR SAN 7.1
10 11 747 MQ 3052 JFK TPA 7.3
12 1 726 B6 461 LGA PBI 7.3

Em terceiro lugar, é comum que a descrição das nossas variáveis na tabela final seja diferente que o nome da variável no script de programação. Isto é natural; em nosso script temos que escrever os nomes das variáveis muitas vezes e um atalho é muito mais eficiente, mas na tabela final o leitor tem que entender o significado da variável com apenas a informação na tabela. O kable permite especificar nomes de colunas mais complexas, sem renomear elas no tibble original. Por exemplo, o significado de ‘dep_time’ não é óbvio:

flights %>% sample_n(8) %>%
  select(month, day, dep_time, carrier, flight, origin, dest) %>%
    kable(col.names=c("Mês","Dia","Hora de Partida","Companhia Aérea","Voo","Origem","Destino"))
Mês Dia Hora de Partida Companhia Aérea Voo Origem Destino
1 3 1640 B6 139 JFK RSW
9 25 918 EV 4092 EWR IND
2 19 1144 DL 1689 LGA PBI
5 20 1454 US 2177 LGA DCA
2 3 NA MQ 4670 LGA BNA
4 29 1923 9E 3899 JFK CLE
8 5 1817 DL 1715 LGA MSY
9 25 622 B6 675 JFK MSY

Finalmente, podemos formatar os números em português (ou qualquer outro local) com o argumento format.args:

flights %>% sample_n(8) %>%
  mutate(velocidade=distance/air_time) %>%
  select(month, day, dep_time, carrier, flight, origin, dest, velocidade) %>%
    kable(digits=1, format.args=list(big.mark=".", decimal.mark=","))
month day dep_time carrier flight origin dest velocidade
3 15 658 EV 4.547 EWR BNA 6,9
9 17 1.307 UA 1.401 EWR MIA 6,9
9 29 1.554 DL 221 LGA ATL 7,1
2 3 1.758 B6 1.016 JFK BOS 5,1
4 6 2.032 UA 1.071 EWR BQN 7,4
7 26 1.430 FL 349 LGA ATL 6,0
6 21 913 VX 161 EWR LAX 7,6
9 18 904 B6 5 EWR FLL 7,3

Para mais opções de kable, leia aqui, e para saber mais ainda veja o pacote kableExtra aqui.

Note que existe a opção de gerar tabelas ‘manuais’ diretamente em R markdown, fora de um chunk (detalhes aqui). Não recomendo isso porque a sintaxe é chata para digitar, não temos controle de formatação, e não podemos acessar e transformar o conteúdo no futuro. Se você precisa gerar uma tabela rápida, sempre é recomendado que você gera uma pequena tabela com tibble() e manda para kable(), por exemplo:

tibble(Função=c("kable","opção df_print: paged","datatable"),
       Utilidade=c("Word/PDF, relatórios estáticos","HTML, relatórios interativos simples","HTML, relatórios interativos complexos")) %>%
  kable()
Função Utilidade
kable Word/PDF, relatórios estáticos
opção df_print: paged HTML, relatórios interativos simples
datatable HTML, relatórios interativos complexos

Tabelas Interativas

A terceira entrada na tabela acima é mais uma opção para gerar tabelas. A função datatable() (do pacote DT) tem o mesmo objetivo da opção df_print: paged; gerar tabelas interativas em HTML. O problema com df_print: paged é que ela não tem opções para personalizar a tabela final e temos que fazer toda a preparação manualmente. O datatable é uma função mais avançada que permite personalizações infinitas.

Não entramos nos detalhes de datatable no tutorial; vocês podem explorar a documentação detalhada aqui. Mas vamos mostrar um exemplo com funcionalidade avançado (filtros de colunas, formatação de números, e coloração de células por valor, neste caso velocidade acima de 7,5):

#install.packages("DT")
library(DT)
flights %>% 
  sample_n(100) %>%
  mutate(velocidade=distance/air_time) %>%
  select(month, day, dep_time, carrier, flight, origin, dest, velocidade) %>%
  datatable()
flights %>% 
  sample_n(100) %>%
  mutate(velocidade=distance/air_time) %>%
  select(month, day, dep_time, carrier, flight, origin, dest, velocidade) %>%
  datatable(colnames=c("Mês","Dia","Hora de Partida","Companhia Aérea","Voo","Origem","Destino","Velocidade"), 
            caption="Tabela de 100 voos aleatórios",
            filter='top') %>%
  formatRound("velocidade",1) %>%
  formatStyle('velocidade',
    backgroundColor = styleInterval(7, c('white', 'orange'))
  )

Exercício 1: Tabelas Bonitas

  1. Gere uma tabela estática de duração (air_time) média dos voos de cada aeoporto de origem, ordenado de menor a maior duração. Inclua um título e a formatação apropriada na sua tabela.
Mostrar Código
flights %>% group_by(origin) %>%
  summarize(media_air_time=mean(air_time,na.rm=T)) %>%
  arrange(media_air_time) %>%
  kable(digits=1, format.args=list(big.mark=".", decimal.mark=","),
        caption="Duração média dos voos de Nova Iorque em 2013")
  1. Identifique os voos de origem ‘EWR’ no dia 1 de Abril com decolagem antes de 6h. Prepare uma tabela estática que mostra as variáveis dep_time, dep_delay, carrier, flight, dest, air_time, distance. Inclua um título e a formatação apropriada.
Mostrar Código
flights %>% filter(month==4 & day==1 & origin=="EWR" & dep_time<600) %>%
  select(dep_time, dep_delay, carrier, flight, dest, air_time, distance) %>%
  kable(caption="Voos de EWR no dia 4 de abril, antes de 6h")
  1. Duplique a tabela de questão 2, mas agora mande o resultado para uma tabela interativa de datatable. (Não se preocupe com a formatação).
Mostrar Código
flights %>% filter(month==4 & day==1 & origin=="EWR" & dep_time<600) %>%
  select(dep_time, dep_delay, carrier, flight, dest, air_time, distance) %>%
  datatable(caption="Voos de EWR no dia 4 de abril, antes de 6h")
  1. Knit o seu script para HTML e verifique que as tabelas aparecem limpas e claras.

Mudando a Unidade de Observação (Pivot_wide, Pivot_longer)

Nós temos trabalhado com uma distinção entre variáveis (colunas) e observações (linhas) fixa. Mas a distinção na prática é mais flexível, e a estrutura/organização dos nossos dados depende do nosso objetivo. As vezes vamos querer virar a nossa tabela (tibble) para que as variáveis se transformem em observações múltiplas, ou vice-versa. Esse tipo de operação é muito útil quando trabalhamos com ou queremos criar dados em painel, ou para uma tabela específica em nosso relatório.

É mais fácil demonstar com um exemplo. Veja a tabela de flights com as variáveis origin e dest:

flights %>% filter(dest=="SNA") %>% 
  select(month, day, origin, dest)

A observação é cada voo, e cada variável é um atributo de cada voo - isto é dados de onde o voo partiu, e onde ele chegou. Mas ambos as variáveis origin e dest são a mesma coisa: aeroportos. Podemos imaginar um banco de dados em que desagregamos os voos em dois ‘eventos’ - decolagens e chegadas. Neste banco de dados alternativo, cada observação é um evento e cada voo tem duas entradas (observações, linhas), um decolagem e uma chegada. Isso pode ser útil, por exemplo, se tenhamos muitos dados sobre cada evento/aeroporto, por exemplo os dados de controle de tráfego aéreo.

De Largo para Longo

Para ‘virar’ (pivot) a nossa tabela para o formato mais ‘longo’, usamos a função pivot_longer. O objetivo de pivot_longer é converter colunas em observações - no nosso exemplo, converter o evento único “voo” em dois eventos diferentes, pousos e decolagens. Mas não queremos mudar todas as colunas (algumas pertencem ao voo inteiro, como distância, duração); temos que especificar quais colunas queremos virar no argumento cols. É sempre mais que uma coluna, então temos que especificar as colunas dentro de c(). As outras colunas nào mencionadas em cols são colunas ‘identificadoras’ que queremos duplicar para as novas linhas (em nosso exemplo os dados que pertencem ao voo e não ao aeroporto).

Como uma primeira tentativa, o código abaixo é suficiente para virar o nosso tibble:

flights %>% filter(dest=="SNA") %>% 
  select(month, day, origin, dest) %>% 
  pivot_longer(cols=c(origin, dest))

Note que agora o nosso banco de dados tem dobro o número de linhas que antes. Cada observação é um evento (decolagen/chegada). As duas colunas ‘origin’ e ‘dest’ agora são linhas separadas, identificadas na colona name, e o aeroporto que antes era um valor abaixo de ‘origin’ ou ‘dest’ na coluna tem a sua própria coluna value. Ou seja, em vez de ler os detalhes do primeiro voo horizontalmente no tibble original, agora lemos verticalmente - origem EWR, destino ANC.

O que falta aqui é que os nomes das colunas name e value são genéricas e não ajudam entender e descrever os dados. Então é importante renomear as colunas como parte de pivot_longer. Há duas colunas para nomear: a coluna de name que contém os nomes de colunas no tibble original (a direção de viagem), que especificamos com o argumento names_to, e a coluna de valor que contém os valores no tibble original (os aeroportos), que especificamos com o argumento values_to.

flights %>% filter(dest=="SNA") %>% 
  select(month, day, origin, dest) %>% 
  pivot_longer(cols=c(origin, dest), 
               names_to="Direção", 
               values_to="Aeroporto")

Agora, recebemos o mesmo resultado, mas é bem mais fácil interpretar, pois a natureza do evento é cadastrada na coluna ‘Direção’ e o local do evento fica na coluna ‘Aeroporto’.

Mas ainda existe um problema sério aqui - como sabemos que a primeira linha - o voo com origem em EWR no dia 1 de janeiro é o mesmo voo que chegou no aeroporto SNA no mesmo dia? Não sabemos isso porque não temos dados suficientes para identificar os mesmos voos em linhas separadas. Pode ser que o voo da primeira linha de origem é o mesmo voo na linha 3 ou linha 120.

O problema é que não temos um identificador único para cada voo no banco de dados. Isto é importante para todos os bancos de dados, mas sobretudo quando usamos um ‘pivot’ para mudar a unidade de análise, pois temos que restrear a divisão de uma observação em duas.

Para resolver o problema, temos que incluir colunas suficientes no tibble original. Usando distinct do tutorial 3, podemos verificar que as colunas month, day, dep_time, carrier, flight, origin e dest são suficientes para identificar cada voo único. O banco de dados original tem 336,776 linhas, ígual o resultado do distinct com estas variáveis:

flights %>% distinct(month, day, dep_time, carrier, flight, origin, dest) 

Agora podemos refazer o pivot_longer incluindo estas variáveis identificadores:

flights_longo <- flights %>% filter(dest=="SNA") %>% 
  select(month, day, dep_time, carrier, flight, origin, dest) %>% 
  pivot_longer(cols=c(origin, dest), 
               names_to="Direção", 
               values_to="Aeroporto")

Perfeito! Agora, dá para rastrear cada voo: Voo UA1496 as 0646 no dia 1 de janeiro tem duas entradas - origem em EWR e chegada em SNA.

De Longo para Largo

É comum que recebemos um banco de dados no formato longo e queremos virar (pivot) para o formato largo. Ou seja, tornar observações em variáveis. Imagine-se que recebemos do fornecedor o banco de dados flights_longo produzido no chunk anterior. Como reproduzimos o nosso banco de dados bem conhecido, flights, no formato mais largo?

A função pivot_wider é exatamante o inverso de pivot_longer. Em vez de especificar as colunas que queremos virar, especificamos as colunas de ‘identificação’ que não queremos virar no argumento id_cols; em vez da coluna nova, especificamos a coluna original que contém os nomes das colunas novas, no argumento names_from; e em vez de valores noves, especificamos a coluna de onde vão sair os valores que vão formar as colunas novas (no argumento values_from).

Note que com pivot_longer os argumentos names_to e values_to precisam de aspas porque são colunas novas que não existem em nosso tibble. No pivot_wider os argumentos names_from e values_from são colunas atuais em nosso tibble, então não precisam de aspas.

flights_longo %>% pivot_wider(id_cols=c(month, day, dep_time, carrier, flight), 
                              names_from=Direção, 
                              values_from=Aeroporto)

Veja que o resultado aqui tem metade das linhas de flights_longo, e o mesmo número do nosso banco de dados original. O R peguou os valores da coluna ‘Direção’ e criou uma coluna para cada valor único (uma para ‘origin’, uma para ‘dest’). Os valores destas duas novas colunas são preenchidas da coluna ‘Aeroporto’.

Um atalho para simplificar aqui é quando queremos virar todas as colunas exceto eles mencionados em names_from e values_from, podemos pular a especificação de id_cols:

flights_longo %>% pivot_wider(names_from=Direção, 
                              values_from=Aeroporto)

Essas duas funções de pivot são mais avançadas, mas as vezes são essenciais para gerar a estrutura e formato da tabela desejada. É comum que o resultado da nossa análise em R é em formato ‘larga’, mas seria mais bonito imprimir no relatório final no formato ‘longo’. Podemos usar pivot_longer para rapidamente produzir a tabela desejada. Similarmente, na semana que vem, aprendemos que os gráficos exigem um formato específico - frequentemente o formato ‘longo’ - e estas novas habilidades ajudam bastante.

Exercício 2: Virando Tabelas

  1. Use pivot_longer para virar a tabela flights mais longa, para que cada voo tem duas observações - uma para a hora de partida (dep_time) e uma outra para a hora de chegada (arr_time).
Mostrar Código
flights_hora_longo <- flights %>% pivot_longer(c(arr_time, dep_time),
                         names_to="Direção",
                         values_to="Hora")
  1. Usando o seu resultado de questão 1, gere uma tabela estática de 10 linhas selecionadas aleatoriamente por R, mostrando as variáveis carrier, flight, origin, dest e as colunas novas que você gerou na questão 1.
Mostrar Código
flights_hora_longo %>% 
  select(carrier, flight, origin, dest, Direção, Hora) %>%
  sample_n(10) %>%
  kable(caption="Tabela de cada Partida e Chegada")
  1. Usando o seu resultado de questão 1, use pivot_wider para recuperar o banco de dados original de flights. Verifique que os números de colunas e linhas são íguais.
Mostrar Código
flights_recuperado <- flights_hora_longo %>% pivot_wider(names_from="Direção",
                                                         values_from="Hora")

Voltando ao R Base Brevemente

As funçoes de filter, select, mutate etc. e o nosso pipe %>% que compõem o tidyverse têm cerca de 5 anos de idade, mas o R tem mais de 25 anos de idade. O que os usuários fizeram antes do tidyverse?

Existem várias formas de interagir e manipular dados em R, mas nada muda os básicos: existem um script, objetos que podemos salvar, e funções que transformam objetos. Também trabalhamos mais frequentemente com um data.frame, que é um tibble um pouco mais básico.

A maior diferença quando trabalhamos no mundo do ‘R básico’ é na forma em que acessamos e transformamos os valores dentro de um data.frame. Não usamos uma sequência de ações/verbos para separar cada passo; temos que usar uma sintaxe e símbolos bem específicos.

Note que nada nesta seção do tutorial é necessário, e encorajamos vocês a trabalhar dentro do tidyverse sempre que possível, para criar análises mais simples, transparentes e reproduizíveis. Porém, as vezes temos que trabalhar com usuários ou pacotes fora do tidyverse, então é útil entender os conceitos básicos, e também ajuda a entender como o R funciona atrás do interface.

Os símbolos mais importantes no R base são os colchetes. Depois de um data.frame, eles significam que queremos ajustar as colunas e/ou as linhas. Dentro dos colchetes, tem dois ‘espaços’; o primeiro para mexer com as linhas, e o segundo para mexer com as colunas, separado por vírgula.

flights[ , ]

O que inserimos nestes espaços? Vamos começar com as linhas e deixar o espaço para colunas branco. Podemos escolher as linhas para manter por número (como slice) ou por condição (como filter):

flights[1:5, ]
flights[distance==200, ]
Error in eval(expr, envir, enclos): object 'distance' not found

Hmm, o primeiro funcionou bem, mas o distance==200 não deu certo. As condições no R base são idênticas às que usamos no tidyverse, isso não é o problema. O problema é que no R base, o R não sabe o que significa distance. Ele busca para um objeto (um data.frame) se chama distance, mas realmente distance é uma coluna dentro do objeto flights. Mesmo que começamos com flights, temos que informar R que distance fica dentro de flights de novo (o motivo para exigir a repetição do objeto é que podemos querer no futuro filtrar uma tabela baseado no conteúdo de uma segunda tabela). Digitamos isso assim, separando o data.frame e a coluna com o símbolo $:

flights[flights$distance==200, ]

Agora funcionou. Como escolhemos colunas em R base? Usamos o segundo espaço, depois da vírgula, para escrever os nomes das colunas desejadas, agora - e em contraste ao tidyverse - em aspas e dentro de um vetor, c():

flights[flights$distance==200, c("month","day", "dep_time","origin","dest")]

Compare isso com o equivalente no tidyverse:

flights %>% filter(distance==200) %>%
  select(month, day, dep_time, origin, dest)

Qual você prefere? Para a maioria de tarefas, sobretudo para tarefas mais complexas, é mais intuitivo e transparente usar o tidyverse.

Totais para Tabelas

Em alguns momentos os símbolos de R base voltam no tidyverse também. Por exemplo, se quisermos gerar tabelas resumidas, às vezes é útil adicionar uma linha resumida no final, por exemplo um total ou uma média. Para adicionar mais uma linha no final da nossa tabela, podemos usar a função add_row() no fluxo de pipes, e especificar um argumento para cada coluna na tabela, com o nome de coluna na esquerda do símbolo de íguais, e o valor desejado na direita. É comum adicionar uma linha ‘Total’:

flights %>% group_by(origin) %>%
  tally() %>%
  add_row(origin="Total", n=sum(n))
Error in sum(n): invalid 'type' (closure) of argument

Com toda a nossa intuição do tidyverse isso deve funcionar. Mas infelizmente (e por motivos bem chatos da programação de add_row) não dá. O problema é que o sum(n) na funcão add_row não sabe procurar a colona n na tabela atual (o tibble resultante do último pipe). Ele precisa uma dica para onde procurar. Dentro do tidyverse o símbolo ‘.’ significa o tibble atual, e já aprendemos do R base que o $ nos permitimos especificar uma coluna de um tibble. Então em vez de usar o atalho n, temos que usar .$n. Um conjunto de símbolos estranhos, mas funciona:

flights %>% group_by(origin) %>%
  tally() %>%
  add_row(origin="Total", n=sum(.$n))


Leitura para Tutorial 6

Antes da próxima aula, por favor leia R 4 Data Science, Capítulo 3 e ggplot2: Elegant Graphics for Data Analysis, Capítulos 1-5


Desafio 2

O Desafio 2 teste a sua capacidade de organizar, resumir, agrupar, e apresentar um banco de dados para produzir um relatório bonito e claro em HTML.

O prazo para entregar Desafio 2 por email com título “[FLS6397] - D2” à minha conta é 02/06/2021, antes da aula. Por favor entregue (i) o arquivo .Rmd, e (ii) o arquivo .html.