4 Manipulando os dados

Neste capítulo, usaremos estes pacotes:

Após obter uma boa fonte de dados, e carregá-los para poder trabalhá-los no R, você certamente precisará realizar algumas limpezas e manipulações para que os dados estejam no ponto ideal para as fases finais de uma análise: execução de modelos econométricos, visualizações de dados, tabelas agregadas, relatórios etc. A realidade é que, na prática, os dados nunca estarão do jeito que você de fato precisa. Portanto, é fundamental dominar técnicas de manipulação de dados.

Entendamos a manipulação de dados como o ato de transformar, reestruturar, limpar, agregar e juntar os dados. Para se ter uma noção da importância dessa fase, alguns estudiosos da área de Ciência de Dados costumam afirmar que 80% do trabalho é encontrar uma boa fonte de dados, limpar e preparar os dados, sendo que os 20% restantes seriam o trabalho de aplicar modelos e realizar alguma análise propriamente dita.

80% of data analysis is spent on the process of cleaning and preparing the data (Dasu and Johnson, 2003).

Data preparation is not just a first step, but must be repeated many over the course of analysis as new problems come to light or new data is collected (Hadley Wickham).

4.1 Tipos de Variáveis e Colunas

Existem diversos tipos de objetos, e cada tipo “armazena” um conteúdo diferente, desde tabelas de dados recém-carregados a textos, números, ou simplesmente a afirmação de verdadeiro ou falso (Boleano).

Repare nas atribuições acima. Usaremos a função class() para ver o tipo de cada uma:

## [1] "numeric"
## [1] "numeric"
## [1] "numeric"
## [1] "character"
## [1] "logical"
## [1] "logical"

Esses são alguns dos tipos básicos de objetos/variáveis no R. Para valores inteiros ou decimais, numeric , character para valores textuais e logical para valores lógicos (verdadeiro ou falso). Existe também o tipo integer, que representa apenas números inteiros, sem decimais, porém, na maioria das vezes, o R interpreta o integer como numeric, pois o integer também é um numeric.

Além dos tipos básicos, existem também os tipos “complexos”, que são vector, array, matrix, list, data.frame e factor.

Data frame é, provavelmente, o tipo de dado complexo mais utilizado em R. É nele que você armazena conjuntos de dados estruturados em linhas e colunas. Um data frame possui colunas nomeadas, sendo que todas as colunas possuem a mesma quantidade de linhas. Imagine o dataframe como uma tabela.

## [1] "spec_tbl_df" "tbl_df"      "tbl"         "data.frame"
## [1] 9262   15

Percebeu o termo tbl no output acima? Significa tibble(), que, conforme o próprio pacote chamado tibble descreve, corresponde a uma moderna implementação da estrutura data.frame. A classe tibble não possui algumas deficiências da classe data.frame, por isso a usaremos sempre que possui. É muito simples transformar um data.frame em tibble.

## [1] "data.frame"
## [1] "tbl_df"     "tbl"        "data.frame"

Imprima no seu console os dois objetos e note a diferença:

Outro tipo que já utilizamos bastante até agora, mas que não foi detalhado, é o vector, ou vetor. Vetores são sequências unidimensionais de valores de um mesmo tipo:

## [1] "tipo1" "tipo2" "tipo3" "tipo4"
## [1]    1    2    5    8 1001
##  [1] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [34] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
##  [1]   0   5  10  15  20  25  30  35  40  45  50  55  60  65  70  75
## [17]  80  85  90  95 100
## [1]  TRUE  TRUE  TRUE FALSE FALSE

Para a criação de vetores, usamos a função de combinação de valores c() (combine). Esta função vai combinar todos os parâmetros em um único vetor. Lembre-se: vetores são sequências que contêm apenas um tipo de dado.

Conhecendo o data.frame e o vector, você será capaz de entender como os dois se relacionam. Cada coluna de um data frame é um vetor. Um data frame pode ter colunas de diferentes tipos, mas cada coluna só pode ter registros de um único tipo.

Ficará mais claro a seguir. Veja como se cria um data.frame:

##    nome idade adulto uf
## 1  João    45   TRUE DF
## 2  José    12  FALSE SP
## 3 Maria    28   TRUE RJ
## 4 Joana    31   TRUE MG
## 'data.frame':    4 obs. of  4 variables:
##  $ nome  : chr  "João" "José" "Maria" "Joana"
##  $ idade : num  45 12 28 31
##  $ adulto: logi  TRUE FALSE TRUE TRUE
##  $ uf    : chr  "DF" "SP" "RJ" "MG"

4.1.1 Conversões de tipos de variáveis

Quando é feito o carregamento de algum arquivo de dados no R, ele tenta “deduzir” os tipos de dados de cada coluna. Nem sempre essa dedução sai correta e, eventualmente, você precisará converter de um tipo para o outro. O R tem algumas funções para fazer essas conversões.

## [1] "character"
## [1] 2015
## [1] "numeric"
## [1] "55"
## [1] "numeric"
## [1] 3
## [1] 1
## [1] 0
## [1] TRUE
## [1] FALSE

O R também tenta “forçar a barra”, às vezes, para te ajudar. Quando você faz uma operação entre dois tipos diferentes, ele tenta fazer algo chamado coerção de tipos, ou seja, ele tenta converter os dados para que a operação faça sentido. Caso o R não consiga fazer a coerção, ele vai mostrar uma mensagem de erro.

