4 Manipulando os dados

Neste capítulo, usaremos estes pacotes:

library(nycflights13)
library(tidyverse)

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).

inteiro <- 928
outro.inteiro <- 5e2
decimal <- 182.93
caracter <- 'exportação'
logico <- TRUE
outro.logico <- FALSE

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

class(inteiro)
## [1] "numeric"
class(outro.inteiro)
## [1] "numeric"
class(decimal)
## [1] "numeric"
class(caracter)
## [1] "character"
class(logico)
## [1] "logical"
class(outro.logico)
## [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.

class(df)
## [1] "spec_tbl_df" "tbl_df"      "tbl"         "data.frame"
dim(df)
## [1] 144367     31

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.

# carregando um dataset pronto do R
data(iris)
# verificando a classe
class(iris)
## [1] "data.frame"
# transformando para tibble
iris_tbl <- as_tibble(iris)
# verificando a classe do novo objeto
class(iris_tbl)
## [1] "tbl_df"     "tbl"        "data.frame"

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

print(iris)

print(iris_tbl)

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:

#faça as seguintes atribuições
vetor.chr <- c('tipo1', 'tipo2', 'tipo3', 'tipo4')
vetor.num <- c(1, 2, 5, 8, 1001)
vetor.num.repetidos <- c(rep(2, 50)) #usando funcão para repetir números
vetor.num.sequencia <- c(seq(from=0, to=100, by=5)) #usando função para criar sequências
vetor.logical <- c(TRUE, TRUE, TRUE, FALSE, FALSE)
##veja o conteúdo das variáveis
vetor.chr
## [1] "tipo1" "tipo2" "tipo3" "tipo4"
vetor.num
## [1]    1    2    5    8 1001
vetor.num.repetidos
##  [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 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
vetor.num.sequencia
##  [1]   0   5  10  15  20  25  30  35  40  45  50  55  60  65  70  75  80  85  90  95 100
vetor.logical
## [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:

#cria-se diferentes vetores
nome <- c('João', 'José', 'Maria', 'Joana')
idade <- c(45, 12, 28, 31)
adulto <- c(TRUE, FALSE, TRUE, TRUE)
uf <- c('DF', 'SP', 'RJ', 'MG')
#cada vetor é uma combinação de elementos de um MESMO tipo de dados
#sendo assim, cada vetor pode ser uma coluna de um data.frame
clientes <- data.frame(nome, idade, adulto, uf)
clientes
##    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
str(clientes)
## '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.

class("2015")
## [1] "character"
as.numeric("2015")
## [1] 2015
class(55)
## [1] "numeric"
as.character(55)
## [1] "55"
class(3.14)
## [1] "numeric"
as.integer(3.14)
## [1] 3
as.numeric(TRUE)
## [1] 1
as.numeric(FALSE)
## [1] 0
as.logical(1)
## [1] TRUE
as.logical(0)
## [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:

7 + TRUE
2015 > "2016"
"2014" < 2017
# em alguns casos a coerção irá falhar ou dar resultado indesejado
6 > "100"
"6" < 5
1 + "1"

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:

data("airquality") # carrega uma base de dados pré-carregada no R
summary(airquality) # verificando ocorrência de NA
##      Ozone           Solar.R           Wind             Temp           Month            Day      
##  Min.   :  1.00   Min.   :  7.0   Min.   : 1.700   Min.   :56.00   Min.   :5.000   Min.   : 1.0  
##  1st Qu.: 18.00   1st Qu.:115.8   1st Qu.: 7.400   1st Qu.:72.00   1st Qu.:6.000   1st Qu.: 8.0  
##  Median : 31.50   Median :205.0   Median : 9.700   Median :79.00   Median :7.000   Median :16.0  
##  Mean   : 42.13   Mean   :185.9   Mean   : 9.958   Mean   :77.88   Mean   :6.993   Mean   :15.8  
##  3rd Qu.: 63.25   3rd Qu.:258.8   3rd Qu.:11.500   3rd Qu.:85.00   3rd Qu.:8.000   3rd Qu.:23.0  
##  Max.   :168.00   Max.   :334.0   Max.   :20.700   Max.   :97.00   Max.   :9.000   Max.   :31.0  
##  NA's   :37       NA's   :7
is.na(airquality$Ozone)
##   [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [21] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE
##  [41] FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
##  [61]  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
##  [81] FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [101] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE
## [121] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [141] FALSE FALSE FALSE 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:

if( variavel >= 500 ) {
  #executa uma tarefa se operação resultar TRUE
} else {
  #executa outra tarefa se operação resultar FALSE
}

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

ifelse(variavel >= 500, 'executa essa tarefa se TRUE', 'executa outra se FALSE')

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:

a <- 9823

if(a >= 10000) {
  b <- 'VALOR ALTO'
} else if(a < 10000 & a >= 1000) {
  b <- 'VALOR MEDIO' 
} else if(a < 1000) {
  b <- 'VALOR BAIXO'
}

b
## [1] "VALOR MEDIO"

Ou ainda:

a <- 839
c <- ifelse(a >= 10000, 'VALOR ALTO', ifelse(a < 10000 & a >= 1000, 'VALOR MEDIO', 'VALOR BAIXO'))
c
## [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:

for(i in c(1, 2, 3, 4, 5)) {
  print(i^2)
}
## [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().

lista.de.arquivos <- list.files('dados/dados_loop') #lista todos os arquivos de uma pasta
is.vector(lista.de.arquivos)
## [1] TRUE
for(i in lista.de.arquivos) {
  print(paste('Leia o arquivo:', i))
  #exemplo: read_delim(i, delim = "|")
}
## [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.

for(i in 1:1000){
  if((i %% 29 == 0) & (i %% 3 == 0)){
    print(i)
  }
}
## [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.

i <- 1
while(i <= 5){
  print(i)
  i <- i + 1
}
## [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 é:

automatico <- list.files('dados/automatico/')
length(automatico) == 0

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.

while (length(automatico) == 0) {
  automatico <- list.files('dados/automatico/')
  if(length(automatico) > 0) {
    print('O arquivo chegou!')
    print('Inicia a leitura dos dados')
    print('Faz a manipulação')
    print('Envia email informando conclusão dos cálculos')
  } else {
    print('aguardando arquivo...')
    Sys.sleep(5)
  }
}

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:

sua_funcao <- function(parametro1, parametro2){
  
  # sequência de tarefas
  
  return(valores_retornados)
}

# chamada da função
sua_funcao

Agora tente entender a seguinte função:

montanha_russa <- function(palavra) {
  retorno <- NULL
  for(i in 1:nchar(palavra)) {
    if(i %% 2 == 0) {
      retorno <- paste0(retorno, tolower(substr(palavra, i, i)))
    } else {
      retorno <- paste0(retorno, toupper(substr(palavra, i, i)))
    }
  }
  return(retorno)
}

montanha_russa('teste de função: letras maiúsculas e minúsculas')
## [1] "TeStE De fUnÇãO: lEtRaS MaIúScUlAs e mInÚsCuLaS"
montanha_russa('CONSEGUIU ENTENDER?')
## [1] "CoNsEgUiU EnTeNdEr?"
montanha_russa('É Fácil Usar Funções!')
## [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.

head(airquality$Ozone)
## [1] 41 36 12 18 NA 28
tail(airquality$Ozone)
## [1] 14 30 NA 14 18 20
class(airquality$Ozone) # Informa o tipo da coluna
## [1] "integer"
is.vector(airquality$Ozone) # Apenas para verificar que cada coluna de um data.frame é um vector
## [1] TRUE
unique(df$sgPartido) # Função que retorna apenas os valores únicos, sem repetição, de um vetor
##  [1] NA              "PSDB"          "DEM"           "PP"            "PDT"           "PODE"          "PSB"          
##  [8] "PT"            "REPUBLICANOS"  "CIDADANIA"     "PSOL"          "MDB"           "PSD"           "PCdoB"        
## [15] "PL"            "PTB"           "PROS"          "PSC"           "PSL"           "SOLIDARIEDADE" "NOVO"         
## [22] "AVANTE"        "PATRIOTA"      "PV"            "REDE"

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:

vetor <- c(seq(from=0, to=100, by=15)) #vetor de 0 a 100, de 15 em 15.
vetor #lista todos os elementos
## [1]  0 15 30 45 60 75 90
vetor[1] #mostra apenas o elemento na posição 1
## [1] 0
vetor[2] #apenas o elemento na posição 2
## [1] 15
vetor[7] #apenas o elemento na posição 7
## [1] 90
vetor[8] #não existe nada na posição 8...
## [1] NA

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

vetor[c(2,7)] #selecionando mais de um elemento no 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.

df[10, ] #linha 10, todas as colunas
## # A tibble: 1 × 31
##   txNomeParlamentar         cpf ideCadastro nuCarteiraParlamentar nuLegislatura sgUF  sgPartido codLegislatura numSubCota txtDescricao
##   <chr>                   <dbl>       <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>       
## 1 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA …
## # … with 21 more variables: numEspecificacaoSubCota <dbl>, txtDescricaoEspecificacao <chr>, txtFornecedor <chr>,
## #   txtCNPJCPF <chr>, txtNumero <chr>, indTipoDocumento <dbl>, datEmissao <dttm>, vlrDocumento <chr>, vlrGlosa <chr>,
## #   vlrLiquido <chr>, numMes <dbl>, numAno <dbl>, numParcela <dbl>, txtPassageiro <chr>, txtTrecho <chr>, numLote <dbl>,
## #   numRessarcimento <dbl>, vlrRestituicao <lgl>, nuDeputadoId <dbl>, ideDocumento <dbl>, urlDocumento <chr>
df[72, 3] #linha 72, coluna 3
## # A tibble: 1 × 1
##   ideCadastro
##         <dbl>
## 1          NA
df[c(100, 200), c(2,3,4)] # selecionando mais de uma linha e coluna em um data.frame
## # A tibble: 2 × 3
##     cpf ideCadastro nuCarteiraParlamentar
##   <dbl>       <dbl>                 <dbl>
## 1    NA          NA                    NA
## 2    NA          NA                    NA
df[c(10:20), ]
## # A tibble: 11 × 31
##    txNomeParlamentar         cpf ideCadastro nuCarteiraParlamentar nuLegislatura sgUF  sgPartido codLegislatura numSubCota txtDescricao
##    <chr>                   <dbl>       <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>       
##  1 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA …
##  2 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA …
##  3 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         13 FORNECIMENT…
##  4 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         13 FORNECIMENT…
##  5 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         13 FORNECIMENT…
##  6 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         13 FORNECIMENT…
##  7 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         13 FORNECIMENT…
##  8 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         13 FORNECIMENT…
##  9 LIDERANÇA DO NOVO          NA          NA                    NA          2019 <NA>  <NA>                  56          1 MANUTENÇÃO …
## 10 LIDERANÇA DO NOVO          NA          NA                    NA          2019 <NA>  <NA>                  56          1 MANUTENÇÃO …
## 11 LIDERANÇA DO NOVO          NA          NA                    NA          2019 <NA>  <NA>                  56          1 MANUTENÇÃO …
## # … with 21 more variables: numEspecificacaoSubCota <dbl>, txtDescricaoEspecificacao <chr>, txtFornecedor <chr>,
## #   txtCNPJCPF <chr>, txtNumero <chr>, indTipoDocumento <dbl>, datEmissao <dttm>, vlrDocumento <chr>, vlrGlosa <chr>,
## #   vlrLiquido <chr>, numMes <dbl>, numAno <dbl>, numParcela <dbl>, txtPassageiro <chr>, txtTrecho <chr>, numLote <dbl>,
## #   numRessarcimento <dbl>, vlrRestituicao <lgl>, nuDeputadoId <dbl>, ideDocumento <dbl>, urlDocumento <chr>

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:

df[1:10, c('sgPartido', 'cpf')]
## # A tibble: 10 × 2
##    sgPartido   cpf
##    <chr>     <dbl>
##  1 <NA>         NA
##  2 <NA>         NA
##  3 <NA>         NA
##  4 <NA>         NA
##  5 <NA>         NA
##  6 <NA>         NA
##  7 <NA>         NA
##  8 <NA>         NA
##  9 <NA>         NA
## 10 <NA>         NA

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

