Capítulo 4 tidyverse

Até agora, manipulamos vetores reorganizando-os e criando subconjuntos indexando. No entanto, quando iniciamos as análises mais avançadas, a unidade preferida para armazenamento de dados não é o vetor, mas o data frame. Neste capítulo, aprenderemos a trabalhar diretamente com data frames, o que facilita muito a organização da informação. Usaremos data frames para a maior parte deste livro. Vamos nos concentrar em um formato de dados específico chamado tidy_e uma coleção específica de pacotes que são particularmente úteis para trabalhar com dados_tidy_chamados_tidyverse.

Podemos carregar todos os pacotes _tidyverse_de uma só vez instalando e carregando o pacotetidyverse:

library(tidyverse)

Aprenderemos como implementar a abordagem tidyverse ao longo do livro, mas antes de nos aprofundarmos nos detalhes, neste capítulo, apresentaremos alguns dos aspectos mais comuns do tidyverse, começando com o pacote __dplyr__para manipular os quadros de dados e o pacotepurrr para trabalhar com As funções. Observe que o tidyverse também inclui um pacote gráfico, ggplot2, que apresentaremos mais adiante neste capítulo. 7 na parte “Visualização de dados” do livro; o pacote readr discutido no capítulo 5; e muitos outros. Neste capítulo, primeiro apresentamos o conceito de dados tidy_e depois demonstramos como usamos_tidyverse_para trabalhar com_data frames neste formato.

4.1 Data tidy

Dizemos que uma tabela de dados está no formato tidy se cada linha representa uma observação e as colunas representam as diferentes variáveis disponíveis para cada uma dessas observações. O conjunto de dados murders é um exemplo de um tidy data frame.

#>        state abb region population total
#> 1    Alabama  AL  South    4779736   135
#> 2     Alaska  AK   West     710231    19
#> 3    Arizona  AZ   West    6392017   232
#> 4   Arkansas  AR  South    2915918    93
#> 5 California  CA   West   37253956  1257
#> 6   Colorado  CO   West    5029196    65

Cada linha representa um estado com cada uma das cinco colunas fornecendo uma variável diferente relacionada a esses estados: nome, abreviação, região, população e número total de assassinatos.

Para ver como as mesmas informações podem ser fornecidas em diferentes formatos, considere o seguinte exemplo:

#>       country year fertility
#> 1     Germany 1960      2.41
#> 2 South Korea 1960      6.16
#> 3     Germany 1961      2.44
#> 4 South Korea 1961      5.99
#> 5     Germany 1962      2.47
#> 6 South Korea 1962      5.79

Este conjunto de dados tidy oferece taxas de fertilidade para dois países ao longo dos anos. Um conjunto de dados arrumado é considerado porque cada linha apresenta uma observação com as três variáveis: país, ano e taxa de fertilidade. No entanto, esse conjunto de dados originalmente veio em um formato diferente e o remodelamos para distribuição através do pacote dslabs. Originalmente, os dados estavam no seguinte formato:

#>       country 1960 1961 1962
#> 1     Germany 2.41 2.44 2.47
#> 2 South Korea 6.16 5.99 5.79

A mesma informação é fornecida, mas há duas diferenças importantes no formato: 1) cada linha inclui várias observações e 2) uma das variáveis, ano, é armazenada no cabeçalho. Para que os pacotes tidyverse sejam utilizados da melhor maneira, precisamos alterar a forma dos dados para que estejam no formato tidy, que eles aprenderão na seção “Wrangling data” do livro. Até lá, usaremos exemplos de conjuntos de dados que já estão no formato tidy.

Embora não seja imediatamente óbvio, ao longo do livro, você começará a apreciar os benefícios de trabalhar em uma estrutura em que as funções usam os formatos tidy_para_inputs_e_outputs. Você verá como isso permite que os analistas de dados se concentrem nos aspectos mais importantes da análise, e não no formato dos dados.

4.2 Exercícios

1. Examine o conjunto de dados incluído na base R co2. Qual dos seguintes é verdadeiro?

para. co2 tidy data: possui um ano para cada linha. b. co2 not tidy: precisamos de pelo menos uma coluna com um vetor de caracteres. c. co2 não é tidy: é uma matriz em vez de um data frame. d. co2 não é arrumado: para arrumar, teríamos que mudar a forma (em inglês) para ter três colunas (ano, mês e valor), e então cada observação de CO2 teria uma linha.

2. Examine o conjunto de dados incluído na base R ChickWeight. Qual dos seguintes é verdadeiro?

para. ChickWeight não tidy: cada pintinho tem mais de uma linha. b. ChickWeight is_tidy_: cada observação (um peso) é representada por uma linha. O pintinho de onde essa medição veio é uma das variáveis. c. ChickWeight não é arrumado: estamos perdendo a coluna do ano. d. ChickWeight é tidy: é armazenado em um data frame.

3. Examine o conjunto de dados predefinido BOD. Qual dos seguintes é verdadeiro?

para. BOD não é arrumado: tem apenas seis linhas. b. BOD not tidy: a primeira coluna é apenas um índice. c. BOD é tidy: cada linha é uma observação com dois valores (tempo e demanda) d. BOD é tidy: todos os conjuntos de dados pequenos são tidy por definição.

4. Qual dos seguintes conjuntos de dados internos é tidy? Você pode escolher mais de um.

para. BJsales b. EuStockMarkets c. DNase d. Formaldehyde e. Orange f. UCBAdmissions

4.3 Como manipular data frames