Experimente os comandos a seguir no console:

Recomendamos fortemente que sempre se realize as conversões explicitamente com as funções apropriadas ao invés de confiar na coerção do R, a não ser que se tenha certeza do resultado.

4.1.2 Outros tipos de variáveis

Existem outros tipos de variáveis bastante utilizados. Citaremos alguns deles, pois nesse curso utilizaremos muito pouco os demais tipos.

Tipo Descrição Dimensões Homogêneo
vector Coleção de elementos simples. Todos os elementos precisam ser do mesmo tipo básico de dado 1 Sim
array Coleção que se parece com o vector, mas é multidimensional n Sim
matrix Tipo especial de array com duas dimensões 2 Sim
list Objeto complexo com elementos que podem ser de diferentes tipos 1 Não
data.frame Tipo especial de lista, onde cada coluna é um vetor de apenas um tipo e todas as colunas têm o mesmo número de registros. É o tipo mais utilizado para se trabalhar com dados 2 Não
factor Tipo especial de vector, que só contém valores predefinidos (levels) e categóricos (characters). Não é possível adicionar novas categorias sem criação de novos levels 1 Não

4.1.3 Valores faltantes e o ‘NA’

Em casos onde não existe valor em uma coluna de uma linha, o R atribui NA. É muito comum lidar com conjuntos de dados que tenham ocorrências de NA em alguns campos. É importante saber o que se fazer em casos de NA, e nem sempre a solução será a mesma: varia de acordo com as suas necessidades.

Em algumas bases de dados, quem gera o dado atribui valores genéricos como 999 ou até mesmo um “texto vazio”, ' '. Neste caso, você provavelmente terá que substituir esses valores “omissos” por NA. Imputar dados em casos de NA é uma das várias estratégias para lidar-se com ocorrência de missing no conjunto dos dados.

Seguem algumas funções úteis para lidar-se com NA:

  • A função summary() pode ser usada para averiguar a ocorrência de NA.
  • A função is.na() realiza um teste para saber se a variável/coluna possui um valor NA. retorna TRUE se for NA e FALSE se não for.
  • A função complete.cases() retorna TRUE para as linhas em que todas as colunas possuem valores válidos (preenchidos) e FALSE para as linhas em que, em alguma coluna, existe um NA. Ou seja, esta função diz quais são as linhas (amostras) completas em todas as suas características (campos).
  • Algumas funções possuem o argumento na.rm, ou semelhantes, para desconsiderar NA no cálculo. É o caso da função mean() ou sum().

Por exemplo:

##      Ozone           Solar.R           Wind             Temp      
##  Min.   :  1.00   Min.   :  7.0   Min.   : 1.700   Min.   :56.00  
##  1st Qu.: 18.00   1st Qu.:115.8   1st Qu.: 7.400   1st Qu.:72.00  
##  Median : 31.50   Median :205.0   Median : 9.700   Median :79.00  
##  Mean   : 42.13   Mean   :185.9   Mean   : 9.958   Mean   :77.88  
##  3rd Qu.: 63.25   3rd Qu.:258.8   3rd Qu.:11.500   3rd Qu.:85.00  
##  Max.   :168.00   Max.   :334.0   Max.   :20.700   Max.   :97.00  
##  NA's   :37       NA's   :7                                       
##      Month            Day      
##  Min.   :5.000   Min.   : 1.0  
##  1st Qu.:6.000   1st Qu.: 8.0  
##  Median :7.000   Median :16.0  
##  Mean   :6.993   Mean   :15.8  
##  3rd Qu.:8.000   3rd Qu.:23.0  
##  Max.   :9.000   Max.   :31.0  
## 
##   [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE
##  [12] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [23] FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE
##  [34]  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE
##  [45]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
##  [56]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE
##  [67] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE
##  [78] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE
##  [89] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [100] FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE
## [111] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE
## [122] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [133] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [144] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE

4.2 Estruturas de Controle de Fluxo

Para auxiliar no processo de manipulação de dados, você eventualmente precisará de algumas técnicas e estruturas de controle de fluxo. Estruturas para controle de fluxo nada mais são do que loops e condições. São estruturas fundamentais para qualquer linguagem de programação.

4.2.1 If e Else

A estrutura condicional é algo bastante intuitivo. A estrutura de if (se) e else (então) usa os operadores lógicos apresentados anteriormente. Se a condição do if() for verdadeira, executa-se uma tarefa específica, se for falsa, executa-se uma tarefa diferente. A estrutura parece algo do tipo:

Da mesma forma, existe uma função que gera o mesmo resultado, o ifelse() (e uma do pacote dplyr, o if_else()).

Existe uma diferença entre as duas formas de “if else”: a estrutura if() {} else {} só opera variáveis, uma por uma, já a estrutura ifelse() opera vetores, ou seja, consegue fazer a comparação para todos os elementos. Isso faz com que a forma if() {} else {} seja mais utilizada para comparações fora dos dados, com variáveis avulsas. Já a estrutura ifelse() é mais usada para comparações dentro dos dados, com colunas, vetores e linhas.

Qualquer uma dessas estruturas pode ser “aninhada”, ou seja, encadeada. Por exemplo:

## [1] "VALOR MEDIO"

Ou ainda:

## [1] "VALOR BAIXO"

4.2.2 Loops

Trata-se de um dos conceitos mais importantes de qualquer linguagem de programação, em R não é diferente. Loops (ou laços) repetem uma sequência de comando quantas vezes você desejar, ou até que uma condição aconteça, variando-se alguns aspectos entre uma repetição e outra.

Supondo que você tenha que ler 400 arquivos de dados que você obteve de um cliente. Você vai escrever 400 vezes a funcão de leitura? Nesse caso, basta fazer um loop para percorrer todos os arquivos da pasta e ler um por um com a função de leitura.

4.2.2.1 For

O for() é usado para realizar uma série de ordens para uma determinada sequência ou índices (vetor). Sua sintaxe é bem simples:

## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25

Para cada valor (chamamos esse valor de i) dentro do vetor c(1, 2, 3, 4, 5), execute o comando print(i^2). Qualquer outro comando dentro das chaves { ... } seria executado para cada valor do vetor.

Para entendermos melhor, vamos repensar o exemplo das séries usando o for().

## [1] TRUE
## [1] "Leia o arquivo: arquivo1.txt"
## [1] "Leia o arquivo: arquivo10.txt"
## [1] "Leia o arquivo: arquivo11.txt"
## [1] "Leia o arquivo: arquivo12.txt"
## [1] "Leia o arquivo: arquivo13.txt"
## [1] "Leia o arquivo: arquivo2.txt"
## [1] "Leia o arquivo: arquivo3.txt"
## [1] "Leia o arquivo: arquivo4.txt"
## [1] "Leia o arquivo: arquivo5.txt"
## [1] "Leia o arquivo: arquivo6.txt"
## [1] "Leia o arquivo: arquivo7.txt"
## [1] "Leia o arquivo: arquivo8.txt"
## [1] "Leia o arquivo: arquivo9.txt"

Também é possível utilizar loop com if. No exemplo a seguir, queremos ver todos os números de 1 a 1000 que são divisíveis por 29 e por 3 ao mesmo tempo. Para isso, utilizaremos o operador %%, que mostra o resto da divisão. Se o resto for zero, é divisível.

## [1] 87
## [1] 174
## [1] 261
## [1] 348
## [1] 435
## [1] 522
## [1] 609
## [1] 696
## [1] 783
## [1] 870
## [1] 957

4.2.2.2 While

O while() também é uma estrutura de controle de fluxo do tipo loop, mas, diferentemente do for(), o while executa as tarefas repetidamente até que uma condição seja satisfeita, não percorrendo um vetor.

## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5

O uso do while é um pouco menos intuitivo, mas não menos importante. O while é mais apropriado para eventos de automação ou simulação, onde tarefas serão executadas quando um “gatilho” for acionado. Um simples exemplo para ajudar na intuição de seu uso é:

Temos uma pasta vazia. O loop abaixo vai monitorar essa pasta. Enquanto essa pasta estiver vazia, ele estará em execução. Quando você colocar um arquivo dentro dessa pasta, vai mudar a condição length(automatico) == 0 de TRUE para FALSE e vai mudar a condição length(automatico) > 0 de FALSE para TRUE, disparando todas as tarefas programadas. Usamos a função Sys.sleep(5) para que o código espere por mais cinco segundos antes de começar o loop novamente.

Faça o teste: execute o código acima, aguarde alguns segundos e perceba que nada aconteceu. Crie um arquivo qualquer dentro da pasta dados/automatico/. Imediatamente o loop será encerrado e as tarefas executadas. Observe o output em tela.

4.2.3 Funções

Funções “encapsulam” uma sequência de comandos e instruções. É uma estrutura nomeada, que recebe parâmetros para iniciar sua execução e retorna um resultado ao final. Até o momento, você já usou diversas funções. Vejamos então como criar uma função:

Agora tente entender a seguinte função:

## [1] "TeStE De fUnÇãO: lEtRaS MaIúScUlAs e mInÚsCuLaS"
## [1] "CoNsEgUiU EnTeNdEr?"
## [1] "É FáCiL UsAr fUnÇõEs!"

4.3 Manipulações com R base

Dominar a manipulação de data frames e vetores é muito importante. Em geral, toda manipulação pode ser feita com o R base, mas acreditamos que utilizando técnicas do tidyverse a atividade fica bem mais fácil. Portanto, utilizaremos o dplyr, um dos principais pacotes do tidyverse. Porém, alguns conceitos do R base são clássicos e precisam ser dominados.

4.3.1 Trabalhando com colunas de um data.frame

Para selecionar ou trabalhar separadamente com apenas um campo (coluna) do seu data.frame, deve-se utilizar o $. Repare nas funções abaixo e no uso do sifrão.

## [1] 41 36 12 18 NA 28
## [1] 14 30 NA 14 18 20
## [1] "integer"
## [1] TRUE
##  [1] "PSDB"    "PT"      "PRB"     "PDT"     "PR"      "PFL/DEM"
##  [7] "PMDB"    "PP"      "PSB"     "PTB"     "PCdoB"   "PSOL"   
## [13] "S/PART"  "PSC"     "PV1"

