Construindo Tabelas

Comunicando os nossos Dados

Clique aqui para assistir o vídeo da aula online do dia 30 de abril de 2021. E o chat.

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 10 26 1626 1625 1 1843 1827 16 DL 2231 N357NW LGA DTW 100 502 16 25 2013-10-26 16:00:00
2013 10 20 1525 1536 -11 1651 1728 -37 UA 494 N824UA LGA CLE 69 419 15 36 2013-10-20 15:00:00
2013 9 9 1848 1619 149 2117 1919 118 B6 283 N708JB JFK MCO 115 944 16 19 2013-09-09 16:00:00
2013 8 19 1330 1335 -5 1456 1503 -7 B6 286 N206JB JFK ROC 57 264 13 35 2013-08-19 13: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
11 2 906 B6 1634 JFK BTV
2 5 1831 MQ 3944 JFK BWI
5 16 1539 AA 133 JFK LAX
1 30 1026 US 2169 LGA DCA
5 26 818 MQ 4478 LGA DTW
5 30 1045 DL 1903 LGA SRQ
10 1 816 UA 294 EWR MCO
7 8 NA DL 831 LGA DTW

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
5 16 822 B6 1172 EWR BOS
7 17 556 US 1629 LGA PHL
9 9 1717 UA 1064 EWR BOS
9 6 1811 DL 926 EWR ATL
5 26 1055 WN 2631 LGA MDW
5 19 2 B6 739 JFK PSE
8 29 1239 EV 3848 EWR CLT
11 7 1653 DL 1585 LGA MCO

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
6 18 1844 EV 4532 EWR CHS 6.2
8 12 724 AA 1815 JFK MCO 7.4
10 27 649 UA 331 LGA ORD 6.5
9 5 1554 B6 543 EWR PBI 7.1
6 12 1445 EV 4434 EWR MHT 5.1
6 25 929 WN 3841 EWR MDW 7.0
12 30 931 EV 4364 EWR MCI 6.0
8 11 1517 EV 4381 EWR DTW 5.9

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
11 19 1850 9E 2928 JFK ORD
12 12 1207 9E 3508 LGA JAX
3 12 1505 EV 4572 EWR GSP
8 30 554 US 1909 LGA PHL
3 28 658 DL 1879 LGA FLL
12 1 541 EV 3819 EWR CVG
10 11 905 EV 5813 EWR RIC
10 31 1651 EV 4294 EWR SAV

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
9 21 655 DL 2.285 LGA MCO 7,9
11 26 952 9E 4.060 LGA DAY 5,8
10 27 1.041 DL 954 LGA FLL 6,7
6 11 659 DL 763 JFK LAX 7,9
1 22 753 US 2.165 LGA DCA 4,0
2 3 2.005 MQ 4.555 LGA CMH 6,0
5 16 2.054 MQ 4.573 LGA DTW 5,6
8 10 1.832 B6 153 JFK PBI 6,9

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

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 `[.tbl_df`(flights, distance == 200, ): 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 filtro 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 é 14h, 14/05/2021, antes da próxima aula. Por favor entregue (i) o arquivo .Rmd, e (ii) o arquivo .html.