O pacote __dplyr_do_tidyverse_oferece funções que executam algumas das operações mais comuns ao trabalhar com_data frames e usa nomes para essas funções que são relativamente fáceis de lembrar. Por exemplo, para alterar a tabela de dados adicionando uma nova coluna, usamos mutate. Para filtrar a tabela de dados para um subconjunto de linhas, usamos filter. Por fim, para subdividir os dados selecionando colunas específicas, usamos select.

4.3.1 Como adicionar uma coluna com mutate

Queremos que todas as informações necessárias para nossa análise sejam incluídas na tabela de dados. Portanto, a primeira tarefa é adicionar as taxas de assassinato ao nosso quadro de dados de assassinatos. A função mutate pegue data frame como primeiro argumento e nome da variável e valores como segundo argumento usando a convenção name = values. Então, para adicionar taxas de assassinatos, usamos:

library(dslabs)
data("murders")
murders <- mutate(murders, rate = total/ population * 100000)

Lembre-se que aqui usamos total e population dentro da função, que são objetos não definidos em nossa área de trabalho. Mas por que não recebemos um erro?

Este é um dos principais recursos do dplyr. As funções neste pacote, como mutate eles sabem como procurar variáveis no data frame que o primeiro argumento fornece. Na chamada para mutate que vemos acima, total terá os valores de murders$total. Essa abordagem torna o código muito mais legível.

Podemos ver que a nova coluna foi adicionada:

head(murders)
#>        state abb region population total rate
#> 1    Alabama  AL  South    4779736   135 2.82
#> 2     Alaska  AK   West     710231    19 2.68
#> 3    Arizona  AZ   West    6392017   232 3.63
#> 4   Arkansas  AR  South    2915918    93 3.19
#> 5 California  CA   West   37253956  1257 3.37
#> 6   Colorado  CO   West    5029196    65 1.29

Embora tenhamos substituído o objeto original murders, isso não altera o objeto que foi carregado com data(murders). Se carregarmos os dados murders novamente, o original substituirá nossa versão mutada.

4.3.2 Como criar subconjuntos com filter

Agora, suponha que desejamos filtrar a tabela de dados para mostrar apenas as entradas para as quais a taxa de homicídios é menor que 0,71. Para fazer isso, usamos a função filter, que usa a tabela de dados como o primeiro argumento e, em seguida, a instrução condicional como o segundo. O mesmo que com mutate, podemos usar nomes de variáveis sem aspas de murders dentro da função e ele saberá que estamos nos referindo às colunas e não aos objetos na área de trabalho.

filter(murders, rate <= 0.71)
#>           state abb        region population total  rate
#> 1        Hawaii  HI          West    1360301     7 0.515
#> 2          Iowa  IA North Central    3046355    21 0.689
#> 3 New Hampshire  NH     Northeast    1316470     5 0.380
#> 4  North Dakota  ND North Central     672591     4 0.595
#> 5       Vermont  VT     Northeast     625741     2 0.320

4.3.3 Como selecionar colunas com select

Embora nossa tabela de dados tenha apenas seis colunas, algumas tabelas de dados incluem centenas. Se queremos ver apenas algumas colunas, podemos usar a função select de dplyr. No código a seguir, selecionamos três, atribuímos o resultado a um novo objeto e filtramos esse novo objeto:

new_table <- select(murders, state, region, rate)
filter(new_table, rate <= 0.71)
#>           state        region  rate
#> 1        Hawaii          West 0.515
#> 2          Iowa North Central 0.689
#> 3 New Hampshire     Northeast 0.380
#> 4  North Dakota North Central 0.595
#> 5       Vermont     Northeast 0.320

Na chamada para select, o primeiro argumento murders é um objeto mas state, region e rate eles são nomes de variáveis.

4.4 Exercícios

1. Carregue o pacote dplyr e o conjunto de dados de assassinato nos EUA.

library(dplyr)
library(dslabs)
data(murders)

Você pode adicionar colunas usando a função mutate de dplyr. Esta função reconhece os nomes das colunas e, dentro da função, você pode chamá-los sem aspas:

murders <- mutate(murders, population_in_millions = population/ 10^6)

Nós podemos escrever population em vez de murders$population. A função mutate sabemos que estamos agarrando colunas de murders.

Usar função mutate para adicionar uma coluna de assassinato chamada rate com a taxa de homicídios por 100.000, como no código do exemplo acima. Certifique-se de redefinir murders como foi feito no código do exemplo anterior (assassinatos <- [seu código]) para que possamos continuar usando essa variável.

2. Sim rank(x) fornece o alcance das entradas x do menor para o maior, rank(-x) fornece os intervalos do maior para o menor. Usar função mutate adicionar uma coluna rank a contendo a faixa de taxa de homicídios da maior para a menor. Certifique-se de redefinir murders para continuar usando esta variável.

3. Com dplyr, podemos usar select para mostrar apenas determinadas colunas. Por exemplo, com este código, mostraríamos apenas os estados e tamanhos da população:

select(murders, state, population) %>% head()

Usar select para exibir nomes e abreviações de estado em murders. Não redefina murders, apenas mostre os resultados.

4. A função filter __dplyr_é usado para escolher linhas específicas do_data frame para salvar. A diferença de select que é para colunas, filter é para linhas. Por exemplo, você pode exibir apenas a linha de Nova York da seguinte maneira:

filter(murders, state == "New York")

Você pode usar outros vetores lógicos para filtrar linhas.

Usar filter para mostrar os cinco estados com as maiores taxas de homicídio. Depois de adicionar a taxa e o intervalo de assassinatos, não altere o conjunto de dados de assassinatos nos EUA. EUA, apenas mostre o resultado. Lembre-se de que você pode filtrar com base na coluna rank.

5. Podemos excluir linhas usando o operador !=. Por exemplo, para remover a Flórida, faríamos o seguinte:

no_florida <- filter(murders, state != "Florida")