head(df[df$sgPartido == 'PT', ])
## # A tibble: 6 × 31
##   txNomeParlamentar   cpf ideCadastro nuCarteiraParlamentar nuLegislatura sgUF  sgPartido codLegislatura numSubCota txtDescricao
##   <chr>             <dbl>       <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>       
## 1 <NA>                 NA          NA                    NA            NA <NA>  <NA>                  NA         NA <NA>        
## 2 <NA>                 NA          NA                    NA            NA <NA>  <NA>                  NA         NA <NA>        
## 3 <NA>                 NA          NA                    NA            NA <NA>  <NA>                  NA         NA <NA>        
## 4 <NA>                 NA          NA                    NA            NA <NA>  <NA>                  NA         NA <NA>        
## 5 <NA>                 NA          NA                    NA            NA <NA>  <NA>                  NA         NA <NA>        
## 6 <NA>                 NA          NA                    NA            NA <NA>  <NA>                  NA         NA <NA>        
## # … with 21 more variables: numEspecificacaoSubCota <dbl>, txtDescricaoEspecificacao <chr>, txtFornecedor <chr>,
## #   txtCNPJCPF <chr>, txtNumero <chr>, indTipoDocumento <dbl>, datEmissao <dttm>, vlrDocumento <chr>, vlrGlosa <chr>,
## #   vlrLiquido <chr>, numMes <dbl>, numAno <dbl>, numParcela <dbl>, txtPassageiro <chr>, txtTrecho <chr>, numLote <dbl>,
## #   numRessarcimento <dbl>, vlrRestituicao <lgl>, nuDeputadoId <dbl>, ideDocumento <dbl>, urlDocumento <chr>

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:

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

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.

df %>% select(urlDocumento, txtDescricao, vlrRestituicao)
## # A tibble: 144,367 × 3
##    urlDocumento                                                                            txtDescricao             vlrRestituicao
##    <chr>                                                                                   <chr>                    <lgl>         
##  1 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7050172 CONSULTORIAS, PESQUISAS… NA            
##  2 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7058437 CONSULTORIAS, PESQUISAS… NA            
##  3 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7078508 CONSULTORIAS, PESQUISAS… NA            
##  4 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7123566 CONSULTORIAS, PESQUISAS… NA            
##  5 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7067718 DIVULGAÇÃO DA ATIVIDADE… NA            
##  6 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7088718 DIVULGAÇÃO DA ATIVIDADE… NA            
##  7 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7101137 DIVULGAÇÃO DA ATIVIDADE… NA            
##  8 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7140186 DIVULGAÇÃO DA ATIVIDADE… NA            
##  9 <NA>                                                                                    ASSINATURA DE PUBLICAÇÕ… NA            
## 10 http://camara.leg.br/cota-parlamentar/nota-fiscal-eletronica?ideDocumentoFiscal=7042111 ASSINATURA DE PUBLICAÇÕ… NA            
## # … with 144,357 more rows

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

df %>% select(-txNomeParlamentar)
## # A tibble: 144,367 × 30
##      cpf ideCadastro nuCarteiraParlamentar nuLegislatura sgUF  sgPartido codLegislatura numSubCota txtDescricao   numEspecificaca…
##    <dbl>       <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>                     <dbl>
##  1    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS,…                0
##  2    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS,…                0
##  3    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS,…                0
##  4    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS,…                0
##  5    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA…                0
##  6    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA…                0
##  7    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA…                0
##  8    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA…                0
##  9    NA          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA DE…                0
## 10    NA          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA DE…                0
## # … with 144,357 more rows, and 20 more variables: txtDescricaoEspecificacao <chr>, txtFornecedor <chr>, txtCNPJCPF <chr>,
## #   txtNumero <chr>, indTipoDocumento <dbl>, datEmissao <dttm>, vlrDocumento <chr>, vlrGlosa <chr>, vlrLiquido <chr>,
## #   numMes <dbl>, numAno <dbl>, numParcela <dbl>, txtPassageiro <chr>, txtTrecho <chr>, numLote <dbl>, numRessarcimento <dbl>,
## #   vlrRestituicao <lgl>, nuDeputadoId <dbl>, ideDocumento <dbl>, urlDocumento <chr>
df %>% select(-c(txNomeParlamentar, cpf))
## # A tibble: 144,367 × 29
##    ideCadastro nuCarteiraParlamentar nuLegislatura sgUF  sgPartido codLegislatura numSubCota txtDescricao        numEspecificacao…
##          <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>                           <dbl>
##  1          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS, PESQ…                 0
##  2          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS, PESQ…                 0
##  3          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS, PESQ…                 0
##  4          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIAS, PESQ…                 0
##  5          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA ATIV…                 0
##  6          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA ATIV…                 0
##  7          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA ATIV…                 0
##  8          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO DA ATIV…                 0
##  9          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA DE PUBL…                 0
## 10          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA DE PUBL…                 0
## # … with 144,357 more rows, and 20 more variables: txtDescricaoEspecificacao <chr>, txtFornecedor <chr>, txtCNPJCPF <chr>,
## #   txtNumero <chr>, indTipoDocumento <dbl>, datEmissao <dttm>, vlrDocumento <chr>, vlrGlosa <chr>, vlrLiquido <chr>,
## #   numMes <dbl>, numAno <dbl>, numParcela <dbl>, txtPassageiro <chr>, txtTrecho <chr>, numLote <dbl>, numRessarcimento <dbl>,
## #   vlrRestituicao <lgl>, nuDeputadoId <dbl>, ideDocumento <dbl>, urlDocumento <chr>
  • 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:
# manter apenas as colunas que começam com S e transformar o nome para maiusculo
#df %>% select_at(vars(starts_with("vlr")))
# é equivalente a:
df %>% select(starts_with("vlr"))
## # A tibble: 144,367 × 4
##    vlrDocumento vlrGlosa vlrLiquido vlrRestituicao
##    <chr>        <chr>    <chr>      <lgl>         
##  1 1527.28      0        1527.28    NA            
##  2 1527.28      0        1527.28    NA            
##  3 1527.28      0        1527.28    NA            
##  4 1527.28      0        1527.28    NA            
##  5 1527.28      0        1527.28    NA            
##  6 1527.28      0        1527.28    NA            
##  7 1527.28      0        1527.28    NA            
##  8 1527.28      0        1527.28    NA            
##  9 613.61       0        613.61     NA            
## 10 1527.28      0        1527.28    NA            
## # … with 144,357 more rows
  • select_if(): Selecionar o conjunto de variáveis do dataframe que atende a um teste lógico:
# selecionar apenas as colunas numericas do dataframe
df %>% select_if(is.numeric) %>% head()
## # A tibble: 6 × 15
##     cpf ideCadastro nuCarteiraParlamen… nuLegislatura codLegislatura numSubCota numEspecificacaoSu… indTipoDocumento numMes numAno
##   <dbl>       <dbl>               <dbl>         <dbl>          <dbl>      <dbl>               <dbl>            <dbl>  <dbl>  <dbl>
## 1    NA          NA                  NA          2019             56          4                   0                4      4   2020
## 2    NA          NA                  NA          2019             56          4                   0                4      5   2020
## 3    NA          NA                  NA          2019             56          4                   0                4      7   2020
## 4    NA          NA                  NA          2019             56          4                   0                4     11   2020
## 5    NA          NA                  NA          2019             56          5                   0                4      6   2020
## 6    NA          NA                  NA          2019             56          5                   0                4      8   2020
## # … with 5 more variables: numParcela <dbl>, numLote <dbl>, numRessarcimento <dbl>, nuDeputadoId <dbl>, ideDocumento <dbl>
  • select_all(): seleciona todas as colunas, opcionalmente aplicando uma função ao nome delas.
df %>% select_all(tolower)
## # A tibble: 144,367 × 31
##    txnomeparlamentar         cpf idecadastro nucarteiraparlamentar nulegislatura sguf  sgpartido codlegislatura numsubcota txtdescricao
##    <chr>                   <dbl>       <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>       
##  1 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIA…
##  2 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIA…
##  3 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIA…
##  4 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          4 CONSULTORIA…
##  5 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO …
##  6 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO …
##  7 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO …
##  8 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO …
##  9 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA …
## 10 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         12 ASSINATURA …
## # … with 144,357 more rows, and 21 more variables: numespecificacaosubcota <dbl>, txtdescricaoespecificacao <chr>,
## #   txtfornecedor <chr>, txtcnpjcpf <chr>, txtnumero <chr>, indtipodocumento <dbl>, datemissao <dttm>, vlrdocumento <chr>,
## #   vlrglosa <chr>, vlrliquido <chr>, nummes <dbl>, numano <dbl>, numparcela <dbl>, txtpassageiro <chr>, txttrecho <chr>,
## #   numlote <dbl>, numressarcimento <dbl>, vlrrestituicao <lgl>, nudeputadoid <dbl>, idedocumento <dbl>, urldocumento <chr>