Lembre-se sempre: cada coluna de um data.frame é um vetor, portanto todos os registros (linhas) daquela coluna devem ser do mesmo tipo. Um data.frame pode ser considerado um conjunto de vetores nomeados, todos do mesmo tamanho, ou seja, todos com a mesma quantidade de registros.

Usando termos mais técnicos, um data frame é um conjunto de dados HETEROGÊNEOS, pois cada coluna pode ser de um tipo, e BIDIMENSIONAL, por possuir apenas linhas e colunas. Já o vetor é um conjunto de dados HOMOGÊNEO, pois só pode ter valores de um mesmo tipo, e UNIDIMENSIONAL.

Com esses conceitos em mente fica mais fácil entender o que mostraremos a seguir:

## [1]  0 15 30 45 60 75 90
## [1] 0
## [1] 15
## [1] 90
## [1] NA

A notação [] é usada para selecionar o elemento em uma ou mais posições do vetor.

## [1] 15 90

Uma notação parecida é usada para selecionar elementos no data.frame. Porém, como já comentamos, data frames são BIDIMENSIONAIS. Então usaremos a notação [,] com uma vírgula separando qual a linha (posição antes da vírgula) e a coluna (posição após a vírgula) que queremos selecionar.

## # A tibble: 1 x 15
##   VoteNumber SenNumber SenatorUpper Vote  Party GovCoalition State
##        <dbl> <chr>     <chr>        <chr> <chr> <lgl>        <chr>
## 1    2007001 PRS0002/… MAO SANTA    S     PMDB  TRUE         PI   
## # … with 8 more variables: FP <dbl>, Origin <dbl>, Contentious <dbl>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <dbl>, Content <chr>,
## #   Round <dbl>
## # A tibble: 1 x 1
##   SenatorUpper      
##   <chr>             
## 1 WELLINGTON SALGADO
## # A tibble: 2 x 3
##   SenNumber  SenatorUpper       Vote 
##   <chr>      <chr>              <chr>
## 1 PLS0229/06 MARISA SERRANO     S    
## 2 PLS0134/06 EPITACIO CAFETEIRA S
## # A tibble: 11 x 15
##    VoteNumber SenNumber SenatorUpper Vote  Party GovCoalition State
##         <dbl> <chr>     <chr>        <chr> <chr> <lgl>        <chr>
##  1    2007001 PRS0002/… MAO SANTA    S     PMDB  TRUE         PI   
##  2    2007001 PRS0002/… MAGNO MALTA  S     PR    TRUE         ES   
##  3    2007001 PRS0002/… EDUARDO SUP… S     PT    TRUE         SP   
##  4    2007001 PRS0002/… GILVAM BORG… S     PMDB  TRUE         AP   
##  5    2007001 PRS0002/… RAIMUNDO CO… S     PFL/… FALSE        SC   
##  6    2007001 PRS0002/… CICERO LUCE… S     PSDB  FALSE        PB   
##  7    2007001 PRS0002/… FRANCISCO D… S     PP    TRUE         RJ   
##  8    2007001 PRS0002/… OSMAR DIAS   N     PDT   FALSE        PR   
##  9    2007001 PRS0002/… ALFREDO NAS… S     PR    TRUE         AM   
## 10    2007001 PRS0002/… VALDIR RAUPP S     PMDB  TRUE         RO   
## 11    2007001 PRS0002/… GARIBALDI A… S     PMDB  TRUE         RN   
## # … with 8 more variables: FP <dbl>, Origin <dbl>, Contentious <dbl>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <dbl>, Content <chr>,
## #   Round <dbl>

Repare na notação c(10:20), você pode usar : para criar sequências. Experimente 1:1000

Também é possível selecionar o item desejado utilizando o próprio nome da coluna:

## # A tibble: 10 x 3
##    SenatorUpper       Party   State
##    <chr>              <chr>   <chr>
##  1 FLEXA RIBEIRO      PSDB    PA   
##  2 ARTHUR VIRGILIO    PSDB    AM   
##  3 FLAVIO ARNS        PT      PR   
##  4 MARCELO CRIVELLA   PRB     RJ   
##  5 JOAO DURVAL        PDT     BA   
##  6 PAULO PAIM         PT      RS   
##  7 EXPEDITO JUNIOR    PR      RO   
##  8 EFRAIM MORAIS      PFL/DEM PB   
##  9 ALOIZIO MERCADANTE PT      SP   
## 10 MAO SANTA          PMDB    PI

Existem diversas outras formas de seleção e manipulação de dados, como, por exemplo, seleção condicional:

## # A tibble: 6 x 15
##   VoteNumber SenNumber SenatorUpper Vote  Party GovCoalition State
##        <dbl> <chr>     <chr>        <chr> <chr> <lgl>        <chr>
## 1    2007001 PRS0002/… JOAO DURVAL  N     PDT   FALSE        BA   
## 2    2007001 PRS0002/… OSMAR DIAS   N     PDT   FALSE        PR   
## 3    2007001 PRS0002/… CRISTOVAM B… A     PDT   FALSE        DF   
## 4    2007002 PLS0229/… JOAO DURVAL  S     PDT   FALSE        BA   
## 5    2007002 PLS0229/… OSMAR DIAS   S     PDT   FALSE        PR   
## 6    2007002 PLS0229/… CRISTOVAM B… S     PDT   FALSE        DF   
## # … with 8 more variables: FP <dbl>, Origin <dbl>, Contentious <dbl>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <dbl>, Content <chr>,
## #   Round <dbl>