Crie um novo data frame com o nome no_south isso elimina os estados da região sul. Quantos estados há nesta categoria? Você pode usar a função nrow para isto.

6. Também podemos usar %in% para filtrar com dplyr. Portanto, você pode visualizar os dados de Nova York e Texas como este:

filter(murders, state %in% c("New York", "Texas"))

Crie um novo data frame chamado murders_nw apenas com os estados nordeste e oeste. Quantos estados há nesta categoria?

7. Suponha que você queira morar no nordeste ou oeste e que a taxa de homicídios seja menor que 1. Queremos ver os dados dos estados que satisfazem essas opções. Observe que você pode usar operadores lógicos com filter. Aqui está um exemplo em que filtramos para manter apenas pequenos estados na região nordeste.

filter(murders, population < 5000000 & region == "Northeast")

Certifique-se de que murders foi definido com rate e rank e ainda tem todos os estados. Crie uma tabela chamada my_states contendo linhas para estados que satisfazem ambas as condições: é no nordeste ou oeste e a taxa de homicídios é menor que 1. select para exibir apenas o nome, a taxa e o intervalo do estado.

4.5 O cano_: %>%

Com dplyr, podemos executar uma série de operações, por exemplo select e então filter, enviando os resultados de uma função para outra usando o que é chamado de pipe operator: %>%. Alguns detalhes estão incluídos abaixo.

Escrevemos o código acima para mostrar três variáveis (estado, região, taxa) para estados com taxas de homicídio abaixo de 0,71. Para isso, definimos o objeto intermediário new_table. Em dplyr, podemos escrever um código mais parecido com uma descrição do que queremos fazer sem objetos intermediários:

\[ \mbox {original data } \rightarrow \mbox { select } \rightarrow \mbox { filter } \]

Para essa operação, podemos usar o pipe %>%. O código fica assim:

murders %>% select(state, region, rate) %>% filter(rate <= 0.71)
#>           state        region  rate
#> 1        Hawaii          West 0.515
#> 2          Iowa North Central 0.689
#> 3 New Hampshire     Northeast 0.380
#> 4  North Dakota North Central 0.595
#> 5       Vermont     Northeast 0.320

Essa linha de código é equivalente às duas linhas de código anteriores. O que está acontecendo aqui?

Em geral, o pipe_envia o resultado que está no lado esquerdo do_pipe_para ser o primeiro argumento da função no lado direito do_pipe. Aqui está um exemplo simples:

16 %>% sqrt()
#> [1] 4

Podemos continuar canalizando valores (piping em inglês) ao longo de:

16 %>% sqrt() %>% log2()
#> [1] 2

A declaração acima é equivalente a log2(sqrt(16)).

Lembre-se de que o pipe envia valores para o primeiro argumento, para que possamos definir outros argumentos como se o primeiro argumento já estivesse definido:

16 %>% sqrt() %>% log(base = 2)
#> [1] 2

Portanto, ao usar _pipe_com_data frames_edplyr, não precisamos mais especificar o primeiro argumento necessário, pois as funções dplyr que descrevemos pegam todos os dados como o primeiro argumento. No código que escrevemos:

murders %>% select(state, region, rate) %>% filter(rate <= 0.71)

murders é o primeiro argumento da função select e o novo data frame (anteriormente new_table) é o primeiro argumento da função filter.

Observe que o pipe funciona bem com funções em que o primeiro argumento são os dados de entrada. As funções nos pacotes __tidyverse__e__dplyr_têm esse formato e podem ser facilmente usadas com o_pipe.

4.6 Exercícios

1. O cano_ %>% ele pode ser usado para executar operações sequencialmente sem precisar definir objetos intermediários. Comece redefinindo murders para incluir a taxa e o intervalo.

murders <- mutate(murders, rate = total/ population * 100000,
rank = rank(-rate))

Na solução do exercício anterior, fizemos o seguinte:

my_states <- filter(murders, region %in% c("Northeast", "West") &
rate < 1)

select(my_states, state, rate, rank)

O cano_ %>% nos permite executar as duas operações sequencialmente sem precisar definir uma variável intermediária my_states. Então, poderíamos ter mudado e selecionado na mesma linha assim:

mutate(murders, rate = total/ population * 100000,
rank = rank(-rate)) %>%
select(state, rate, rank)

Eu sinto isso select ele não possui mais um data frame como seu primeiro argumento. O primeiro argumento é assumido como o resultado da operação realizada imediatamente antes %>%.

Repita o exercício anterior, mas agora, em vez de criar um novo objeto, mostre o resultado e inclua apenas as colunas de status, velocidade e intervalo. Use um pipe %>% para fazer isso em uma linha.

2. Reiniciar murders para a tabela original usando data(murders). Use um pipe_para criar um novo_data frame chamado my_states ele considera apenas os estados do nordeste ou oeste que têm uma taxa de homicídios menor que 1 e contém apenas as colunas de estado, taxa e faixa. O pipe também deve ter quatro componentes separados por três %>%. O código deve se parecer com o seguinte:

my_states <- murders %>%
mutate SOMETHING %>%
filter SOMETHING %>%
select SOMETHING

4.7 Como resumir dados

Uma parte importante da análise exploratória de dados é resumir os dados. A média e o desvio padrão são dois exemplos de estatísticas resumidas amplamente usadas. É possível obter resumos mais informativos, dividindo primeiro os dados em grupos. Nesta seção, abordamos dois novos verbos dplyr que facilitam esses cálculos: summarize e group_by. Aprendemos a acessar os valores resultantes usando a função pull.

4.7.1 summarize

