Strings

Variáveis de texto são muito comuns nos bancos de dados e, geralmente, dão bastante trabalho para serem manipuladas. É muito comum encontrarmos colunas com categorias não padronizadas, como, por exemplo, uma variável Estado com “SP”, “sp”, “Sao Paulo”, “São Paulo” etc, todas indicando o mesmo estado.

O R possui várias funções para manipular textos (ou strings). No entanto, as funções do base não possuem uma interface consistente e cada uma tem a sua forma de passar os parâmetros, dificultando a programação durante a análise.

Pensando nisso, Hadley Wickham deu aquela força para a comunidade R e criou o pacote stringr, que possui uma sintaxe consistente, permitindo o usuário manipular textos com muito mais facilidade.

Vantagens do stringr em relação ao base%>%

Sintaxe unificada, o que auxilia na memorização e leitura do código. Todas as funções são vetorizadas. Construído sobre a biblioteca ICU, implementada em C e C++. É uma garantia de resultados mais rápidos e confiáveis. ### Regras básicas do pacote As funções de manipulação de texto começam com str_. Caso esqueça o nome de uma função, basta digitar stringr::str_ e apertar TAB para ver quais são as opções. O primeiro argumento da função é sempre uma string ou um vetor de strings. ### Curiosidade Inicialmente, o stringr era um wrapper de funções do base. Depois disso, surgiu um novo pacote stringi, com sintaxe similar ao stringr, mas funcionando como wrapper da biblioteca ICU. Wickham gostou tanto do pacote stringi que decidiu reescrever o stringr como um wrapper do stringi. Veja essa página para detalhes.

Conceitos básicos

Todas as funções do stringr começam com o prefixo str_. Isso ajuda na hora de encontrar a função que você está procurando. No RStudio, digite str_ e aperte TAB para visualizar a lista de funções com esse prefixo. Você pode verificar o que cada função faz até encontrar a que atende às suas necessidades.

\label{fig:1} Tipos de Variáveis

Tipos de Variáveis

Nesta seção, vamos utilizar as funções mais simples do stringr. Em seguida, vamos falar um pouco de regex e então veremos funções mais avançadas do pacote.

Antes de mais nada, instale e carregue o pacote stringr.

#install.packages("stringr")
library(stringr)

str_length

A função mais simples do stringr() é a função str_length(). Esta função recebe como argumento um vetor de strings e retorna o número de caracteres de cada string. Repare que o espaço " " é considerado um caracter.

str_length("São Paulo")
## [1] 9
str_length(c("São Paulo", "Rio de Janeiro", 
             "Rio Grande do Norte", "Acre"))
## [1]  9 14 19  4

Note que str_length() é diferente de length(). O primeiro retorna o número de caracteres e o segundo retorna o comprimento do objeto. Isso fica mais claro no seguinte exemplo:

s <- c("São Paulo", "Rio de Janeiro", 
       "Rio Grande do Norte", "Acre")

str_length(s)
## [1]  9 14 19  4

A função str_length() retornou um vetor com o número de caracteres de cada elemento do vetor s, enquanto length() retornou o comprimento do vetor s.

str_to_upper, str_to_lower, str_to_title Essas funções servem para modificar a caixa das letras. Veja alguns exemplos:

s <- "Somos a curso-r"
str_to_lower(s)
## [1] "somos a curso-r"
str_to_upper(s)
## [1] "SOMOS A CURSO-R"
str_to_title(s)
## [1] "Somos A Curso-R"

str_trim

É muito comum encontrar textos que vêm com espaços a mais, principalmente de dados provenientes de formulários em que cada usuário escreve da forma que prefere. Isso é um problema pois cria categorias diferentes para valores que deveriam ser iguais. Espaços antes e após o texto são especialmente chatos, pois pode ser difícil detectá-los.

s <- c("M", "F", "F", " M", " F ", "M")
as.factor(s)
## [1] M   F   F    M   F  M  
## Levels:  F   M F M

A função str_trim() ajuda removendo os espaços excedentes antes e depois da string.

string_aparada <- str_trim(s)
as.factor(string_aparada)
## [1] M F F M F M
## Levels: F M

str_sub

Não é raro você precisar obter uma parte fixa de uma string, como, por exemplo, manipular textos da forma:

s <- c("01-Feminino", "02-Masculino", "03-Indefinido")

Você pode querer apenas a parte final da string. Neste caso, pode usar a função str_sub().

str_sub(s, start = 4)
## [1] "Feminino"   "Masculino"  "Indefinido"

Também é possível obter apenas os números.

str_sub(s, end = 2)
## [1] "01" "02" "03"

Em outros casos você precisa obter os últimos 2 caracteres.

s <- c("Feminino-01", "Masculino-02", "Indefinido-03")
str_sub(s, end = -4)
## [1] "Feminino"   "Masculino"  "Indefinido"
str_sub(s, start = -2)
## [1] "01" "02" "03"

É possível usar os argumentos start e end conjuntamente.

s <- c("__SP__", "__MG__", "__RJ__")
str_sub(s, 3, 4)
## [1] "SP" "MG" "RJ"

str_c

Concatena strings em uma única string.

string1 <- "O valor p é: "
string2 <- 0.03
library(stringr)
str_c(string1, string2)
## [1] "O valor p é: 0.03"

Pode misturar objetos com strings definidas diretamente na função.

string1 <- "Brigadeiro"
string2 <- "bom"
string3 <- "melhor"
library(stringr)
str_c(string1, " é a prova de que não existe nada tão ", string2,
      " que não pode ficar ", string3, ".")
## [1] "Brigadeiro é a prova de que não existe nada tão bom que não pode ficar melhor."

A função é vetorizada.

string1 <- c("Brigadeiro", "A política brasileira")
string2 <- c("bom", "ruim")
string3 <- c("melhor", "pior")
library(stringr)
str_c(string1, " é a prova de que não existe nada tão ", string2,
      " que não pode ficar ", string3, ".")
## [1] "Brigadeiro é a prova de que não existe nada tão bom que não pode ficar melhor."          
## [2] "A política brasileira é a prova de que não existe nada tão ruim que não pode ficar pior."

Expressões Regulares

Trabalhar com textos exige um certo conhecimento de expressões regulares. As expressões regulares — ou simplesmente regex — permitem identificar conjuntos de caracteres, palavras e outros padrões por meio de uma sintaxe concisa.

O stringr utiliza regex da forma descrita neste documento. A própria definição de regex do R é um ótimo manual.

Vamos estudar expressões regulares por meio de exemplos e da função str_detect(). Ela retorna TRUE se uma string atende a uma expressão regular e FALSE caso contrário. Por exemplo:

library(stringr)
str_detect("sao paulo", pattern = "paulo$")
## [1] TRUE
str_detect("sao paulo sp", pattern = "paulo$")
## [1] FALSE

A regex/pattern paulo$ indica que o texto deve ser terminado em paulo. Existem diversos caracteres auxiliares que vão auxiliar na manipulação dos textos, assim como o $. Importante: o valor passado para o argumento pattern de qualquer função do pacote stringr será entendido como uma regex.

A tabela abaixo mostra a aplicação de cinco regex em seis strings distintas.

^ban’ reconhece apenas o que começa exatamente com “ban”. ‘b ?an’ reconhece tudo que tenha “ban”, com ou sem espaço entre o “b” e o “a”. ‘ban’ reconhece tudo que tenha “ban”, mas não ignora case. BAN’ reconhece tudo que tenha “BAN”, mas não ignora case. ‘ban$’ reconhece apenas o que termina exatamente em “ban”

\label{fig:1}

funções avançadas%>%

Agora que já vimos as funções básicas do stringr e aprendemos um pouco de regex, vamos às funções mais avançadas. Basicamente, essas funções buscarão patterns em um vetor de strings e farão alguma coisa quando encontrá-lo.

Como já vimos na sessão sobre regex, a função mais simples que possui o argumento pattern= é a str_detect().

str_detect()

Retorna TRUE se a regex é compatível com a string e FALSE caso contrário.

library(stringr)
str_detect("sao paulo", pattern = "paulo$")
## [1] TRUE
str_detect("sao paulo sp", pattern = "paulo$")
## [1] FALSE

str_replace() e str_replace_all()%>%

Substituem um padrão (ou todos) encontrado para um outro padrão.