4.4.3 Mutate

Para criar novos campos ou transformar campos já existentes, podemos usar a mutate(). Ela é útil para, por exemplo, converter classes de colunas:

# criar coluna com o nome do senador em minusculo
df <- df %>% 
  mutate(vlrDocumento = as.numeric(vlrDocumento),
         vlrLiquido = as.numeric(vlrLiquido),
         vlrRestituicao = as.numeric(vlrRestituicao))

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

df %>% 
  select(txNomeParlamentar, vlrLiquido) %>% 
  mutate(txNomeParlamentar = tolower(txNomeParlamentar)) %>% 
  head()
## # A tibble: 6 × 2
##   txNomeParlamentar       vlrLiquido
##   <chr>                        <dbl>
## 1 liderança do democratas      1527.
## 2 liderança do democratas      1527.
## 3 liderança do democratas      1527.
## 4 liderança do democratas      1527.
## 5 liderança do democratas      1527.
## 6 liderança do democratas      1527.

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.

df %>% 
  # o primeiro argumento de mutate_if 
  mutate_if(is.character, tolower) %>% 
  head()
## # A tibble: 6 × 31
##   txNomeParlamentar         cpf ideCadastro nuCarteiraParlamentar nuLegislatura sgUF  sgPartido codLegislatura numSubCota txtDescricao
##   <chr>                   <dbl>       <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>       
## 1 liderança do democratas    NA          NA                    NA          2019 <NA>  <NA>                  56          4 consultoria…
## 2 liderança do democratas    NA          NA                    NA          2019 <NA>  <NA>                  56          4 consultoria…
## 3 liderança do democratas    NA          NA                    NA          2019 <NA>  <NA>                  56          4 consultoria…
## 4 liderança do democratas    NA          NA                    NA          2019 <NA>  <NA>                  56          4 consultoria…
## 5 liderança do democratas    NA          NA                    NA          2019 <NA>  <NA>                  56          5 divulgação …
## 6 liderança do democratas    NA          NA                    NA          2019 <NA>  <NA>                  56          5 divulgação …
## # … with 21 more variables: numEspecificacaoSubCota <dbl>, txtDescricaoEspecificacao <chr>, txtFornecedor <chr>,
## #   txtCNPJCPF <chr>, txtNumero <chr>, indTipoDocumento <dbl>, datEmissao <dttm>, vlrDocumento <dbl>, vlrGlosa <chr>,
## #   vlrLiquido <dbl>, numMes <dbl>, numAno <dbl>, numParcela <dbl>, txtPassageiro <chr>, txtTrecho <chr>, numLote <dbl>,
## #   numRessarcimento <dbl>, vlrRestituicao <dbl>, nuDeputadoId <dbl>, ideDocumento <dbl>, urlDocumento <chr>

4.4.4 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.

df %>% 
  select(cpf, txNomeParlamentar, txtDescricao, vlrDocumento) %>% 
  filter(vlrDocumento >= 100000)