A função summarize o de dplyr oferece uma maneira de calcular estatísticas resumidas com código intuitivo e legível. Começamos com um exemplo simples baseado em alturas. O conjunto de dados heights inclui as alturas e o sexo relatados pelos alunos em uma pesquisa de classe.

library(dplyr)
library(dslabs)
data(heights)

O código a seguir calcula a média e o desvio padrão para mulheres:

s <- heights %>%
filter(sex == "Female") %>%
summarize(average = mean(height), standard_deviation = sd(height))
s
#>   average standard_deviation
#> 1    64.9               3.76

Isso leva nossa tabela de dados original como entrada, a filtra para incluir apenas as linhas que representam as fêmeas e, em seguida, produz uma nova tabela de resumo com apenas a média e o desvio padrão das alturas. Podemos escolher os nomes das colunas da tabela resultante. Por exemplo, acima, decidimos usar average e standard_deviation, mas poderíamos ter usado outros nomes da mesma maneira.

Como a tabela resultante armazenada em s é um data frame, podemos acessar os componentes com o operador de acesso $:

s$average
#> [1] 64.9
s$standard_deviation
#> [1] 3.76

Como na maioria das outras funções dplyr, summarize conhece os nomes das variáveis e podemos usá-las diretamente. Então, quando escrevemos mean(height) dentro da chamada de função summarize, a função acessa a coluna denominada “height” ou height e calcula a média do vetor numérico resultante. Podemos calcular qualquer outro resumo que opere em vetores e retorne um único valor. Por exemplo, podemos adicionar as medianas, alturas mínima e máxima desta maneira:

heights %>%
filter(sex == "Female") %>%
summarize(median = median(height), minimum = min(height),
maximum = max(height))
#>   median minimum maximum
#> 1     65      51      79

Podemos obter esses três valores com apenas uma linha usando a função quantile: por exemplo, quantile(x, c(0,0.5,1)) retorna o mínimo (percentil 0), mediana (percentil 50) e máximo (percentil 100) do vetor x. No entanto, se tentarmos usar uma função como essa que retorne dois ou mais valores dentro summarize:

heights %>%
filter(sex == "Female") %>%
summarize(range = quantile(height, c(0, 0.5, 1)))

receberemos um erro: Error: expecting result of length one, got : 2. Com função summarize, podemos chamar apenas funções que retornam um único valor. Na seção 4.12 vamos aprender a lidar com funções que retornam mais de um valor.

Para outro exemplo de como podemos usar a função summarize vamos calcular a taxa média de homicídios nos Estados Unidos. Lembre-se de que nossa tabela de dados inclui o total de assassinatos e o tamanho da população de cada estado e já usamos dplyr para adicionar uma coluna de taxa de assassinatos:

murders <- murders %>% mutate(rate = total/population*100000)

Lembre-se que a taxa de homicídios nos EUA EUA não é a média das taxas de homicídio do estado:

summarize(murders, mean(rate))
#>   mean(rate)
#> 1       2.78

Isso ocorre porque no cálculo anterior, estados pequenos têm o mesmo peso que estados grandes. A taxa de assassinatos nos Estados Unidos é o número total de assassinatos nos Estados Unidos dividido pela população total. Portanto, o cálculo correto é:

us_murder_rate <- murders %>%
summarize(rate = sum(total)/ sum(population) * 100000)
us_murder_rate
#>   rate
#> 1 3.03

Este cálculo conta estados maiores proporcionalmente ao seu tamanho, resultando em um valor maior.

4.7.2 pull

O objeto us_murder_rate definido acima representa apenas um número. No entanto, estamos armazenando-o em um data frame:

class(us_murder_rate)
#> [1] "data.frame"

desde que, como a maioria das funções dplyr, summarize sempre retorna um data frame.

Isso pode ser problemático se quisermos usar esse resultado com funções que requerem um valor numérico. Aqui está um truque útil para acessar os valores armazenados nos dados quando usamos pipes: quando um objeto de dados é canalizado (_ é canalizado em inglês), esse objeto e suas colunas podem ser acessados usando a função pull. Para entender o que queremos dizer, considere esta linha de código:

us_murder_rate %>% pull(rate)
#> [1] 3.03

Isso retorna o valor na coluna rate do us_murder_rate tornando-o equivalente a us_murder_rate$rate.

Para obter um número da tabela de dados original com uma linha de código, podemos escrever:

us_murder_rate <- murders %>%
summarize(rate = sum(total)/ sum(population) * 100000) %>%
pull(rate)

us_murder_rate
#> [1] 3.03

que agora é numérico:

class(us_murder_rate)
#> [1] "numeric"

4.7.3 Como agrupar e resumir com group_by

Uma operação comum na exploração de dados é primeiro dividir os dados em grupos e depois calcular resumos para cada grupo. Por exemplo, podemos querer calcular a média e o desvio padrão para as alturas de homens e mulheres separadamente. A função group_by nos ajuda a fazer isso.

Se escrevermos isso:

heights %>% group_by(sex)
#> # A tibble: 1,050 x 2
#> # Groups:   sex [2]
#>   sex   height
#>   <fct>  <dbl>
#> 1 Male      75
#> 2 Male      70
#> 3 Male      68
#> 4 Male      74
#> 5 Male      61
#> # … with 1,045 more rows

O resultado não parece muito diferente de heights exceto que vemos Groups: sex [2] quando imprimimos o objeto. Embora não seja imediatamente óbvio a partir de sua aparência, agora é um data frame_especial chamado data de dados agrupados, e as funções de dplyr, em particular summarize, eles se comportarão de maneira diferente quando agirem sobre esse objeto. Conceitualmente, eles podem pensar nessa tabela como muitas tabelas, com as mesmas colunas, mas não necessariamente com o mesmo número de linhas, empilhadas em um objeto. Quando resumimos os dados após o agrupamento, é o que acontece:

heights %>%
group_by(sex) %>%
summarize(average = mean(height), standard_deviation = sd(height))
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 2 x 3
#>   sex    average standard_deviation
#>   <fct>    <dbl>              <dbl>
#> 1 Female    64.9               3.76
#> 2 Male      69.3               3.61

A função summarize aplique o resumo a cada grupo separadamente.

Para ver outro exemplo, vamos calcular a taxa média de homicídios nas quatro regiões do país:

murders %>%
group_by(region) %>%
summarize(median_rate = median(rate))
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 4 x 2
#>   region        median_rate
#>   <fct>               <dbl>
#> 1 Northeast            1.80
#> 2 South                3.40
#> 3 North Central        1.97
#> 4 West                 1.29

4.8 Como encomendar os data frames

Ao examinar um conjunto de dados, geralmente é conveniente classificar numericamente ou alfabeticamente, com base em uma ou mais das colunas da tabela. Conhecemos as funções order e sort, mas para classificar tabelas inteiras, a função arrange dplyr é útil. Por exemplo, aqui ordenamos os estados de acordo com o tamanho da população:

murders %>%
arrange(population) %>%
head()
#>                  state abb        region population total   rate
#> 1              Wyoming  WY          West     563626     5  0.887
#> 2 District of Columbia  DC         South     601723    99 16.453
#> 3              Vermont  VT     Northeast     625741     2  0.320
#> 4         North Dakota  ND North Central     672591     4  0.595
#> 5               Alaska  AK          West     710231    19  2.675
#> 6         South Dakota  SD North Central     814180     8  0.983

Com arrange podemos decidir qual coluna usar para solicitar. Para ver os estados por população, do menor para o maior, organizamos pela rate :

murders %>%
arrange(rate) %>%
head()
#>           state abb        region population total  rate
#> 1       Vermont  VT     Northeast     625741     2 0.320
#> 2 New Hampshire  NH     Northeast    1316470     5 0.380
#> 3        Hawaii  HI          West    1360301     7 0.515
#> 4  North Dakota  ND North Central     672591     4 0.595
#> 5          Iowa  IA North Central    3046355    21 0.689
#> 6         Idaho  ID          West    1567582    12 0.766

Observe que o comportamento padrão é classificar em ordem crescente. Em dplyr, a função desc transformar um vetor para que fique em ordem decrescente. Para classificar a tabela em ordem decrescente, podemos escrever:

murders %>%
arrange(desc(rate))

4.8.1 Como encomendar aninhado

Se estivermos ordenando uma coluna quando houver empates, podemos usar uma segunda coluna para quebrar o empate. Da mesma forma, uma terceira coluna pode ser usada para romper os laços entre a primeira e a segunda, e assim por diante. Aqui nós ordenamos por region, na região, ordenamos por taxa de homicídio:

murders %>%
arrange(region, rate) %>%
head()
#>           state abb    region population total  rate
#> 1       Vermont  VT Northeast     625741     2 0.320
#> 2 New Hampshire  NH Northeast    1316470     5 0.380
#> 3         Maine  ME Northeast    1328361    11 0.828
#> 4  Rhode Island  RI Northeast    1052567    16 1.520
#> 5 Massachusetts  MA Northeast    6547629   118 1.802
#> 6      New York  NY Northeast   19378102   517 2.668

4.8.2 Os primeiros \(n\)

No código acima, usamos a função head para impedir que a página seja preenchida com todo o conjunto de dados. Se queremos ver uma proporção maior, podemos usar a função top_n. Essa função usa um data frame como o primeiro argumento, o número de linhas a serem exibidas no segundo e a variável a ser filtrada no terceiro. Aqui está um exemplo de como visualizar as 5 principais linhas:

murders %>% top_n(5, rate)
#>                  state abb        region population total  rate
#> 1 District of Columbia  DC         South     601723    99 16.45
#> 2            Louisiana  LA         South    4533372   351  7.74
#> 3             Maryland  MD         South    5773552   293  5.07
#> 4             Missouri  MO North Central    5988927   321  5.36
#> 5       South Carolina  SC         South    4625364   207  4.48

Observe que as linhas não são ordenadas por rate, apenas filtrado. Se quisermos pedir, precisamos usar arrange. Lembre-se de que, se o terceiro argumento for deixado em branco, top_n filtrar pela última coluna.

4.9 Exercícios

Para esses exercícios, usaremos os dados da pesquisa coletados pelo Centro Nacional de Estatísticas da Saúde dos Estados Unidos (NCHS). Este centro realiza uma série de pesquisas em saúde e nutrição desde a década de 1960. Desde 1999, cerca de 5.000 indivíduos de todas as idades foram entrevistadas a cada ano e concluíram o componente de triagem de saúde da pesquisa. Alguns dos dados estão disponíveis no pacote NHANES. Depois de instalar o pacote NHANES, eles podem carregar os dados da seguinte maneira:

library(NHANES)
data(NHANES)

Os dados NHANES têm muitos valores ausentes. As funções mean e sd retornará NA se alguma das entradas do vetor de entrada for uma NA. Aqui está um exemplo:

library(dslabs)
data(na_example)
mean(na_example)
#> [1] NA
sd(na_example)
#> [1] NA

Para ignorar o NA s podemos usar o argumento na.rm:

mean(na_example, na.rm = TRUE)
#> [1] 2.3
sd(na_example, na.rm = TRUE)
#> [1] 1.22

Vamos agora explorar os dados NHANES.