titulos <- c("o arqueiro", "o andarilho", "o herege")
# remove a primeira vogal de cada string
str_replace(titulos, "[aeiou]", "") 
## [1] " arqueiro"  " andarilho" " herege"
# substitui todas as vogais por "-"
str_replace_all(titulos, "[aeiou]", "-") 
## [1] "- -rq---r-"  "- -nd-r-lh-" "- h-r-g-"
s <- "--    ffffWda, --- unWvers--    e    tud-  maWs"
# substitui o primeiro f (ou f"s) por "v"
s <- str_replace(s, "f+", "v")
s
## [1] "--    vWda, --- unWvers--    e    tud-  maWs"
# substitui o primeiro hífen (ou hífens) por "A"
s <- str_replace(s, "-+", "A")
s
## [1] "A    vWda, --- unWvers--    e    tud-  maWs"
# substitui todos os hífens (um ou mais) por somente "o"
s <- str_replace_all(s, "-+", "o") 
s
## [1] "A    vWda, o unWverso    e    tudo  maWs"
# substitui "W" por "i"
s <- str_replace_all(s, "W", "i") 
s
## [1] "A    vida, o universo    e    tudo  mais"
# tirar espaços extras
s <- str_replace_all(s, " +", " ") 
s
## [1] "A vida, o universo e tudo mais"

Muitas vezes queremos remover alguns caracteres especiais de um texto, mas esses caracteres fazem parte de comandos de regex, por exemplo:

s <- "1 + 2 + 5"
#str_replace_all(s, "+", "-")

Essa forma retorna um erro, pois a função tenta montar uma regex. Você poderia tentar de outras formas, que não retornariam erro, mas também não retornariam o resultado esperado.

str_replace_all(s, " + ", " - ")
## [1] "1 + 2 + 5"

Nesse caso, use a função fixed() para indicar que o parâmetro não é uma regex.

str_replace_all(s, fixed("+"), "-")
## [1] "1 - 2 - 5"

str_extract() e str_extract_all()

As funções str_extract() e str_extract_all() extraem padrões de uma string. Por exemplo:

r_core_group <- c(
  'Douglas Bates', 'John Chambers', 'Peter Dalgaard',
  'Robert Gentleman', 'Kurt Hornik', 'Ross Ihaka', 'Tomas Kalibera',
  'Michael Lawrence', 'Friedrich Leisch', 'Uwe Ligges', '...'
)

sobrenomes <- str_extract(r_core_group, '[:alpha:]+$')
sobrenomes
##  [1] "Bates"     "Chambers"  "Dalgaard"  "Gentleman" "Hornik"    "Ihaka"    
##  [7] "Kalibera"  "Lawrence"  "Leisch"    "Ligges"    NA

str_match() e str_match_all()

As funções str_match() e str_match_all() extraem pedaços da string identificados pela regex. Caso queira extrair somente a parte identificada, use parênteses.

presidentes <- c("da Fonseca, DeodoroDeodoro da Fonseca", 
"Peixoto, FlorianoFloriano Peixoto", "de Morais, PrudentePrudente de Morais", 
"Sales, CamposCampos Sales")

nomes_presidentes <- str_match(presidentes, '(.*), ([a-zA-Z]{1,})[A-Z]{1}')
nomes_presidentes
##      [,1]                   [,2]         [,3]      
## [1,] "da Fonseca, DeodoroD" "da Fonseca" "Deodoro" 
## [2,] "Peixoto, FlorianoF"   "Peixoto"    "Floriano"
## [3,] "de Morais, PrudenteP" "de Morais"  "Prudente"
## [4,] "Sales, CamposC"       "Sales"      "Campos"
str_c(nomes_presidentes[,3], nomes_presidentes[,2], sep = ' ')
## [1] "Deodoro da Fonseca" "Floriano Peixoto"   "Prudente de Morais"
## [4] "Campos Sales"

str_split() e str_split_fixed()

Essas funções separam uma string em várias de acordo com um separador.

string <- 'Durante um longo período de tempo o "R" foi escrito "P" como no alfabeto cirílico. O seu nome no alfabeto fenício era "rech". Seu significado era o de uma cabeça, representada pela adaptação do hieróglifo egípcio de uma cabeça. Transformou-se no "rô" dos gregos. Os romanos modificaram o rô acrescentando um pequeno traço para diferenciá-lo do no nosso P.'