Em todas as comparações do R usamos operadores lógicos. São operações matemáticas em que o resultado é TRUE ou FALSE (tipo logic). Para melhor entendimento, selecionamos alguns operadores lógicos e seus significados:

  • == igual a: compara dois objetos e se forem iguais retorna TRUE, caso contrário, FALSE;
  • != diferente: compara dois objetos e se forem diferentes retorna TRUE, caso contrário, FALSE;
  • | ou (or): compara dois objetos, se um dos dois for TRUE, retorna TRUE, se os dois forem FALSE, retorna FALSE;
  • & e (and): compara dois objetos, se os dois forem TRUE, retorna TRUE, se um dos dois ou os dois forem FALSE, retorna FALSE;
  • >, >=, <, <= maior, maior ou igual, menor, menor ou igual: compara grandeza de dois números e retorna TRUE ou FALSE conforme a condição;

É possível fazer muita coisa com o R base, porém, vamos avançar com as manipulações, utilizando o pacote dplyr, por ser mais simples e, por isso, de mais rápido aprendizado.

4.4 Pacote dplyr

O forte do pacote dplyr é a sintaxe simples e concisa, o que facilita o aprendizado e torna o pacote um dos preferidos para as tarefas do dia a dia. Também conta como ponto forte sua otimização de performance para manipulação de dados. Ao carregar o pacote tidyverse, você já carregará automaticamente o pacote dplyr, mas você também pode carregá-lo individualmente:

Referências:
Site do pacote dplyr

4.4.1 Verbetes do dplyr e o operador %>%

O dplyr cobre praticamente todas as tarefas básicas da manipulação de dados: agregar, sumarizar, filtrar, ordenar, criar variáveis, joins, dentre outras.

As funções do dplyr reproduzem as principais tarefas da manipulação, de forma bastante intuitiva. Veja só:

  • select()
  • filter()
  • arrange()
  • mutate()
  • group_by()
  • summarise()

Esses são os principais verbetes, mas existem outros disponíveis, como por exemplo slice(), rename() e transmute(). Além de nomes de funções intuitivos, o dplyr também faz uso de um recurso disponível em boa parte dos pacotes do Hadley, o operador %>% (originário do pacote magrittr). Este operador encadeia as chamadas de funções de forma que você não vai precisar ficar chamando uma função dentro da outra ou ficar fazendo atribuições usando diversas linhas para concluir suas manipulações. Aliás, podemos dizer que esse operador %>%, literalmente, cria um fluxo sequencial bastante claro e legível para todas as atividades de manipulação.

Os 6 principais verbetes listados acima possuem funções derivadas com os sufixos _at, _if e _all, que podem ser muito úteis em arquivos com muitas colunas.

4.4.2 Select

O select() é a função mais simples de ser entendida. É usada para selecionar variáveis (colunas, campos, features…) do seu data frame.

## # A tibble: 6 x 2
##   SenatorUpper     Party
##   <chr>            <chr>
## 1 FLEXA RIBEIRO    PSDB 
## 2 ARTHUR VIRGILIO  PSDB 
## 3 FLAVIO ARNS      PT   
## 4 MARCELO CRIVELLA PRB  
## 5 JOAO DURVAL      PDT  
## 6 PAULO PAIM       PT

Você pode, também, fazer uma “seleção negativa”, ou seja, escolher as colunas que não quer:

## # A tibble: 6 x 13
##   VoteNumber SenNumber Vote  GovCoalition State    FP Origin
##        <dbl> <chr>     <chr> <lgl>        <chr> <dbl>  <dbl>
## 1    2007001 PRS0002/… S     FALSE        PA        2     11
## 2    2007001 PRS0002/… S     FALSE        AM        2     11
## 3    2007001 PRS0002/… N     TRUE         PR        2     11
## 4    2007001 PRS0002/… S     TRUE         RJ        2     11
## 5    2007001 PRS0002/… N     FALSE        BA        2     11
## 6    2007001 PRS0002/… S     TRUE         RS        2     11
## # … with 6 more variables: Contentious <dbl>, PercentYes <dbl>,
## #   IndGov <chr>, VoteType <dbl>, Content <chr>, Round <dbl>
  • select_at(): Selecionar um conjunto de variáveis, especificadas pelo nome ou pela posição numérica, podendo aplicar uma função ao nome delas:
## # A tibble: 6 x 3
##   SENNUMBER  SENATORUPPER     STATE
##   <chr>      <chr>            <chr>
## 1 PRS0002/07 FLEXA RIBEIRO    PA   
## 2 PRS0002/07 ARTHUR VIRGILIO  AM   
## 3 PRS0002/07 FLAVIO ARNS      PR   
## 4 PRS0002/07 MARCELO CRIVELLA RJ   
## 5 PRS0002/07 JOAO DURVAL      BA   
## 6 PRS0002/07 PAULO PAIM       RS
  • select_if(): Selecionar o conjunto de variáveis do dataframe que atende a um teste lógico:
## # A tibble: 6 x 7
##   VoteNumber    FP Origin Contentious PercentYes VoteType Round
##        <dbl> <dbl>  <dbl>       <dbl>      <dbl>    <dbl> <dbl>
## 1    2007001     2     11           0       85.5        1     1
## 2    2007001     2     11           0       85.5        1     1
## 3    2007001     2     11           0       85.5        1     1
## 4    2007001     2     11           0       85.5        1     1
## 5    2007001     2     11           0       85.5        1     1
## 6    2007001     2     11           0       85.5        1     1
  • select_all(): seleciona todas as colunas, opcionalmente aplicando uma função ao nome delas.
## # A tibble: 9,262 x 15
##    votenumber sennumber senatorupper vote  party govcoalition state
##         <dbl> <chr>     <chr>        <chr> <chr> <lgl>        <chr>
##  1    2007001 PRS0002/… FLEXA RIBEI… S     PSDB  FALSE        PA   
##  2    2007001 PRS0002/… ARTHUR VIRG… S     PSDB  FALSE        AM   
##  3    2007001 PRS0002/… FLAVIO ARNS  N     PT    TRUE         PR   
##  4    2007001 PRS0002/… MARCELO CRI… S     PRB   TRUE         RJ   
##  5    2007001 PRS0002/… JOAO DURVAL  N     PDT   FALSE        BA   
##  6    2007001 PRS0002/… PAULO PAIM   S     PT    TRUE         RS   
##  7    2007001 PRS0002/… EXPEDITO JU… S     PR    TRUE         RO   
##  8    2007001 PRS0002/… EFRAIM MORA… S     PFL/… FALSE        PB   
##  9    2007001 PRS0002/… ALOIZIO MER… S     PT    TRUE         SP   
## 10    2007001 PRS0002/… MAO SANTA    S     PMDB  TRUE         PI   
## # … with 9,252 more rows, and 8 more variables: fp <dbl>,
## #   origin <dbl>, contentious <dbl>, percentyes <dbl>, indgov <chr>,
## #   votetype <dbl>, content <chr>, round <dbl>

4.4.3 Filter

Além de escolher apenas alguns campos, você pode escolher apenas algumas linhas, utilizando alguma condição como filtragem. Para isso, basta utilizar a função filter.

## # A tibble: 2 x 3
##   SenatorUpper   Party State
##   <chr>          <chr> <chr>
## 1 PAULO DUQUE    PMDB  RJ   
## 2 REGIS FICHTNER PMDB  RJ
## # A tibble: 6 x 15
##   VoteNumber SenNumber SenatorUpper Vote  Party GovCoalition State
##        <dbl> <chr>     <chr>        <chr> <chr> <lgl>        <chr>
## 1    2007002 PLS0229/… JOAO DURVAL  S     PDT   FALSE        BA   
## 2    2007002 PLS0229/… EDUARDO SUP… S     PT    TRUE         SP   
## 3    2007002 PLS0229/… OSMAR DIAS   S     PDT   FALSE        PR   
## 4    2007002 PLS0229/… PRESIDENT L… S     PT    TRUE         <NA> 
## 5    2007002 PLS0229/… GERALDO MES… S     PMDB  TRUE         AC   
## 6    2007002 PLS0229/… FLEXA RIBEI… S     PSDB  FALSE        PA   
## # … with 8 more variables: FP <dbl>, Origin <dbl>, Contentious <dbl>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <dbl>, Content <chr>,
## #   Round <dbl>

4.4.4 Mutate

Para criar novos campos, podemos usar o mutate:

## Rows: 9,262
## Columns: 15
## $ VoteNumber   <dbl> 2007001, 2007001, 2007001, 2007001, 2007001, 20…
## $ SenNumber    <chr> "PRS0002/07", "PRS0002/07", "PRS0002/07", "PRS0…
## $ SenatorUpper <chr> "FLEXA RIBEIRO", "ARTHUR VIRGILIO", "FLAVIO ARN…
## $ Vote         <chr> "S", "S", "N", "S", "N", "S", "S", "S", "S", "S…
## $ Party        <chr> "PSDB", "PSDB", "PT", "PRB", "PDT", "PT", "PR",…
## $ GovCoalition <lgl> FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FA…
## $ State        <chr> "PA", "AM", "PR", "RJ", "BA", "RS", "RO", "PB",…
## $ FP           <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
## $ Origin       <dbl> 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,…
## $ Contentious  <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ PercentYes   <dbl> 85.45455, 85.45455, 85.45455, 85.45455, 85.4545…
## $ IndGov       <chr> "S", "S", "S", "S", "S", "S", "S", "S", "S", "S…
## $ VoteType     <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ Content      <chr> "Creates the Senate Commission of Science, Tech…
## $ Round        <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## # A tibble: 6 x 2
##   SenatorUpper     SenatorLower    
##   <chr>            <chr>           
## 1 FLEXA RIBEIRO    flexa ribeiro   
## 2 ARTHUR VIRGILIO  arthur virgilio 
## 3 FLAVIO ARNS      flavio arns     
## 4 MARCELO CRIVELLA marcelo crivella
## 5 JOAO DURVAL      joao durval     
## 6 PAULO PAIM       paulo paim

Caso o argumento da esquerda em mutate() seja um nome de coluna que já existe no dataframe, esta será sobrescrita:

## # A tibble: 6 x 15
##   VoteNumber SenNumber SenatorUpper Vote  Party GovCoalition State
##        <dbl> <chr>     <chr>        <chr> <chr> <lgl>        <chr>
## 1    2007001 PRS0002/… FLEXA RIBEI… S     psdb  FALSE        PA   
## 2    2007001 PRS0002/… ARTHUR VIRG… S     psdb  FALSE        AM   
## 3    2007001 PRS0002/… FLAVIO ARNS  N     pt    TRUE         PR   
## 4    2007001 PRS0002/… MARCELO CRI… S     prb   TRUE         RJ   
## 5    2007001 PRS0002/… JOAO DURVAL  N     pdt   FALSE        BA   
## 6    2007001 PRS0002/… PAULO PAIM   S     pt    TRUE         RS   
## # … with 8 more variables: FP <dbl>, Origin <dbl>, Contentious <dbl>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <dbl>, Content <chr>,
## #   Round <dbl>

Veja como é simples realizar a operação acima, de transformar uma coluna para minúsculo, para todas as colunas de texto do dataframe com o auxílio de mutate_if(), na qual o primeiro argumento é um teste lógico e o segundo é a função a ser aplicada para todas as colunas onde o teste lógico seja TRUE.

## # A tibble: 6 x 15
##   VoteNumber SenNumber SenatorUpper Vote  Party GovCoalition State
##        <dbl> <chr>     <chr>        <chr> <chr> <lgl>        <chr>
## 1    2007001 prs0002/… flexa ribei… s     psdb  FALSE        pa   
## 2    2007001 prs0002/… arthur virg… s     psdb  FALSE        am   
## 3    2007001 prs0002/… flavio arns  n     pt    TRUE         pr   
## 4    2007001 prs0002/… marcelo cri… s     prb   TRUE         rj   
## 5    2007001 prs0002/… joao durval  n     pdt   FALSE        ba   
## 6    2007001 prs0002/… paulo paim   s     pt    TRUE         rs   
## # … with 8 more variables: FP <dbl>, Origin <dbl>, Contentious <dbl>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <dbl>, Content <chr>,
## #   Round <dbl>

4.4.5 Group By e Summarise

O group_by() e o summarise() são operações que trabalham na agregação dos dados, ou seja, um dado mais detalhado passa a ser um dado mais agregado e agrupado, em consequência disso, menos detalhado. O agrupamento de dados geralmente é trabalhado em conjunção com sumarizações, que usam funções matemáticas do tipo soma, média, desvio padrão etc.

Enquanto o group_by() “separa” seus dados nos grupos que você selecionar, o summarise() faz operações de agregação de linhas limitadas a esse grupo.

Vale observar que operações de agrupamento e sumarização geralmente DIMINUEM a quantidade de linhas dos seus dados, pois está reduzindo o nível de detalhe. Ou seja, de alguma forma, você está “perdendo” detalhe para “ganhar” agregação.

Como exemplo, utilizaremos os dados disponíveis no pacote nycflights13:

## tibble [336,776 × 19] (S3: tbl_df/tbl/data.frame)
##  $ year          : int [1:336776] 2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
##  $ month         : int [1:336776] 1 1 1 1 1 1 1 1 1 1 ...
##  $ day           : int [1:336776] 1 1 1 1 1 1 1 1 1 1 ...
##  $ dep_time      : int [1:336776] 517 533 542 544 554 554 555 557 557 558 ...
##  $ sched_dep_time: int [1:336776] 515 529 540 545 600 558 600 600 600 600 ...
##  $ dep_delay     : num [1:336776] 2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
##  $ arr_time      : int [1:336776] 830 850 923 1004 812 740 913 709 838 753 ...
##  $ sched_arr_time: int [1:336776] 819 830 850 1022 837 728 854 723 846 745 ...
##  $ arr_delay     : num [1:336776] 11 20 33 -18 -25 12 19 -14 -8 8 ...
##  $ carrier       : chr [1:336776] "UA" "UA" "AA" "B6" ...
##  $ flight        : int [1:336776] 1545 1714 1141 725 461 1696 507 5708 79 301 ...
##  $ tailnum       : chr [1:336776] "N14228" "N24211" "N619AA" "N804JB" ...
##  $ origin        : chr [1:336776] "EWR" "LGA" "JFK" "JFK" ...
##  $ dest          : chr [1:336776] "IAH" "IAH" "MIA" "BQN" ...
##  $ air_time      : num [1:336776] 227 227 160 183 116 150 158 53 140 138 ...
##  $ distance      : num [1:336776] 1400 1416 1089 1576 762 ...
##  $ hour          : num [1:336776] 5 5 5 5 6 5 6 6 6 6 ...
##  $ minute        : num [1:336776] 15 29 40 45 0 58 0 0 0 0 ...
##  $ time_hour     : POSIXct[1:336776], format: "2013-01-01 05:00:00" ...

Gostaríamos de obter a média de atraso da chegada para cada mês. Para isso, primeiro agrupamos no nível necessário e depois sumarizamos.

## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 12 x 3
##    month arr_delay_media dep_delay_media
##    <int>           <dbl>           <dbl>
##  1     1           6.13            10.0 
##  2     2           5.61            10.8 
##  3     3           5.81            13.2 
##  4     4          11.2             13.9 
##  5     5           3.52            13.0 
##  6     6          16.5             20.8 
##  7     7          16.7             21.7 
##  8     8           6.04            12.6 
##  9     9          -4.02             6.72
## 10    10          -0.167            6.24
## 11    11           0.461            5.44
## 12    12          14.9             16.6