1. Oferecemos algumas informações básicas sobre pressão arterial. Primeiro, vamos selecionar um grupo para definir o padrão. Usaremos mulheres de 20 a 29 anos. AgeDecade é uma variável categórica com essas idades. Observe que a categoria está codificada “20-29,” com um espaço na frente! Qual é a média e desvio padrão da pressão arterial sistólica, conforme armazenado na variável BPSysAve? Salve-o em uma variável chamada ref.

Dica: use filter e summarize e use o argumento na.rm = TRUE ao calcular a média e o desvio padrão. Você também pode filtrar valores de NA usando filter.

2. Usando um pipe, atribua a média a uma variável numérica ref_avg. Dica: use o código semelhante ao acima e depois pull.

3. Agora insira os valores mínimo e máximo para o mesmo grupo.

4. Calcule a média e o desvio padrão para as mulheres, mas para cada faixa etária separadamente, em vez de uma década selecionada, como na pergunta 1. Observe que as faixas etárias são definidas por AgeDecade. Dica: em vez de filtrar por idade e sexo, filtre por Gender e depois use group_by.

5. Repita o exercício 4 para os meninos.

6. Podemos combinar os dois resumos dos exercícios 4 e 5 em uma linha de código. Isto é porque group_by permite agrupar por mais de uma variável. Obtenha uma excelente tabela de resumo usando group_by(AgeDecade, Gender).

7. Para homens entre 40 e 49 anos, compare a pressão arterial sistólica por raça, conforme aparece na variável Race1. Encomende a tabela resultante com base na pressão arterial sistólica média mais baixa para a mais alta.

4.10 Tibbles

Os dados tidy_devem ser armazenados em_data frames. Discutimos o data frame na Seção 2.4.1 e estamos usando o data frame murders ao longo do livro. Na seção 4.7.3 nós introduzimos a função group_by, que permite estratificar os dados antes de calcular as estatísticas de resumo. Mas onde estão as informações do grupo armazenadas no data frame?

murders %>% group_by(region)
#> # A tibble: 51 x 6
#> # Groups:   region [4]
#>   state      abb   region population total  rate
#>   <chr>      <chr> <fct>       <dbl> <dbl> <dbl>
#> 1 Alabama    AL    South     4779736   135  2.82
#> 2 Alaska     AK    West       710231    19  2.68
#> 3 Arizona    AZ    West      6392017   232  3.63
#> 4 Arkansas   AR    South     2915918    93  3.19
#> 5 California CA    West     37253956  1257  3.37
#> # … with 46 more rows

Observe que não há colunas com essas informações. Mas se você olhar para a saída acima, verá a linha A tibble seguido por dimensões. Podemos aprender a classe do objeto retornado usando:

murders %>% group_by(region) %>% class()
#> [1] "grouped_df" "tbl_df"     "tbl"        "data.frame"

O tbl é um tipo especial de data frame. As funções group_by e summarize sempre retorne esse tipo de data frame. A função group_by retorna um tipo especial de tbl, a grouped_df. Discutiremos isso mais tarde. Para consistência, os verbos de manipulação dplyr ( select, filter, mutate e arrange) preserva a classe input: se eles recebem um data frame regular, eles retornam um data frame regular, enquanto que se eles recebem um tibble, eles retornam um tibble. Mas tibbles_é o formato preferido_tidyverse e, como resultado, funções tidyverse_que produzem um_data frame_do zero retornam uma_tibble. Por exemplo, no capítulo 5 veremos que as funções tidyverse_usadas para importar dados criam_tibbles.

Tibbles_são muito semelhantes aos_data frames. De fato, eles podem pensar neles como uma versão moderna de data frames. No entanto, existem três diferenças importantes que descreveremos abaixo.

4.10.1 Tibbles parece melhor

O método de impressão para tibbles_é mais legível que o de um_data frame. Para ver isso, compare a saída da digitação murders e a saída dos assassinatos, se os tornarmos uma tagarelice. Podemos fazer isso usando as_tibble(murders). Se você usa o RStudio, a saída_de_tabela_ ajusta-se ao tamanho da sua janela. Para ver isso, altere a largura do seu console R e observe como mais/ menos colunas são exibidas.

4.10.2 Tibbles_subconjuntos são_tibbles

Se criarmos subconjuntos das colunas de um data frame, eles poderão retornar um objeto que não seja data frame, como um vetor ou escalar. Por exemplo:

class(murders[,4])
#> [1] "numeric"

não é um data frame. Com tibbles isso não acontece:

class(as_tibble(murders)[,4])
#> [1] "tbl_df"     "tbl"        "data.frame"

Isso é útil em tidyverse, pois as funções requerem data frames_como_input.

Com tibbles, se você deseja acessar o vetor que define uma coluna e não recuperar um data frame, deve usar o operador de acesso $:

class(as_tibble(murders)$population)
#> [1] "numeric"

Um recurso relacionado é que tibbles avisará se eles tentarem acessar uma coluna que não existe. Por exemplo, se escrevermos acidentalmente Population ao invés de population nós vemos que:

murders$Population
#> NULL

retorna um NULL sem aviso, o que pode dificultar a depuração. Por outro lado, se tentarmos isso com uma tibble, obteremos um aviso informativo:

as_tibble(murders)$Population
#> Warning: Unknown or uninitialised column: `Population`.
#> NULL

4.10.3 Tibbles pode ter entradas complexas

Embora as colunas do data frame devam ser vetores de números, cadeias ou valores lógicos, tibbles pode ter objetos mais complexos, como listas ou funções. Além disso, podemos criar tibbles com funções:

tibble(id = c(1, 2, 3), func = c(mean, median, sd))
#> # A tibble: 3 x 2
#>      id func  
#>   <dbl> <list>
#> 1     1 <fn>  
#> 2     2 <fn>  
#> 3     3 <fn>

