Capítulo 4 Strings no R
Na ciência da computação chamamos uma sequência de caracteres de string
. Para
o desenvolvimento de análises automatizadas de conteúdo, é necessário saber como processar esse tipo especial de dado (o texto como dado)3. Nesse sentido, três coisas são importantes de serem lembradas aqui:
Computadores não interpretam letras. No limite, todos os caracteres são transformados em sequências compostas por zeros e uns. Logo, é através de padrões que caracteres são interpretados e os computadores armazenam os dados que retornam a nossos olhos.
Programar é escrever! Não é à toa que chamamos as formas de escrita em programação de linguagens de programação. Nesse livro, por exemplo, usamos a linguagem
R
. Sabendo disso, o desafio de se trabalhar com o texto como dado é o desafio de fazer com que o computador diferenciecódigo escrito
do"texto como dado"
que ele precisará processar de acordo com os interesses do analista.Como nós brasileiros lemos, o código e texto é processado pelo computador no seguinte sentido: da esquerda para a direita e de cima para baixo. Logo, ao desenvolver seu
script
é importante ter atenção em relação à ordem de escrita para que o computador possa desempenhar corretamente suas tarefas.
É possível utilizar toda a versatilidade de estruturas de dados no R
(vetores, matrizes, listas, data.frame, etc.) para processar sequências de caracteres. Como trabalhar com strings
no R
, portanto?
4.1 Strings e vetores
Para declarar uma string, utilizamos aspas simples '
ou aspas dupla “““. Vejamos o caso dos dois vetores abaixo, ambos recebendo a letra”a”.
# Vetores de caracteres
<- "a"
caracter1 <- 'A'
caracter2
class(caracter1)
## [1] "character"
class(caracter2)
## [1] "character"
Ambos são da classe character
.
4.1.1 O R
é case sensitive
O R
diferencia letras maiúsculas de letras minúsculas. Se compararmos os dois objetos criados acima, temos:
== caracter2 caracter1
## [1] FALSE
4.1.2 Sequências de caracteres
# string
<- "uma string é uma sequência de caracteres"
txt <- 'também pode ser utilizada com aspas simples'
txt
<- "no caso de aspas dupla, usa-se 'aspas simples' na string"
txt <- 'no caso de aspas simples, usa-se "aspas dupla" na string' txt
<- "para usar \"aspas dupla\" na string é necessário usar \\"
txt cat(txt)
## para usar "aspas dupla" na string é necessário usar \
O R
armazena a sequência de caracteres conforme ela é apresentada. Porém, é possível fazer uso de caracteres especiais para que o computador interprete e apresente o texto de forma adequada. Como vimos acima, o objeto txt
armazena a string conforme foi redigida, mas com o uso da função cat()
podemos apresentá-lo de forma adequada. Perceba a diferença entre o resultado e a sequência de caracteres que, de fato, foi armazenada no objeto txt
.
4.1.3 Operações básicas com vetores de strings
É possível declarar um vetor de caracteres vazio.
# vetor de caracteres com 5 strings vazias
<- character(5)
palmeiras palmeiras
## [1] "" "" "" "" ""
Vejamos seu tamanho:
length(palmeiras) # verificando o tamanho do vetor
## [1] 5
Vemos que o objeto palmeiras
possui 5 elementos, todos sem qualquer conteúdo, mas da classe character
.
Será que é possível inserir conteúdo em elementos específicos do vetor? Vejamos:
# incluindo string no primeiro e terceiro elementos do vetor
1] <- "Quando surge o alviverde imponente"
palmeiras[3] <- "Sabe bem o que vem pela frente"
palmeiras[ palmeiras
## [1] "Quando surge o alviverde imponente" ""
## [3] "Sabe bem o que vem pela frente" ""
## [5] ""
Ótimo! Significa que podemos ter um vetor no com o Hino do Palmeiras, sendo cada um de seus elementos um verso dessa bela poesia.
E seria possível ter um vetor cujos elementos fossem os hinos (sequências de caracteres/strings) de todos os times do país? Sim!
4.1.4 Caracteres e outros tipos de dados
É importante saber como o R
processa o texto como dado (character
) em conjunto com outros formatos.
<- "Campeonatos Brasileiros vencidos pelo Palmeiras."
frase is.numeric(frase)
## [1] FALSE
is.character(frase)
## [1] TRUE
Acima verificamos que a classe do objeto frase
é de tipo character
.
<- 5 + 5
quantidade quantidade
## [1] 10
is.numeric(quantidade)
## [1] TRUE
is.character(quantidade)
## [1] FALSE
Acima verificamos que a classe do objeto quantidade
é de tipo numeric
. Seria possível converter de um tipo para outro?
# convertendo quantidade
<- as.character(quantidade)
quantidade quantidade
## [1] "10"
is.character(quantidade)
## [1] TRUE
Sim! Veja que agora o valor 10 aparece entre aspas, pois o objeto quantidade
foi convertido para a classe character
.
E se um vetor possuir números e caracteres em diferentes elementos, como o R
interpreta a classe desse vetor?
# vetor com números e strings
<- c(10, "Campeonatos Brasileiros vencidos pelo Palmeiras.")
brasileiros brasileiros
## [1] "10"
## [2] "Campeonatos Brasileiros vencidos pelo Palmeiras."
class(brasileiros)
## [1] "character"
Perceba que o vetor é declarado com o número 10 no primeiro elemento e uma string no segundo elemento. Contudo, o R
adota um critério de coerção de dados para que o vetor seja da classe character
. Por isso, o número 10 é automaticamente convertido como caracter.
O R segue duas regras básicas de coerção de tipos de dados:
Se uma cadeia de caracteres estiver presente em um vetor, todo o resto do vetor será convertido em cadeias de caracteres.
Se um vetor tiver apenas elementos lógicos e números, os elementos lógicos serão convertidos em números; Valores TRUE se tornam 1 e os valores FALSE se tornam 0.
4.2 Strings e matrizes
No R
matrizes são estruturas de dados que suportam apenas um tipo de classe de dados. Logo, assim como no caso do vetor visto anteriormente, ao constatar a presenção de alguma entrada de classe character
automaticamente todos os elementos da matriz são convertidos.
# Matrizes ----
<- rbind(c(1:5), letters[1:5])
m m
## [,1] [,2] [,3] [,4] [,5]
## [1,] "1" "2" "3" "4" "5"
## [2,] "a" "b" "c" "d" "e"
class(m)
## [1] "matrix" "array"
4.3 Strings e data.frames
data.frames
são as estruturas de dados mais utilizadas no R
. Sua versatilidade permite ter no mesmo objeto dados de classes diferentes num formato de matriz (matriz de dados). Vejamos:
# Data Frames ----
<- data.frame(numeros = 1:5, letras = letters[1:5])
df1 str(df1)
## 'data.frame': 5 obs. of 2 variables:
## $ numeros: int 1 2 3 4 5
## $ letras : chr "a" "b" "c" "d" ...
Como padrão da função data.frame()
strings são transformadas em fatores. Para manter strings como caracteres deve-se usar o argumento: stringsAsFactors = FALSE
.
<- data.frame(numeros = 1:5, letras = letters[1:5], stringsAsFactors = FALSE)
df1 str(df1)
## 'data.frame': 5 obs. of 2 variables:
## $ numeros: int 1 2 3 4 5
## $ letras : chr "a" "b" "c" "d" ...
4.4 Strings e listas
Das estruturas de objetos mais populares no R
, listas são as mais complexas. Sua grande vantagem em relação às demais estruturas é permitir uma organização hierárquica dos dados independente de sua classe e tamanho. Vejamos um exemplo:
# Listas ----
# listas contemplam qualquer tipo de estrutura de dados
<- list(1:10, letters[1:5], rnorm(5), m)
ls ls
## [[1]]
## [1] 1 2 3 4 5 6 7 8 9 10
##
## [[2]]
## [1] "a" "b" "c" "d" "e"
##
## [[3]]
## [1] 0.8857933 -1.0430847 -1.4837529 -0.1982924 -0.5363551
##
## [[4]]
## [,1] [,2] [,3] [,4] [,5]
## [1,] "1" "2" "3" "4" "5"
## [2,] "a" "b" "c" "d" "e"
No exemplo acima, o objeto ls
é composto por quatro elementos que contêm, cada um, diferentes tamanhos e diferentes estruturas de dados.
4.5 Processamento básico
4.5.1 Contando caracteres
A função nchar()
é um forma ágil e fácil de se obter o número de caracteres de uma string ou de strings de um vetor.
nchar(c("Quantos", "caracteres?"))
## [1] 7 11
nchar("Quantos caracteres?")
## [1] 19
No exemplo acima, perceba que a função contabiliza o espaço entre palavras como caracter. Por isso, a soma do total de caracteres do primeiro caso (\(7 + 11 = 18\) caracteres) não é igual ao total de caracteres do segundo (\(19\) caracteres).
4.5.2 toupper()
, tolower()
Sendo o R
case sensitive, para o processamento do texto como dado, pode ser de interesse do pesquisador harmonizar o conteúdo sob análise com o objetivo de ter todos os caracteres em formato maiúsculo ou minúsculo. As funções toupper()
e tolower()
desempenham bem esse papel.
tolower(c("TUdo eM MinúsCuLA", "ABCDE"))
## [1] "tudo em minúscula" "abcde"
toupper(c("TUdo eM mAiúsCula", "ABCDE"))
## [1] "TUDO EM MAIÚSCULA" "ABCDE"
4.5.2.1 Recortando strings: substr()
, substring()
.
Para o processamento do texto como dado, também pode ser de interesse do pesquisador a seleção de trechos de uma sequência de caracteres. Isso pode ser facilmente feito com as funções substr()
e substring()
indicando como parâmetros a posição nas quais a string deve ser recortada.
substr("O Palmeiras é o time da virada, o Palmeiras é o time do amor.", 1, 30)
## [1] "O Palmeiras é o time da virada"
substring("O Palmeiras é o time da virada, o Palmeiras é o time do amor.", 33, 60)
## [1] "o Palmeiras é o time do amor"
4.5.2.2 União, Intersecção, Diferença, Igualdade
Operações com vetores de forma geral podem ser aplicadas a vetores com strings. Podemos, por exemplo, unir diferentes vetores.
# União
<- c("algumas", "palavras", "aleatória", "aqui")
vec1 <- c("e", "algumas", "palavras", "ali")
vec2 union(vec1, vec2)
## [1] "algumas" "palavras" "aleatória" "aqui" "e" "ali"
Verificar a intersecção entre dois vetores.
# Intersecção
intersect(vec1, vec2)
## [1] "algumas" "palavras"
Verificar a diferença entre dois vetores.
# Diferença
setdiff(vec1, vec2)
## [1] "aleatória" "aqui"
E a igualdade de elementos entre dois vetores. No caso, entre o vetor vec1
e ele mesmo.
# Igualdade
identical(vec1, vec1)
## [1] TRUE
4.5.2.3 Elemento contido em
Outra operação básica de interesse é a verificação se um elemento (no caso, uma sequência de caracteres) está contido num objeto. Vamos verificar abaixo se a sequência “aqui” está contida no vetor vec1
através do operador %in%
.
# Elemento contido em ----
<- "aqui"
elem %in% vec1 elem
## [1] TRUE
É importante destacar que o exemplo acima é uma operação básica de vetores no R
e não exclusiva para sequência de caracteres. Nesse sentido, ela apenas checa se no vetor vec1
há algum elemento idêntico ao padrão “aqui”. Mais adiante verificaremos como identificar a presença de sequências de caracteres no interior de outras strings sem que tenham de ser idênticas.
4.5.2.4 Ordenação
É possível ordenar um vetor de strings em ordem alfabética ou em sentido oposto como no exemplo abaixo. Tal versatilidade pode ser útil para o ordenamento de uma matriz de dados completa com base numa variável de nomes, por exemplo.
# Ordenando ----
sort(vec1, decreasing = TRUE)
## [1] "palavras" "aqui" "algumas" "aleatória"
4.6 O pacote stringr
O pacote stringr
integra uma coleção de pacotes projetados para a ciência de dados, o tidyverse
. Combinado ao pacote stringi
, você terá acesso a praticamente todas as possíveis funções necessárias para o processamento de strings em mais alto nível.
Existem quatro famílias principais de funções no stringr
:
Manipulação de caracteres: essas funções permitem que você manipule caracteres individuais dentro de sequências de caracteres.
Ferramentas de espaço em branco para adicionar, remover e manipular espaços.
Operações sensíveis à localização geográfica, cujas operações irão variar de local para local.
Funções de correspondência de padrões, sendo o mais comum as expressões regulares.
Além do que veremos neste material, é altamente recomendável a consulta ao capítulo sobre strings do R for Data Science.
# carregando pacote ----
library(stringr)
4.6.1 Verificando o tamanho de uma string4
str_length("O Palmeiras é o time da virada, o Palmeiras é o time do amor.")
## [1] 61
4.6.2 Identificando caracter numa posição específica.
# vetor de strings
<- c("O Palmeiras é o time da virada", "o Palmeiras é o time do amor.") txt
Selecionando o terceiro caracter.
# identificando terceira letra
str_sub(txt, 3, 3)
## [1] "P" "P"
Selecionando do segundo caracter de trás pra frente.
str_sub(txt, 2, -2)
## [1] " Palmeiras é o time da virad" " Palmeiras é o time do amor"
4.6.3 Incluindo caracter ou string numa posicao específica.
str_sub(txt, 3, 11) <- "PALMEIRAS"
txt
## [1] "O PALMEIRAS é o time da virada" "o PALMEIRAS é o time do amor."
Preencher uma string em tamanho fixo.
str_pad(txt, 50) # por padrão: left
## [1] " O PALMEIRAS é o time da virada"
## [2] " o PALMEIRAS é o time do amor."
Remove espaço extra.
<- str_pad(txt, 50) # por padrão: left
txt str_trim(txt)
## [1] "O PALMEIRAS é o time da virada" "o PALMEIRAS é o time do amor."
4.6.4 Recortando uma string para obter parte da sequência de caracteres.
str_sub(txt, start = 3, end = 11)
## [1] " " " "
É possível fazer o recorte usando índices de trás pra frente.
str_sub(txt, start = -14, end = -1)
## [1] "time da virada" " time do amor."
Extração de palavras.
word(txt, 2)
## [1] "" ""
word(txt, -1)
## [1] "virada" "amor."
4.7 Regular Expressions no R
Até aqui você viu funções básicas e intermediárias para o processamento de sequências de caracteres no R
. Para avançar, é necessário aprender o uso de expressões regulares ( Regular Expressions ).
Como definido no livro Handling Strings with R, uma expressão regular é um conjunto de símbolos que descreve um padrão de texto. Mais formalmente, uma expressão regular é um padrão que descreve um conjunto de cadeias de caracteres. Como o termo “expressão regular” é bastante longo, a maioria das pessoas usa a palavra regex para se referir à área.
Entre outras tarefas, o uso de expressões regulares pode ajudá-lo a (Wickham and Grolemund 2017):
- Determinar cadeias de caracteres correspondentes a um padrão.
- Encontrar as posições de padrões correspondentes.
- Extrair o conteúdo de padrões correspondentes.
- Substituir o padrão correspondente por novos valores.
- Dividir uma sequência com base na correspondência de um padrão determinado.
No entanto, é preciso ter atenção, pois o uso de expressões regulares pode se tornar uma tarefa realmente complexa. Veja esta discussão do StackOverflow a respeito de seu uso para identificação de endereços de e-mail, por exemplo.
Dada a complexidade que a área pode assumir, vamos verificar o uso das regex em algumas funções do pacote stringr
com base nesse tutorial. Junto a ele, é recomendável que a leitura atenta do ?regex
no R
.
4.7.1 Identificação e Extração de padrão
<- c("O Palmeiras é o time da virada", "o Palmeiras é o time do amor.")
txt str_extract(txt, "amor")
## [1] NA "amor"
str_detect(txt, "amor")
## [1] FALSE TRUE
Utilizando o operador |
(“OU”):
str_detect(c("presidente", "presidencialismo", "presidencialista", "parlamentarismo"), "ente|ismo")
## [1] TRUE TRUE FALSE TRUE
str_extract(c("presidente", "presidencialismo", "presidencialista", "parlamentarismo"), "ente|ismo")
## [1] "ente" "ismo" NA "ismo"
str_extract(c("presidente", "presidencialismo", "presidencialista", "parlamentarismo"), "(presidencial|parlamentar)ismo")
## [1] NA "presidencialismo" NA "parlamentarismo"
Usar o “.” corresponde a qualquer caracter exceto uma nova linha:
<- c("presidente", "presidencialismo", "presidencialista", "parlamentarismo")
txt str_extract(txt, "..a.....")
## [1] NA "cialismo" "cialista" "rlamenta"
Para identificar o “.” de fato, usamos “\.”. Para poder usar a “\”, adicionamos mais uma e temos:
<- c("O Palmeiras é o time da virada", "o Palmeiras é o time do amor.")
txt str_detect(txt, "\\.")
## [1] FALSE TRUE
Para identificar a “\” de fato, usamos “\\”:
<- c("O Palmeiras é o time da virada \\ o Palmeiras é o time do amor.")
txt writeLines(txt)
## O Palmeiras é o time da virada \ o Palmeiras é o time do amor.
str_detect(txt, "\\.")
## [1] TRUE
4.7.2 Substituição
<- c("O Palmeiras é o time da virada", "o Palmeiras é o time do amor.")
txt str_replace(txt, "Palmeiras", "PALMEIRAS")
## [1] "O PALMEIRAS é o time da virada" "o PALMEIRAS é o time do amor."
4.7.3 Âncoras
Por padrão, expressões regulares buscam por correspondência em qualquer parte de uma sequência de caracteres. Porém, é extremamente útil poder ancorar a busca pela correspondência no início ou no final de uma string. Podemos usar:
- “^” para coincidir com o início da string.
- “$” para coincidir com o final da string.
<- c("O Palmeiras é o time da virada", "o Palmeiras é o time do amor.")
txt str_detect(txt, "^O")
## [1] TRUE FALSE
str_detect(txt, "\\.$")
## [1] FALSE TRUE