## # A tibble: 6 × 4
##           cpf txNomeParlamentar                 txtDescricao                         vlrDocumento
##         <dbl> <chr>                             <chr>                                       <dbl>
## 1  9006826200 Edmilson Rodrigues                DIVULGAÇÃO DA ATIVIDADE PARLAMENTAR.       151260
## 2 43196926168 Professora Dorinha Seabra Rezende DIVULGAÇÃO DA ATIVIDADE PARLAMENTAR.       105600
## 3 60433086653 Emidinho Madeira                  DIVULGAÇÃO DA ATIVIDADE PARLAMENTAR.       104520
## 4 41864883634 Subtenente Gonzaga                SERVIÇOS POSTAIS                           100587
## 5 49156470991 Christiane de Souza Yared         DIVULGAÇÃO DA ATIVIDADE PARLAMENTAR.       100000
## 6 47102500653 Rogério Correia                   DIVULGAÇÃO DA ATIVIDADE PARLAMENTAR.       144640
df %>% filter(numMes == 9) %>% head()
## # A tibble: 6 × 31
##   txNomeParlamentar         cpf ideCadastro nuCarteiraParlamentar nuLegislatura sgUF  sgPartido codLegislatura numSubCota txtDescricao
##   <chr>                   <dbl>       <dbl>                 <dbl>         <dbl> <chr> <chr>              <dbl>      <dbl> <chr>       
## 1 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56          5 DIVULGAÇÃO …
## 2 LIDERANÇA DO DEMOCRATAS    NA          NA                    NA          2019 <NA>  <NA>                  56         13 FORNECIMENT…
## 3 LIDERANÇA DO NOVO          NA          NA                    NA          2019 <NA>  <NA>                  56          1 MANUTENÇÃO …
## 4 LIDERANÇA DO NOVO          NA          NA                    NA          2019 <NA>  <NA>                  56          1 MANUTENÇÃO …
## 5 LIDERANÇA DO NOVO          NA          NA                    NA          2019 <NA>  <NA>                  56          1 MANUTENÇÃO …
## 6 LIDERANÇA DO NOVO          NA          NA                    NA          2019 <NA>  <NA>                  56          1 MANUTENÇÃO …
## # … with 21 more variables: numEspecificacaoSubCota <dbl>, txtDescricaoEspecificacao <chr>, txtFornecedor <chr>,
## #   txtCNPJCPF <chr>, txtNumero <chr>, indTipoDocumento <dbl>, datEmissao <dttm>, vlrDocumento <dbl>, vlrGlosa <chr>,
## #   vlrLiquido <dbl>, numMes <dbl>, numAno <dbl>, numParcela <dbl>, txtPassageiro <chr>, txtTrecho <chr>, numLote <dbl>,
## #   numRessarcimento <dbl>, vlrRestituicao <dbl>, nuDeputadoId <dbl>, ideDocumento <dbl>, urlDocumento <chr>

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:

install.packages("nycflights13")
library(nycflights13)
data("flights")
str(flights)
## 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" "2013-01-01 05:00:00" "2013-01-01 05:00:00" "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.

atraso_por_mes <- flights %>%
  group_by(month) %>%
  summarise(arr_delay_media = mean(arr_delay, na.rm=TRUE), 
            dep_delay_media = mean(dep_delay, na.rm=TRUE))

atraso_por_mes
## # A tibble: 12 × 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():

# Quantidade de voos por hora do dia
flights %>% 
  group_by(carrier) %>% 
  summarise(n_voos = n()) %>% 
  head()
## # A tibble: 6 × 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:

# quantas companhias aéreas diferentes operam por aeroporto por mês nos EUA?
flights %>% 
  group_by(origin, month) %>% 
  summarise(n_empresas = n_distinct(carrier)) %>% 
  head(6)
## `summarise()` has grouped output by 'origin'. You can override using the `.groups` argument.
## # A tibble: 6 × 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 -)

media <- flights %>%
  group_by(month) %>%
  summarise(arr_delay_media = mean(arr_delay, na.rm=TRUE), 
            dep_delay_media = mean(dep_delay, na.rm=TRUE)) %>% 
  arrange(dep_delay_media)

media
## # A tibble: 12 × 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

  1. Quais foram os deputados com mais despesas na cota parlamentar (considerando a coluna vlrDocumento)?

  2. Quais foram as 5 empresas mais contratadas (considerando a coluna textCNPJCPF)?

  3. Qual foi o gasto médio dos deputados, por UF, com a cota parlamentar (considerando a coluna vlrDocumento)?

  4. Quais categorias de gastos registraram mais despesas nas lideranças (considerando a coluna txtDescricao)?

  5. Quantas linhas da coluna com o PDF da nota fiscal estão com NA (considerando a coluna urlDocumento)?

  6. Qual foi o mês com menos gastos (considerando a coluna numMes)?

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/2021-07-17/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

df_anuncios %>% 
  select(listing_url, neighbourhood, host_neighbourhood,
         availability_30, minimum_nights, 
         security_deposit, instant_bookable,
         guests_included, price, room_type, number_of_reviews,
         cleaning_fee, review_scores_rating)