4 Manipulando os dados

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(senado)
## [1] "tbl_df"     "tbl"        "data.frame"
dim(senado)
## [1] 9262   15

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
## [36] 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
## [18]  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  : Factor w/ 4 levels "Joana","João",..: 2 3 4 1
##  $ idade : num  45 12 28 31
##  $ adulto: logi  TRUE FALSE TRUE TRUE
##  $ uf    : Factor w/ 4 levels "DF","MG","RJ",..: 1 4 3 2

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      
##  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  
## 
is.na(airquality$Ozone)
##   [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.1.4 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 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.3 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.3.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.3.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.3.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.4 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.4.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(senado$Party) # Função que retorna apenas os valores únicos, sem repetição, de um vetor
##  [1] "PSDB"    "PT"      "PRB"     "PDT"     "PR"      "PFL/DEM" "PMDB"   
##  [8] "PP"      "PSB"     "PTB"     "PCdoB"   "PSOL"    "S/PART"  "PSC"    
## [15] "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:

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.

senado[10, ] #linha 10, todas as colunas
## # A tibble: 1 x 15
##   VoteNumber  SenNumber SenatorUpper  Vote Party GovCoalition State    FP
##        <int>      <chr>        <chr> <chr> <chr>        <lgl> <chr> <int>
## 1    2007001 PRS0002/07    MAO SANTA     S  PMDB         TRUE    PI     2
## # ... with 7 more variables: Origin <int>, Contentious <int>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <int>, Content <chr>,
## #   Round <int>
senado[72, 3] #linha 72, coluna 3
## # A tibble: 1 x 1
##         SenatorUpper
##                <chr>
## 1 WELLINGTON SALGADO
senado[c(100, 200), c(2,3,4)] # selecionando mais de uma linha e coluna em um data.frame
## # A tibble: 2 x 3
##    SenNumber       SenatorUpper  Vote
##        <chr>              <chr> <chr>
## 1 PLS0229/06     MARISA SERRANO     S
## 2 PLS0134/06 EPITACIO CAFETEIRA     S
senado[c(10:20), ]
## # A tibble: 11 x 15
##    VoteNumber  SenNumber          SenatorUpper  Vote   Party GovCoalition
##         <int>      <chr>                 <chr> <chr>   <chr>        <lgl>
##  1    2007001 PRS0002/07             MAO SANTA     S    PMDB         TRUE
##  2    2007001 PRS0002/07           MAGNO MALTA     S      PR         TRUE
##  3    2007001 PRS0002/07       EDUARDO SUPLICY     S      PT         TRUE
##  4    2007001 PRS0002/07         GILVAM BORGES     S    PMDB         TRUE
##  5    2007001 PRS0002/07      RAIMUNDO COLOMBO     S PFL/DEM        FALSE
##  6    2007001 PRS0002/07         CICERO LUCENA     S    PSDB        FALSE
##  7    2007001 PRS0002/07   FRANCISCO DORNELLES     S      PP         TRUE
##  8    2007001 PRS0002/07            OSMAR DIAS     N     PDT        FALSE
##  9    2007001 PRS0002/07    ALFREDO NASCIMENTO     S      PR         TRUE
## 10    2007001 PRS0002/07          VALDIR RAUPP     S    PMDB         TRUE
## 11    2007001 PRS0002/07 GARIBALDI ALVES FILHO     S    PMDB         TRUE
## # ... with 9 more variables: State <chr>, FP <int>, Origin <int>,
## #   Contentious <int>, PercentYes <dbl>, IndGov <chr>, VoteType <int>,
## #   Content <chr>, Round <int>

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:

senado[1:10, c('SenatorUpper', 'Party', 'State')]
## # 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:

head(senado[senado$Party == 'PDT', ])
## # A tibble: 6 x 15
##   VoteNumber  SenNumber      SenatorUpper  Vote Party GovCoalition State
##        <int>      <chr>             <chr> <chr> <chr>        <lgl> <chr>
## 1    2007001 PRS0002/07       JOAO DURVAL     N   PDT        FALSE    BA
## 2    2007001 PRS0002/07        OSMAR DIAS     N   PDT        FALSE    PR
## 3    2007001 PRS0002/07 CRISTOVAM BUARQUE     A   PDT        FALSE    DF
## 4    2007002 PLS0229/06       JOAO DURVAL     S   PDT        FALSE    BA
## 5    2007002 PLS0229/06        OSMAR DIAS     S   PDT        FALSE    PR
## 6    2007002 PLS0229/06 CRISTOVAM BUARQUE     S   PDT        FALSE    DF
## # ... with 8 more variables: FP <int>, Origin <int>, Contentious <int>,
## #   PercentYes <dbl>, IndGov <chr>, VoteType <int>, Content <chr>,
## #   Round <int>

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.5 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)
?dplyr

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

4.5.2 Select

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