str_split(string, fixed('.'))
## [[1]]
## [1] "Durante um longo período de tempo o \"R\" foi escrito \"P\" como no alfabeto cirílico"                
## [2] " O seu nome no alfabeto fenício era \"rech\""                                                         
## [3] " Seu significado era o de uma cabeça, representada pela adaptação do hieróglifo egípcio de uma cabeça"
## [4] " Transformou-se no \"rô\" dos gregos"                                                                 
## [5] " Os romanos modificaram o rô acrescentando um pequeno traço para diferenciá-lo do no nosso P"         
## [6] ""

O str_split_fixed faz o mesmo que str_split(), mas separa apenas n vezes.

str_split_fixed(string, fixed('.'), 3)
##      [,1]                                                                                   
## [1,] "Durante um longo período de tempo o \"R\" foi escrito \"P\" como no alfabeto cirílico"
##      [,2]                                          
## [1,] " O seu nome no alfabeto fenício era \"rech\""
##      [,3]                                                                                                                                                                                                                                      
## [1,] " Seu significado era o de uma cabeça, representada pela adaptação do hieróglifo egípcio de uma cabeça. Transformou-se no \"rô\" dos gregos. Os romanos modificaram o rô acrescentando um pequeno traço para diferenciá-lo do no nosso P."

str_subset()

A função str_subset() retorna somente as strings compatíveis com a regex.

frases <- c('a roupa do rei', 'de roma', 'o rato roeu')
str_subset(frases, 'd[eo]')
## [1] "a roupa do rei" "de roma"

É o mesmo que fazer subset usando a função str_detect.

frases[str_detect(frases, "d[eo]")]
## [1] "a roupa do rei" "de roma"

Exercício

  1. O CPF é um número de 11 dígitos, por exemplo: 54491651884. No entanto para facilitar a visualização costumamos mostrá-lo com separadores a cada 3 casas: 544.916.518-84. Crie uma função que transforma um número de 11 dígitos em uma string com as separações, como um CPF.

  2. Transforme o vetor de strings abaixo em “01 - Alto” “02 - Médio” “03 - Baixo”.

Resposta

ormata_CPF <- function(num) {
  
  if(str_count(num) != 11) {
    stop("Número inválido!")
  }
  
  # Concatemos cada parte do número, interpolando
  # com os pontos e o traço.
  
  s <- str_c(
    str_sub(num, start = 1, end = 3),
    ".",
    str_sub(num, start = 4, end = 6),
    ".",
    str_sub(num, start = 7, end = 9),
    "-",
    str_sub(num, start = 10, end = 11)
  )
  
}

formata_CPF <- function(num) {
  
  if(str_count(num) != 11) {
    stop("Número inválido!")
  }
  
  str_replace(string = num, 
              pattern = "([0-9]{3})([0-9]{3})([0-9]{3})", 
              replacement = "\\1.\\2.\\3-")
}

A função str_replace() faz o seguinte:

O pattern= procurará um padrão de 9 números: O primeiro ([0-9]{3}) pega os três primeiros números. O segundo ([0-9]{3}) pega os três próximos números (quarto ao sexto). O terceiro ([0-9]{3}) pega os três números seguintes (sétimo ao nono). O replacement= substituirá esses 9 números pela string formada por: \1, a expressão dada pelo primeiro parêntese do pattern=, isto é, os três primeiros números. ., o primeiro ponto. \2, a expressão dada pelo segundo parêntese do pattern=, isto é, os três próximos números (quarto ao sexto). ., o segundo ponto. \3, a expressão dada pelo terceiro parêntese do pattern=, isto é, os três números seguintes (sétimo ao nono). -, o traço.

s <- c('Alto', 'Médio', 'Baixo')

# Usando str_c()

str_c("0", 1:length(s), " - ", s, sep = "")
## [1] "01 - Alto"  "02 - Médio" "03 - Baixo"
## [1] "01 - Alto"  "02 - Médio" "03 - Baixo"

# Usando o pacote glue
library(glue)
glue("0{1:length(s)} - {s}")
## 01 - Alto
## 02 - Médio
## 03 - Baixo