Na terceira parte da série de posts sobre manipulação de texto com stringr veremos como usar funções que removem padrões encontrados no texto, que substituem padrões por outros e que eliminam espaços de formas diferentes. Também aprofundaremos com um pouco mais de regex.
Na terceira parte sobre manipulação de texto com stringr, manteremos a lógica anterior dos posts da série, isto é, continuaremos a propor problemas e mostrar como os podemos resolver.
Antes de continuar será necessário “sujar” um pouco nossos dados. Para isso, carregamos os dados que já vinhamos usando anteriormente e inserimos aleatoriamente pontuações e espaços desnecessários na coluna “pais_port”. A ideia desse procedimento consiste em “sujar” os dados e simular uma base de dados bruta. A “sujeira” que fizemos é relativamente simples e, na vida real, você poderá encontrar bases de dados em estado muito pior, mas a ideia é que você consiga generalizar o conhecimento a partir dos exemplos mais didáticos que elaboramos.
library(dplyr)
library(stringr)
library(data.table)
Abaixo carregamos os dados que iremos utilizar. Em seguida, “sujamos” a coluna "pais_port:
paises <- fread(
"https://raw.githubusercontent.com/mgcleaver/correlacoes/master/paises_isoa.txt",
encoding = "UTF-8"
)
# para reprodutibilidade o seu R precisa ser uma versão > 3.6.0.
set.seed(1)
indice1 <- sample(1:nrow(paises), floor(nrow(paises)/2)) %>%
sort()
indice2 <- setdiff(1:nrow(paises), indice1)
paises$pais_port[indice1] <- paste0(" ", paises$pais_port[indice1], ".")
paises$pais_port[indice2] <- paste0(paises$pais_port[indice2], " ; ")
indice_espaco <- sample(1:nrow(paises), 10)
paises$pais_port[indice_espaco] <- paste0(" ", paises$pais_port[indice_espaco], " ")
indice_interrogacao <- sample(1:nrow(paises), 10)
paises$pais_port[indice_interrogacao] <- paste0(paises$pais_port[indice_espaco], "?")
head(paises)
ISOA pais_ing pais_port
1: ABW Aruba Aruba.
2: AFG Afghanistan Afeganistão ;
3: AGO Angola Angola ;
4: AIA Anguilla Anguila ;
5: ALA Åland Islands Ilhas Aland ;
6: ALB Albania Albânia ;
Primeiramente, vejamos quais das nossas observações possuem ponto.
Como um primeiro exercício, tentemos identificar as 10 primeiras observações que possuem um ponto na coluna “pais_port”:
paises %>%
filter(str_detect(pais_port, ".")) %>%
head(10)
ISOA pais_ing pais_port
1 ABW Aruba Aruba.
2 AFG Afghanistan Afeganistão ;
3 AGO Angola Angola ;
4 AIA Anguilla Anguila ;
5 ALA Åland Islands Ilhas Aland ;
6 ALB Albania Albânia ;
7 AND Andorra Andorra.
8 ARE United Arab Emirates Emirados Árabes Unidos ;
9 ARG Argentina Argentina ;
10 ARM Armenia Armênia ;
Podemos observar que nosso filtro não foi capaz de identificar quais observações tinham o ponto na coluna “pais_port”. O problema é que em regex o ponto é usado como uma espécie de coringa, pois pode igualar qualquer letra e número, além de outros símbolos de pontuação. Como as observações da coluna “pais_port” tem pelo menos um caractere, o filtro traz todas as observações, uma vez que todas elas atendem o requisito.
Para que identifiquemos quais observações possuem literalmente um ponto, devemos escapar a função regex atribuída ao ponto com duas contrabarras (\\
), isto é, devemos inserir o seguinte padrão na função str_detect
, no argumento correspondente: “\\.
”. Vejamos:
pontos <- paises %>%
filter(str_detect(pais_port, "\\.")) %>%
pull(pais_port) %>%
.[1:10]
# ver as 10 primeiras observações da coluna pais_port com pontos
pontos
[1] " Aruba."
[2] " Andorra."
[3] " Terras Austrais e Antárticas Francesas."
[4] " Antígua e Barbuda."
[5] " Benin."
[6] " Países Baixos Caribenhos."
[7] " Burkina Faso."
[8] " Bangladesh."
[9] " Bulgária."
[10] " Barém."
Agora que conseguimos ver quais observações tem o ponto, como eliminamos o ponto das observações? Devemos utilizar a função str_remove
. No primeiro argumento dessa função inserimos o vetor que desejamos modificar. No segundo argumento inserimos o padrão que desejamos remover. Vejamos, então, como aplicar a função:
str_remove(pontos, "\\.")
[1] " Aruba"
[2] " Andorra"
[3] " Terras Austrais e Antárticas Francesas"
[4] " Antígua e Barbuda"
[5] " Benin"
[6] " Países Baixos Caribenhos"
[7] " Burkina Faso"
[8] " Bangladesh"
[9] " Bulgária"
[10] " Barém"
Comparando o resultado acima com o anterior, verificamos que não há mais pontos em nossos dados. Alteremos o objeto paises
para incorporar a modificação proposta.
paises <- paises %>%
mutate(pais_port = str_remove(pais_port, "\\."))
Agora quando buscamos pelo ponto na coluna “pais_port” não mais o encontramos em nossas observações:
paises %>%
filter(str_detect(pais_port, "\\."))
[1] ISOA pais_ing pais_port
<0 rows> (or 0-length row.names)
Problema resolvido. Mas, note o seguinte: a função str_remove
apenas elimina a primeira ocorrência do padrão encontrado. Se houvesse dois pontos em uma observação, a função str_remove
apenas removeria o ponto que encontrasse primeiro, deixando ainda um ponto remanescente no meio do texto. Se precisássemos remover todos os pontos, utilizaríamos a função str_remove_all
. Em essência ela faz a mesma coisa que a função str_remove
, mas em vez de remover apenas a primeira ocorrência, ela remove todas.
Primeiramente, verifiquemos se existem observações com espaços antes do início do texto na coluna “pais_port”. Se você não entende porque o padrão “^” detecta observações que se iniciam com espaço, veja a a parte II desse post.
espaco1 <- paises %>%
filter(str_detect(pais_port, "^ ")) %>%
pull(pais_port) %>%
.[1:10]
espaco1
[1] " Aruba"
[2] " Andorra"
[3] " Terras Austrais e Antárticas Francesas"
[4] " Antígua e Barbuda"
[5] " Benin"
[6] " Países Baixos Caribenhos"
[7] " Burkina Faso"
[8] " Bangladesh"
[9] " Bulgária"
[10] " Barém"
Ok. Agora verifiquemos se também existem espaços após o fim do texto na coluna “pais_port”:
espaco2 <- paises %>%
filter(str_detect(pais_port, " $")) %>%
pull(pais_port) %>%
.[1:10]
espaco2
[1] "Afeganistão ; " "Angola ; "
[3] "Anguila ; " "Ilhas Aland ; "
[5] "Albânia ; " "Emirados Árabes Unidos ; "
[7] "Argentina ; " "Armênia ; "
[9] "Samoa Americana ; " "Antártica ; "
Após investigar os dados encontramos que há espaços antes do texto começar bem como depois do texto terminar. Para resolver nosso problema devemos utilizar a função str_trim
. Essa função elimina espaços que ficam do lado esquerdo ou do lado direito do dado textual. No primeiro argumento inserimos o vetor que queremos modificar. No segundo argumento escolhemos o lado dos espaços que queremos eliminar. Para eliminar espaços que ficam apenas do lado esquerdo do texto devemos utilizar o argumento “left”. Para eliminar espaços que ficam apenas do lado direito devemos utilizar o argumento “right”. Se quisermos eliminar espaços tanto do lado esquerdo como do lado direito devemos utilizar o argumento “both” (é justamente isso que queremos neste problema). Vejamos como ficam os objetos espaco1
e espaco2
:
str_trim(espaco1, "both")
[1] "Aruba"
[2] "Andorra"
[3] "Terras Austrais e Antárticas Francesas"
[4] "Antígua e Barbuda"
[5] "Benin"
[6] "Países Baixos Caribenhos"
[7] "Burkina Faso"
[8] "Bangladesh"
[9] "Bulgária"
[10] "Barém"
str_trim(espaco2, "both")
[1] "Afeganistão ;" "Angola ;"
[3] "Anguila ;" "Ilhas Aland ;"
[5] "Albânia ;" "Emirados Árabes Unidos ;"
[7] "Argentina ;" "Armênia ;"
[9] "Samoa Americana ;" "Antártica ;"
Agora vemos que não há mais espaços do lado esquerdo nem do lado direito do texto. Contudo, após aplicar a função no objeto espaco2
, observamos que existem espaços excessivos entre a última letra do nome do país e o ponto e vírgula
.
Será que também conseguimos transformar mais de um espaço para um único espaço? Já já veremos como resolver isso, mas antes modifiquemos o objeto paises
com intuito de modificá-lo conforme a proposta deste problema:
paises <- paises %>%
mutate(pais_port = str_trim(pais_port, "both"))
A função que poderá nos ajudar com essa tarefa se chama str_squish
. Note que essa função, além de eliminar espaços duplos, também cumpre a função de eliminar espaços à direita e espaços à esquerda do texto. Diferentemente, da função str_trim
, a função str_squish
não nos permite escolher um lado específico para eliminar espaços. Trata-se de uma função extremamente útil para padronizar dados textuais. No caso da coluna “pais_port”, nós já eliminamos espaços à direita e espaços à esquerda. Portanto a função somente eliminará os espaços duplos contidos na coluna. A função também elimina quebras de linha e outros tipos de espaço. Vejamos primeiro como estão os dez primeiros elementos com mais de um espaço:
mais_espacos <- paises %>%
filter(str_detect(pais_port, " {2,}")) %>%
pull(pais_port) %>%
.[1:10]
mais_espacos
[1] "Afeganistão ;" "Angola ;"
[3] "Anguila ;" "Ilhas Aland ;"
[5] "Albânia ;" "Emirados Árabes Unidos ;"
[7] "Argentina ;" "Armênia ;"
[9] "Samoa Americana ;" "Antártica ;"
Vamos primeiro interpretar o padrão que inserimos no segundo argumento na função str_detect
. O primeiro termo é um espaço (" “). O termo que segue o espaço é o”{2,}“. Em regex, este último termo significa que procuramos padrões textuais que tenham 2 elementos ou mais iguais ao caractere imediatamente anterior. Por exemplo, se tivéssemos inserido o termo” {2,4}", a nossa busca encontraria todas as observações com dois, três ou quatro espaços.
Observemos nossa função str_squish
em ação:
str_squish(mais_espacos)
[1] "Afeganistão ;" "Angola ;"
[3] "Anguila ;" "Ilhas Aland ;"
[5] "Albânia ;" "Emirados Árabes Unidos ;"
[7] "Argentina ;" "Armênia ;"
[9] "Samoa Americana ;" "Antártica ;"
Compare o resultado acima com o resultado imediatamente anterior.
Vejamos um outro exemplo só para fixar o entendimento:
teste <- c(" isso é um \nteste para ver como \ta função str_squish funciona ")
teste
[1] " isso é um \nteste para ver como \ta função str_squish funciona "
Para que você veja o que os \n
e \t
significam na prática utilizemos a função cat
. Vejamos:
cat(teste)
isso é um
teste para ver como a função str_squish funciona
Como podemos ver, o primeiro termo (\n
) representa uma quebra de linha; o segundo (\t
), representa um espaço do tipo “tab”.
De novo, vejamos mais uma vez str_squish
em ação:
str_squish(teste)
[1] "isso é um teste para ver como a função str_squish funciona"
Agora alteremos o objeto paises
com intuito de eliminar os espaços excedentes:
paises <- paises %>%
mutate(pais_port = str_squish(pais_port))
Para este exercício suponhamos que queremos substituir a pontuação ponto e vírgula e ponto de interrogação (?), respectivamente, pelos termos “- observação1” e “- observação2”. Trata-se de um exercício apenas para fins didáticos.
Abaixo identificamos as observações com os padrões “;” e “?” na coluna “pais_port”:
padrao1 <- paises %>%
filter(str_detect(pais_port, ";")) %>%
pull(pais_port) %>%
.[1:10]
padrao1
[1] "Afeganistão ;" "Angola ;"
[3] "Anguila ;" "Ilhas Aland ;"
[5] "Albânia ;" "Emirados Árabes Unidos ;"
[7] "Argentina ;" "Armênia ;"
[9] "Samoa Americana ;" "Antártica ;"
Devemos escapar a interrogação com duas contrabarras, uma vez que esse sinal de pontuação também é, como o ponto, uma expressão do regex.
padrao2 <- paises %>%
filter(str_detect(pais_port, "\\?")) %>%
pull(pais_port) %>%
.[1:10]
padrao2
[1] "Panamá ; ?" "Islândia ?" "Cingapura ; ?" "Jamaica ; ?"
[5] "China ?" "Turquia ?" "Comores ?" "Honduras ; ?"
[9] "Guadalupe ; ?" "Estônia ; ?"
Agora vejamos a função que nos poderá ajudar com este exercício. Ela se chama str_replace
. No primeiro argumento da função inserimos o vetor que desejamos modificar. No segundo argumento inserimos o padrão que queremos localizar e substituir. Por fim, no terceiro argumento inserimos o padrão de substituição.
str_replace(padrao1, ";", "- observação1")
[1] "Afeganistão - observação1"
[2] "Angola - observação1"
[3] "Anguila - observação1"
[4] "Ilhas Aland - observação1"
[5] "Albânia - observação1"
[6] "Emirados Árabes Unidos - observação1"
[7] "Argentina - observação1"
[8] "Armênia - observação1"
[9] "Samoa Americana - observação1"
[10] "Antártica - observação1"
Como no objeto padrao2
existem dois termos, devemos fazer duas substituições por meio da função str_replace
.
str_replace(padrao2, "\\?", "- observação2") %>%
str_replace(";", "- observacao1")
[1] "Panamá - observacao1 - observação2"
[2] "Islândia - observação2"
[3] "Cingapura - observacao1 - observação2"
[4] "Jamaica - observacao1 - observação2"
[5] "China - observação2"
[6] "Turquia - observação2"
[7] "Comores - observação2"
[8] "Honduras - observacao1 - observação2"
[9] "Guadalupe - observacao1 - observação2"
[10] "Estônia - observacao1 - observação2"
A função str_replace
apenas substitui o primeiro padrão encontrado. Se houvesse mais padrões do mesmo tipo, a função não faria a substituição dos termos excedentes. Para substituir todos os padrões que tivessem a especificação inserida seria necessário utilizar a função str_replace_all
(de forma semelhante à função str_remove_all
)
Alteremos o objeto paises
para incorporar as mudanças propostas neste exercício e vejamos como ficaram os dados da coluna pais_port.
paises <- paises %>%
mutate(pais_port = str_replace(pais_port, ";", "- observação1") %>%
str_replace("\\?", "- observação2"))
paises %>%
select(ISOA, pais_port) %>%
rmarkdown::paged_table()
Utilizemos um pouco mais de regex para deixar apenas os nomes dos países na coluna “pais_port”. Como podemos fazer isso? Uma solução, dentre várias, é fazer o seguinte:
paises %>%
mutate(pais_port = str_remove(pais_port, " -.+")) %>%
select(ISOA, pais_port) %>%
rmarkdown::paged_table()
Explicando o padrão regex acima: o termo " -" localiza um espaço e um hífen. Por construção, sabemos que esses termos separam o fim do nome do país dos termos “observação1” e “observação2”. Como visto anteriormente o “.” pode representar letras e espaços, além de outros caracteres, e o “+” significa que queremos que o termo imediatamente anterior esteja presente uma ou mais vezes. Assim, " -.+" captura toda a parte do texto que queremos eliminar.
Dica:
Uma função útil para ver o que uma construção regex captura a partir de um padrão inserido é a função
str_view
. Para usar essa função você precisa instalar o pacotehtmlwidgets
.
Vejamos:
paises %>%
pull(pais_port) %>%
.[1:10] %>%
str_view(" -.")
Como podemos observar, o padrão acima apenas captura " - ". E o padrão abaixo captura tudo após o fim do nome do país como já havíamos explicado.
paises %>%
pull(pais_port) %>%
.[1:10] %>%
str_view(" -.+")
For attribution, please cite this work as
Cleaver (2019, Nov. 6). Fulljoin: Manipulação de texto com stringr - parte III. Retrieved from https://www.fulljoin.com.br/posts/2019-11-06-manipulao-de-texto-com-stringr-parte-iii/
BibTeX citation
@misc{cleaver2019manipulação, author = {Cleaver, Miguel}, title = {Fulljoin: Manipulação de texto com stringr - parte III}, url = {https://www.fulljoin.com.br/posts/2019-11-06-manipulao-de-texto-com-stringr-parte-iii/}, year = {2019} }