4.10.4 Tibbles podem ser agrupados

A função group_by retorna um tipo especial de tibble: um tibble agrupado. Essa classe armazena informações que permitem saber quais linhas estão em quais grupos. Funções Tidyverse, em particular a função summarize, estão cientes das informações do grupo.

4.10.5 Como criar um tibble usando tibble ao invés de data.frame

Às vezes, é útil criarmos nossos próprios data frames. Para criar um data frame_no formato_tibble, você pode usar a função tibble.

grades <- tibble(names = c("John", "Juan", "Jean", "Yao"),
exam_1 = c(95, 80, 90, 85),
exam_2 = c(90, 85, 85, 90))

Observe que a base R (nenhum pacote carregado) tem uma função com um nome muito semelhante, data.frame, que pode ser usado para criar um data frame_regular em vez de um tibble. Outra diferença importante é que, por padrão data.frame forçar a conversão de caracteres em fatores sem fornecer um aviso ou mensagem:

grades <- data.frame(names = c("John", "Juan", "Jean", "Yao"),
exam_1 = c(95, 80, 90, 85),
exam_2 = c(90, 85, 85, 90))
class(grades$names)
#> [1] "character"

Para evitar isso, usamos o argumento bastante complicado stringsAsFactors:

grades <- data.frame(names = c("John", "Juan", "Jean", "Yao"),
exam_1 = c(95, 80, 90, 85),
exam_2 = c(90, 85, 85, 90),
stringsAsFactors = FALSE)
class(grades$names)
#> [1] "character"

Para converter um data frame_normal em um_tibble, você pode usar a função as_tibble.

as_tibble(grades) %>% class()
#> [1] "tbl_df"     "tbl"        "data.frame"

4.11 O operador de ponto

Uma das vantagens de usar o pipe %>% é que não precisamos continuar nomeando novos objetos enquanto manipulamos o data frame. Lembre-se de que, se quisermos calcular a taxa média de homicídios nos estados do sul, em vez de escrever:

tab_1 <- filter(murders, region == "South")
tab_2 <- mutate(tab_1, rate = total/ population * 10^5)
rates <- tab_2$rate
median(rates)
#> [1] 3.4

podemos evitar definir novos objetos intermediários escrevendo:

filter(murders, region == "South") %>%
mutate(rate = total/ population * 10^5) %>%
summarize(median = median(rate)) %>%
pull(median)
#> [1] 3.4

Podemos fazer isso porque cada uma dessas funções usa um data frame como o primeiro argumento. Mas e se quisermos acessar um componente do data frame? Por exemplo, e se a função pull não está disponível e queremos acessar tab_2$rate? Que nome de quadro de dados usamos? A resposta é o operador de ponto (dot operator em inglês).

Por exemplo, para acessar o vetor de velocidade sem a função pull, poderíamos usar:

rates <-filter(murders, region == "South") %>%
mutate(rate = total/ population * 10^5) %>%
.$rate
median(rates)
#> [1] 3.4

Na próxima seção, veremos outras instâncias nas quais usar o . é útil.

4.12 do

As funções tidyverse_sabem como interpretar_tibbles agrupados. Além disso, para facilitar o script através do pipe %>% as funções tidyverse_retornam constantemente_data frames, pois isso garante que a saída de uma função seja aceita como a entrada de outra. Mas a maioria das funções R não reconhece tibbles_agrupados nem retorna_data frames. A função quantile é um exemplo que descrevemos na seção 4.7.1. A função do serve como uma ponte entre as funções de R, como quantile e o tidyverse. A função do entende tibbles_agrupados e sempre retorna um_data frame.

Na seção 4.7.1, percebemos que, se tentarmos usar quantile para obter o mínimo, a mediana e o máximo de uma chamada, receberemos um erro: Error: expecting result of length one, got : 2.

data(heights)
heights %>%
filter(sex == "Female") %>%
summarize(range = quantile(height, c(0, 0.5, 1)))

Nós podemos usar a função do para corrigir isso.

Primeiro, precisamos escrever uma função que se ajuste ao foco do tidyverse: isto é, ele recebe um data frame_e retorna um_data frame.

my_summary <- function(dat){
x <- quantile(dat$height, c(0, 0.5, 1))
tibble(min = x[1], median = x[2], max = x[3])
}

Agora podemos aplicar a função ao conjunto de dados de altura para obter os resumos:

heights %>%
group_by(sex) %>%
my_summary
#> # A tibble: 1 x 3
#>     min median   max
#>   <dbl>  <dbl> <dbl>
#> 1    50   68.5  82.7

Mas não é isso que queremos. Queremos um resumo para cada gênero e o código retornou apenas um resumo. Isto é porque my_summary não faz parte do tidyverse_e não sabe como lidar com os_tibbles agrupados. do faz esta conexão:

heights %>%
group_by(sex) %>%
do(my_summary(.))
#> # A tibble: 2 x 4
#> # Groups:   sex [2]
#>   sex      min median   max
#>   <fct>  <dbl>  <dbl> <dbl>
#> 1 Female    51   65.0  79  
#> 2 Male      50   69    82.7

Lembre-se de que aqui precisamos usar o operador de ponto. O tibble criado por group_by é canalizado para do. Dentro da chamada para do, o nome dessa tibble é . e queremos enviá-lo para my_summary. Se eles não usarem o ponto, então my_summary ele não tem argumento e retorna um erro nos dizendo que o argument "dat" . Você pode ver o erro digitando:

heights %>%
group_by(sex) %>%
do(my_summary())

Se eles não usarem parênteses, a função não será executada e, em vez disso, do tente retornar a função. Isso dá um erro porque do você sempre deve retornar um data frame. Você pode ver o erro digitando:

heights %>%
group_by(sex) %>%
do(my_summary)

4.13 O pacote purrr

Na seção 3.5 nós aprendemos sobre função sapply, o que nos permitiu aplicar a mesma função a cada elemento de um vetor. Criamos uma função e usamos sapply para calcular a soma do primeiro n números inteiros para vários valores de n assim:

compute_s_n <- function(n){
x <- 1:n
sum(x)
}
n <- 1:25
s_n <- sapply(n, compute_s_n)

Esse tipo de operação, que aplica a mesma função ou procedimento aos elementos de um objeto, é bastante comum na análise de dados. O pacote purrr inclui funções semelhantes a sapply mas eles interagem melhor com outras funções tidyverse. A principal vantagem é que podemos controlar melhor o tipo de resultado das funções. Por contraste, sapply você pode retornar vários tipos diferentes de objetos, convertendo-os quando conveniente. As funções purrr nunca farão isso: elas retornarão objetos de um tipo específico ou retornarão um erro se isso não for possível.

A primeira função de purrr que aprenderemos é map, que funciona muito semelhante a sapply mas sempre, sem exceção, ele retorna uma lista:

library(purrr)
s_n <- map(n, compute_s_n)
class(s_n)
#> [1] "list"

Se queremos um vetor numérico, podemos usar map_dbl que sempre retorna um vetor de valores numéricos.

s_n <- map_dbl(n, compute_s_n)
class(s_n)
#> [1] "numeric"

Isso produz os mesmos resultados que a chamada sapply que vemos acima.

Uma função __purrr_particularmente útil para interagir com o resto do_tidyverse é map_df, que sempre retorna um tibble data frame. No entanto, a função chamada deve retornar um vetor ou uma lista com nomes. Por esse motivo, o código a seguir resultaria em um erro Argument 1 must have names:

s_n <- map_df(n, compute_s_n)

Precisamos alterar a função para corrigir isso:

compute_s_n <- function(n){
x <- 1:n
tibble(sum = sum(x))
}
s_n <- map_df(n, compute_s_n)

O pacote purrr oferece muito mais funcionalidades não discutidas aqui. Para mais detalhes, você pode consultar [este recurso online] (https://jennybc.github.io/purrr-tutorial/).

4.14 Os condicionais tidyverse

Uma análise de dados típica geralmente envolve uma ou mais operações condicionais. Na seção 3.1 nós descrevemos a função ifelse, que usaremos extensivamente neste livro. Nesta seção, apresentamos duas funções dplyr que oferecem funcionalidade adicional para executar operações condicionais.

4.14.1 case_when

A função case_when é útil para vetorizar instruções condicionais. Isso é semelhante a ifelse mas pode gerar qualquer número de valores, em vez de apenas TRUE ou FALSE. Aqui está um exemplo que divide os números em negativo, positivo e 0:

x <- c(-2, -1, 0, 1, 2)
case_when(x < 0 ~ "Negative", x > 0 ~ "Positive", TRUE ~ "Zero")
#> [1] "Negative" "Negative" "Zero"     "Positive" "Positive"

Um uso comum dessa função é definir variáveis categóricas com base nas variáveis existentes. Por exemplo, suponha que desejamos comparar as taxas de homicídios em quatro grupos de estados: Nova Inglaterra, Costa Oeste, Sul e Outro. Para cada estado, primeiro perguntamos se está na Nova Inglaterra. Se a resposta for não, perguntamos se está na Costa Oeste e, se não, perguntamos se está no Sul e, se não, atribuímos uma das opções acima (Outro). Aqui vemos como usamos case_when para fazer isso:

murders %>%
mutate(group = case_when(
abb %in% c("ME", "NH", "VT", "MA", "RI", "CT") ~ "New England",
abb %in% c("WA", "OR", "CA") ~ "West Coast",
region == "South" ~ "South",
TRUE ~ "Other")) %>%
group_by(group) %>%
summarize(rate = sum(total)/ sum(population) * 10^5)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 4 x 2
#>   group        rate
#>   <chr>       <dbl>
#> 1 New England  1.72
#> 2 Other        2.71
#> 3 South        3.63
#> 4 West Coast   2.90

4.14.2 between

Uma operação comum na análise de dados é determinar se um valor cai dentro de um intervalo. Podemos verificar isso usando condicionais. Por exemplo, para verificar se os elementos de um vetor x estão entre a e b nós podemos escrever:

x >= a & x <= b

No entanto, isso pode ficar complicado, especialmente dentro da abordagem tidyverse. A função between execute a mesma operação:

between(x, a, b)

4.15 Exercícios

1. Carregar o conjunto de dados murders. Qual dos seguintes é verdadeiro?

para. murders está no formato tidy_e é armazenado em uma_tibble. b. murders está no formato tidy_e é armazenado em um_data frame. c. murders não está no formato tidy_e é armazenado em um_tibble. d. murders não está no formato tidy_e é armazenado em um_data frame.

2. Usar as_tibble converter a tabela de dados murders em uma tibble e salve-a em um objeto chamado murders_tibble.

3. Use a função group_by converter murders em uma tibble que é agrupada por região.

4. Escreva o código tidyverse que é equivalente a este código:

exp(mean(log(murders$population)))

Escreva usando o pipe para que cada função seja chamada sem argumentos. Use o operador de ponto para acessar a população. Dica: o código deve começar com murders %>%.

5. Use o map_df para criar um data frame com três colunas denominadas n, s_n e s_n_2. A primeira coluna deve conter os números de 1 a 100. A segunda e a terceira coluna devem conter a soma de 1 a 100. \(n\) com \(n\) representando o número da linha.