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:

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

  2. 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 diferencie código escrito do "texto como dado" que ele precisará processar de acordo com os interesses do analista.

  3. 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".

## [1] "character"
## [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:

## [1] FALSE

4.1.2 Sequências de caracteres

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

## [1] "" "" "" "" ""

Vejamos seu tamanho:

## [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:

## [1] "Quando surge o alviverde imponente"
## [2] ""                                  
## [3] "Sabe bem o que vem pela frente"    
## [4] ""                                  
## [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.3.1 Atenção

Um vetor com uma string vazia é diferente de um vetor sem strings

## [1] 1
## [1] 0

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.

## [1] FALSE
## [1] TRUE

Acima verificamos que a classe do objeto frase é de tipo character.

## [1] 10
## [1] TRUE
## [1] FALSE

Acima verificamos que a classe do objeto quantidade é de tipo numeric. Seria possível converter de um tipo para outro?

## [1] "10"
## [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?

## [1] "10"                                              
## [2] "Campeonatos Brasileiros vencidos pelo Palmeiras."
## [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:

  1. Se uma cadeia de caracteres estiver presente em um vetor, todo o resto do vetor será convertido em cadeias de caracteres.

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

##      [,1] [,2] [,3] [,4] [,5]
## [1,] "1"  "2"  "3"  "4"  "5" 
## [2,] "a"  "b"  "c"  "d"  "e"
## [1] "matrix"

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.frame':    5 obs. of  2 variables:
##  $ numeros: int  1 2 3 4 5
##  $ letras : Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5

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':    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:

## [[1]]
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## [[2]]
## [1] "a" "b" "c" "d" "e"
## 
## [[3]]
## [1] -0.5131434 -0.3721628  0.2903428  1.3419215  0.2849214
## 
## [[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.

## [1]  7 11
## [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.

## [1] "tudo em minú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.

## [1] "O Palmeiras é o time da virada"
## [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.

## [1] "algumas"   "palavras"  "aleatória" "aqui"      "e"         "ali"

Verificar a intersecção entre dois vetores.

## [1] "algumas"  "palavras"

Verificar a diferença entre dois vetores.

## [1] "aleatória" "aqui"

E a igualdade de elementos entre dois vetores. No caso, entre o vetor vec1 e ele mesmo.

## [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%.

## [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.

## [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:

  1. Manipulação de caracteres: essas funções permitem que você manipule caracteres individuais dentro de sequências de caracteres.

  2. Ferramentas de espaço em branco para adicionar, remover e manipular espaços.

  3. Operações sensíveis à localização geográfica, cujas operações irão variar de local para local.

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

4.6.1 Verificando o tamanho de uma string4

## [1] 61

4.6.2 Identificando caracter numa posição específica.

Selecionando o terceiro caracter.

## [1] "P" "P"

Selecionando do segundo caracter de trás pra frente.

## [1] " Palmeiras é o time da virad" " Palmeiras é o time do amor"

4.6.3 Incluindo caracter ou string numa posicao específica.

## [1] "O PALMEIRAS é o time da virada" "o PALMEIRAS é o time do amor."

Preencher uma string em tamanho fixo.

## [1] "                    O PALMEIRAS é o time da virada"
## [2] "                     o PALMEIRAS é o time do amor."

Remove espaço extra.

## [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.

## [1] "         " "         "

É possível fazer o recorte usando índices de trás pra frente.

## [1] "time da virada" " time do amor."

Extração de palavras.

## [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

## [1] NA     "amor"
## [1] FALSE  TRUE

Utilizando o operador | (“OU”):

## [1]  TRUE  TRUE FALSE  TRUE
## [1] "ente" "ismo" NA     "ismo"
## [1] NA                 "presidencialismo" NA                
## [4] "parlamentarismo"

Usar o “.” corresponde a qualquer caracter exceto uma nova linha:

## [1] NA         "cialismo" "cialista" "rlamenta"

Para identificar o “.” de fato, usamos “\.”. Para poder usar a “\”, adicionamos mais uma e temos:

## [1] FALSE  TRUE

Para identificar a “\” de fato, usamos “\\”:

## O Palmeiras é o time da virada \ o Palmeiras é o time do amor.
## [1] TRUE

4.7.2 Substituição

## [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.
## [1]  TRUE FALSE
## [1] FALSE  TRUE

Referências

Wickham, Hadley, and Garrett Grolemund. 2017. R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. 1 edition. Sebastopol, CA: O’Reilly Media.


  1. Esse capítulo do material é inspirado no livro Handling Strings with R

  2. Assim como fizemos anteriormente com a função nchar().