senadores.partido <- senado %>% select(SenatorUpper, Party)
head(senadores.partido)
## # 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:

senadores.partido <- senado %>% select(-SenatorUpper, -Party)
head(senadores.partido)
## # A tibble: 6 x 13
##   VoteNumber  SenNumber  Vote GovCoalition State    FP Origin Contentious
##        <int>      <chr> <chr>        <lgl> <chr> <int>  <int>       <int>
## 1    2007001 PRS0002/07     S        FALSE    PA     2     11           0
## 2    2007001 PRS0002/07     S        FALSE    AM     2     11           0
## 3    2007001 PRS0002/07     N         TRUE    PR     2     11           0
## 4    2007001 PRS0002/07     S         TRUE    RJ     2     11           0
## 5    2007001 PRS0002/07     N        FALSE    BA     2     11           0
## 6    2007001 PRS0002/07     S         TRUE    RS     2     11           0
## # ... with 5 more variables: PercentYes <dbl>, IndGov <chr>,
## #   VoteType <int>, Content <chr>, Round <int>

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

senadores.pdt.df <- senado %>% 
  select(SenatorUpper, Party, State) %>% 
  filter(State == 'RJ', Party == 'PMDB') %>% 
  distinct() #semelhante ao unique(), traz registros únicos sem repetição

head(senadores.pdt.df)
## # A tibble: 2 x 3
##     SenatorUpper Party State
##            <chr> <chr> <chr>
## 1    PAULO DUQUE  PMDB    RJ
## 2 REGIS FICHTNER  PMDB    RJ

4.5.4 Mutate

Para criar novos campos, podemos usar o mutate:

senadores.pdt.df <- senado %>% 
  select(SenatorUpper, Party, State) %>% 
  filter(Party == 'PMDB') %>% 
  distinct() #semelhante ao unique(), traz registros únicos sem repetição

head(senadores.pdt.df)
## # A tibble: 6 x 3
##            SenatorUpper Party State
##                   <chr> <chr> <chr>
## 1             MAO SANTA  PMDB    PI
## 2         GILVAM BORGES  PMDB    AP
## 3          VALDIR RAUPP  PMDB    RO
## 4 GARIBALDI ALVES FILHO  PMDB    RN
## 5         GERSON CAMATA  PMDB    ES
## 6    JARBAS VASCONCELOS  PMDB    PE

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

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

media
## # A tibble: 12 x 3
##    month arr_delay_media dep_delay_media
##    <int>           <dbl>           <dbl>
##  1     1       6.1299720       10.036665
##  2     2       5.6130194       10.816843
##  3     3       5.8075765       13.227076
##  4     4      11.1760630       13.938038
##  5     5       3.5215088       12.986859
##  6     6      16.4813296       20.846332
##  7     7      16.7113067       21.727787
##  8     8       6.0406524       12.611040
##  9     9      -4.0183636        6.722476
## 10    10      -0.1670627        6.243988
## 11    11       0.4613474        5.435362
## 12    12      14.8703553       16.576688

4.5.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 x 3
##    month arr_delay_media dep_delay_media
##    <int>           <dbl>           <dbl>
##  1    11       0.4613474        5.435362
##  2    10      -0.1670627        6.243988
##  3     9      -4.0183636        6.722476
##  4     1       6.1299720       10.036665
##  5     2       5.6130194       10.816843
##  6     8       6.0406524       12.611040
##  7     5       3.5215088       12.986859
##  8     3       5.8075765       13.227076
##  9     4      11.1760630       13.938038
## 10    12      14.8703553       16.576688
## 11     6      16.4813296       20.846332
## 12     7      16.7113067       21.727787

4.5.7 O operador %>%

Observe novamente as manipulações feitas acima. Repare que apenas acrescentamos verbetes e encadeamos a manipulação com o uso de %>%.

A primeira parte serie.orig %>% é a passagem onde você informa o data.frame que utilizará na sequência de manipulação. A partir daí, as chamadas seguintes select() %>%, filter() %>%, mutate() %>% etc, são os encadeamentos de manipulação que você pode fazer sem precisar atribuir resultados ou criar novos objetos.

Em outras palavras, usando o operador %>%, você estará informando que um resultado da operação anterior será a entrada para a nova operação. Esse encadeamento facilita muito as coisas, tornando a manipulação mais legível e intuitiva.

4.6 Exercícios

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.

Dica: mutate(Regiao = ifelse(State %in% c(“AM”, “AC”, “TO”, “PA”, “RO”, “RR”), “Norte”, ifelse(State %in% c(“SP”, “MG”, “RJ”, “ES”), “Sudeste”, ifelse(State %in% c(“MT”, “MS”, “GO”, “DF”), “Centro-Oeste”, ifelse(State %in% c(“PR”, “SC”, “RS”), “Sul”, “Nordeste”)))))