A função n() retorna a quantidade de observacões (linhas) por variável especificada em group_by():

## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 6 x 2
##   carrier n_voos
##   <chr>    <int>
## 1 9E       18460
## 2 AA       32729
## 3 AS         714
## 4 B6       54635
## 5 DL       48110
## 6 EV       54173

A função n_distinct() retorna a quantidade de casos únicos por grupo:

## `summarise()` regrouping output by 'origin' (override with `.groups` argument)
## # A tibble: 6 x 3
## # Groups:   origin [1]
##   origin month n_empresas
##   <chr>  <int>      <int>
## 1 EWR        1         10
## 2 EWR        2         10
## 3 EWR        3         10
## 4 EWR        4         11
## 5 EWR        5         11
## 6 EWR        6         12

4.4.6 Arrange

A função arrange() serve para organizar os dados em sua ordenação. Costuma ser uma das últimas operações, normalmente usada para organizar os dados e facilitar visualizações ou criação de relatórios. Utilizando o exemplo anterior, gostaríamos de ordenar os meses pelas menores médias de decolagem (para ordens decrescentes basta usar o sinal de menos -)

## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 12 x 3
##    month arr_delay_media dep_delay_media
##    <int>           <dbl>           <dbl>
##  1    11           0.461            5.44
##  2    10          -0.167            6.24
##  3     9          -4.02             6.72
##  4     1           6.13            10.0 
##  5     2           5.61            10.8 
##  6     8           6.04            12.6 
##  7     5           3.52            13.0 
##  8     3           5.81            13.2 
##  9     4          11.2             13.9 
## 10    12          14.9             16.6 
## 11     6          16.5             20.8 
## 12     7          16.7             21.7

4.5 Exercícios

4.5.1 Parte 1

Utilizando os dados em senado.csv, tente usar da manipulação de dados para responder às perguntas a seguir:

  1. Verifique a existência de registros NA em State. Caso existam, crie um novo data.frame senado2 sem esses registros e utilize-o para os próximos exercícios. Dica: is.na(State)

  2. Quais partidos foram parte da coalizão do governo? E quais não foram? Dica: filter()

  3. Quantos senadores tinha cada partido? Qual tinha mais? Quais tinham menos? Dica: group_by(), summarise() e n_distinct()

  4. Qual partido votou mais “sim”? E qual voltou menos “sim”? Dica: sum(Vote == 'S')

  5. Qual região do país teve mais votos “sim”? Primeiro será necessário criar uma coluna região para depois contabilizar o total de votos por região.

4.5.2 Parte 2

Baixe o dataset de anúncios do AirBNB no Rio de Janeiro a partir deste link:

http://data.insideairbnb.com/brazil/rj/rio-de-janeiro/2020-05-24/data/listings.csv.gz

  1. Leia o arquivo listings com a função read_csv() e salve no objeto df_anuncios
  2. Inspecione os dados: funções summary() e glimpse()
  3. A partir do output de glimpse(), explique as diferentes classes de objetos no R.
  4. Observe o problema nas variáveis de preço
  5. Retorne a url (scrape_url) do anúncio mais caro do airbnb
  6. Retorne o nome do host (host_name) que tem a maior quantidade de anúncios
  7. Retorne a quantidade de hosts por ano em que entrou no airbnb
  8. Selecione as colunas name e space e filtre as linhas que contem a palavra praia em space. Dica: Vc pode usar a função str_detect() dentro de filter() ou de mutate()
  9. Imóveis que mencionam a palavra praia são em média mais caros?
  10. Use mutate() para modificar o dataframe criando uma coluna booleana chamada esgotado informando se o imovel esta indisponivel para os proximos 30 dias (coluna availability_30)
  11. Quais os 5 bairros que possuem mais de 100 anúncios com a maior taxa de anúncios esgotados nos próximos 30 dias? Dica: crie duas colunas com summarise, uma usando n() e outra com mean(esgotado) e depois use filter(), arrange() e head()
  12. Retorne a quantidade de anúncios e reviews (number_of_reviews) por bairro, calcule uma taxa de quantidade de reviews por quantidade de anuncios. Os bairros que possuem mais anuncios são, proporcionalmente, os que tem mais reviews?
  13. Quais são os diferentes tipos de anúncio (quarto, apt, etc.) que existem? (Coluna room_type)
  14. A quantidade de quartos tem relação com o preço dos apartamentos inteiros?
  15. DESAFIO Suponha que você planeja uma viagem para o RJ com mais 1 pessoa de 5 diárias nos… proximos 30 dias. Você e seu grupo têm alguns critérios de escolha:
  • Vocês querem ficar em Ipanema, Copacabana ou Leblon.
  • Vocês preferem que o host esteja no mesmo bairro.
  • Não desejam pagar um depósito de segurança;
  • Querem um apartamento inteiro só para vocês que seja “instant bookable”
  • A diária já inclua duas pessoas

Filtre os anúncios que atendem aos critérios acima e crie uma coluna chamada preco_total_viagem, com a formula sendo: taxa de limpeza + preço da diaria x quantidade de diarias. Compare os resultados com os do site. Dica: Comece com o código abaixo, selecionando as colunas importantes