Na quarta parte da série de posts sobre manipulação de texto com stringr veremos como usar funções um pouco mais técnicas. Também discutimos o caso da função case_when do dplyr que pode ser bastante útil ao se trabalhar com texto.
Este é o quarto post da série de manipulação de texto com stringr. Já vimos as principais funções de manipulação de texto, então neste post aproveitaremos para abordar algumas funções do pacote um pouco mais técnicas como a função word
e a função str_count
, que associada a função boundary
pode nos ajudar a contar o número de palavras em um texto. Ainda falaremos da função fixed
e, por fim, falaremos da função case_when
do dplyr que pode vir a ser bastante útil na tarefa de manipular texto.
Este post segue a mesma estrutura dos posts anteriores sobre manipulação de texto, isto é, está organizado na forma de perguntas e respostas.
library(microbenchmark)
library(dplyr)
library(stringr)
Neste caso vamos trabalhar com o objeto sentences
, disponível no pacote stringr. A partir do objeto sentences
aproveitamos para criar uma tabela simples de duas colunas com a função tibble
.
data(sentences)
df <- tibble(id = 1:length(sentences), sentences = sentences)
head(df)
# A tibble: 6 x 2
id sentences
<int> <chr>
1 1 The birch canoe slid on the smooth planks.
2 2 Glue the sheet to the dark blue background.
3 3 It's easy to tell the depth of a well.
4 4 These days a chicken leg is a rare dish.
5 5 Rice is often served in round bowls.
6 6 The juice of lemons makes fine punch.
Já aprendemos como fazer que uma coluna tenha um tamanho fixo de caracteres (pode-se fazer isso com a função str_pad
ou str_sub
, por exemplo, a depender da situação), mas e se nós quiséssemos que uma coluna tivesse sempre as três primeiras palavras de determinado texto? O número de caracteres seria variável e essas funções que vimos não mais nos ajudariam.
A solução é simples e podemos usar a função word
do stringr:
df %>%
mutate(tres_palavras = word(sentences, 1 , 3))
# A tibble: 720 x 3
id sentences tres_palavras
<int> <chr> <chr>
1 1 The birch canoe slid on the smooth planks. The birch canoe
2 2 Glue the sheet to the dark blue background. Glue the sheet
3 3 It's easy to tell the depth of a well. It's easy to
4 4 These days a chicken leg is a rare dish. These days a
5 5 Rice is often served in round bowls. Rice is often
6 6 The juice of lemons makes fine punch. The juice of
7 7 The box was thrown beside the parked truck. The box was
8 8 The hogs were fed chopped corn and garbage. The hogs were
9 9 Four hours of steady work faced us. Four hours of
10 10 Large size in stockings is hard to sell. Large size in
# ... with 710 more rows
Se quiséssemos as últimas 3 palavras poderíamos usar a função word da seguinte forma:
df %>%
mutate(ultimas_tres = word(sentences, -3, -1)) %>%
select(id, ultimas_tres)
# A tibble: 720 x 2
id ultimas_tres
<int> <chr>
1 1 the smooth planks.
2 2 dark blue background.
3 3 of a well.
4 4 a rare dish.
5 5 in round bowls.
6 6 makes fine punch.
7 7 the parked truck.
8 8 corn and garbage.
9 9 work faced us.
10 10 hard to sell.
# ... with 710 more rows
Para isso você vai precisar utilizar duas funções: str_count
e boundary
.
df %>%
mutate(n_palavras = str_count(sentences, boundary(type = "word")))
# A tibble: 720 x 3
id sentences n_palavras
<int> <chr> <int>
1 1 The birch canoe slid on the smooth planks. 8
2 2 Glue the sheet to the dark blue background. 8
3 3 It's easy to tell the depth of a well. 9
4 4 These days a chicken leg is a rare dish. 9
5 5 Rice is often served in round bowls. 7
6 6 The juice of lemons makes fine punch. 7
7 7 The box was thrown beside the parked truck. 8
8 8 The hogs were fed chopped corn and garbage. 8
9 9 Four hours of steady work faced us. 7
10 10 Large size in stockings is hard to sell. 8
# ... with 710 more rows
A função str_count
detecta o padrão inserido na função e conta quantas vezes esse padrão aparece em um segmento de texto. Por exemplo, str_count("o teste consiste em um teste de manhã e um teste de tarde", "teste")
buscaria o padrão “teste” e contaria quantas vezes esse padrão aparece. Nesse caso o resultado seria 3.
Já a função boundary(type = "word")
imputa um padrão de fronteira para palavras, o que permite que a função str_count
conte as palavras em um segmento de texto.
Sim, a função fixed
aumenta a velocidade do processamento de funções do stringr. Ao comparar os bytes de um texto, é possível encontrar correspondências de forma veloz, mas isso somente pode ser usado para um conjunto literal de caracteres ASCII. Em outras palavras, nós não poderíamos fazer uma busca regex do tipo “[0-9]{1,2}” para localizar o padrão “90” ou “22”. Ao utilizar a função fixed com o input “[0-9]{1,2}” só haveria correspondência se esse texto aparecesse exatamente dessa forma (por exemplo, para o trecho “o padrão pode ser [0-9]{1,2}” haveria correspondência).
Para mostrar que a função fixed
acelera funções do stringr, iremos fazer uma operação de filtro simples para obter frases com o padrão “the” e usaremos o pacote microbenchmark
para medir a velocidade com e sem a função fixed
(a unidade de tempo abaixo está em microssegundos).
microbenchmark(sem_fixed = df %>%
filter(str_detect(sentences, "the")),
com_fixed = df %>%
filter(str_detect(sentences, fixed("the"))))
Unit: microseconds
expr min lq mean median uq max neval
sem_fixed 690.4 823.00 1092.103 995.20 1185.85 3714.9 100
com_fixed 478.0 545.85 777.342 679.05 768.60 4101.0 100
Com efeito, podemos observar que a utilização da função fixed
torna o filtro simples mais rápido. Para o exemplo trabalhado, não há diferença prática, pois há poucas observações e a diferença de tempo em microssegundos é imperceptível para nós, mas à medida que o tamanho de observações cresce, a função pode ter grande utilidade ao aumentar a velocidade da localização de correspondências.
A função case_when
do dplyr permite que você edite o texto das observações de uma coluna sempre que uma condição for atendida. Para exemplificar o uso da função case_when
vamos trabalhar com os dados do objeto starwars
, disponíveis no pacote do dplyr.
data(starwars)
sw <- starwars %>%
select(name, eye_color)
sw
# A tibble: 87 x 2
name eye_color
<chr> <chr>
1 Luke Skywalker blue
2 C-3PO yellow
3 R2-D2 red
4 Darth Vader yellow
5 Leia Organa brown
6 Owen Lars blue
7 Beru Whitesun lars blue
8 R5-D4 red
9 Biggs Darklighter brown
10 Obi-Wan Kenobi blue-gray
# ... with 77 more rows
Vamos supor que nós desejamos traduzir as observações da coluna eye_color
para o português da seguinte forma: “blue” para “azul”, “yellow” para “amarelo”, “brown” para “marrom” e as demais cores que aparecem em inglês para um grupo geral chamado “outras cores”. Com algumas linhas de código é possível alterar o nome da coluna eye_color
de forma imediata:
sw %>%
mutate(eye_traducao = case_when(
eye_color == "blue" ~ "azul",
eye_color == "yellow" ~ "amarelo",
eye_color == "brown" ~ "marrom",
TRUE ~ "outras cores"
))
# A tibble: 87 x 3
name eye_color eye_traducao
<chr> <chr> <chr>
1 Luke Skywalker blue azul
2 C-3PO yellow amarelo
3 R2-D2 red outras cores
4 Darth Vader yellow amarelo
5 Leia Organa brown marrom
6 Owen Lars blue azul
7 Beru Whitesun lars blue azul
8 R5-D4 red outras cores
9 Biggs Darklighter brown marrom
10 Obi-Wan Kenobi blue-gray outras cores
# ... with 77 more rows
Trata-se de uma forma elegante de editar ou modificar colunas de texto. Note que a função case_when
acima testa diversas condições e, quando verdadeiras, a função altera o texto para o termo após o til “~” no código acima.
No caso das observações cujas condições não resultem no resultado lógico TRUE
(verdadeiro), é preciso adicionar uma linha adicional com resultado TRUE
e, após o til “~”, selecionar o termo de substituição padrão. Se isso não for feito, as observações que não resultam em condições verdadeiras serão transformadas em NA.
No nosso caso, indicamos que as cores em inglês que não sejam nem “blue”, nem “yellow” e nem “brown” deverão ser chamadas de “outras cores”. Isso se dá com a introdução do termo TRUE ~ "outras cores"
. Se quiséssemos manter os nomes originais com exceção de “blue”, “yellow” e “brown”, poderíamos usar TRUE ~ eye_color
:
sw %>%
mutate(eye_traducao_parcial = case_when(
eye_color == "blue" ~ "azul",
eye_color == "yellow" ~ "amarelo",
eye_color == "brown" ~ "marrom",
TRUE ~ eye_color
))
# A tibble: 87 x 3
name eye_color eye_traducao_parcial
<chr> <chr> <chr>
1 Luke Skywalker blue azul
2 C-3PO yellow amarelo
3 R2-D2 red red
4 Darth Vader yellow amarelo
5 Leia Organa brown marrom
6 Owen Lars blue azul
7 Beru Whitesun lars blue azul
8 R5-D4 red red
9 Biggs Darklighter brown marrom
10 Obi-Wan Kenobi blue-gray blue-gray
# ... with 77 more rows
Nestas quatro partes sobre stringr
tentamos apresentar as principais funções do pacote. Ainda há diversas outras funções de manipulação de texto (ver pacote stringi
) e algumas outras que não comentamos do pacote stringr
. Apesar disso, as funções que abordamos resolvem grande parte dos problemas de organização e limpeza textual. Além dessas funções, também é importante dominar o uso de expressões regulares (regex), pois grande parte do que é possível fazer com o stringr
reside na captura e manipulação de padrões textuais.
Uma dica valiosa para quem está começando a trabalhar com texto é a seguinte: antes de iniciar um trabalho extenso de manipulação de texto, planeje a forma mais adequada de fazer um tratamento inicial nos seus dados textuais. O tratamento ao qual nos referimos visa simplificar os padrões de regex que você terá que escrever e aumentar o controle sobre a previsibilidade dos diversos padrões que possam surgir.
Em regra, recomendaríamos passar o texto inteiro para caixa baixa ou caixa alta, assim como eliminar espaços desnecessários. Além disso, em alguns casos, também recomendaríamos eliminar os diversos acentos da língua portuguesa (ou de outras línguas, se for o caso), passando por exemplo, o “ã” para “a” e o “ó” para “o”. Ao trabalhar com dados textuais, erros de acentuação são comuns (em especial em campos de texto livre) e podem dificultar sua vida na extração de padrões que você procura.
Todavia, não existe um conjunto de regras que funcione como uma panaceia. Há casos, por exemplo, em que os diferentes espaçamentos contidos no texto irão funcionar como pontos de demarcação para extrair padrões. O mesmo pode ocorrer com a caixa original das letras. É preciso conhecer seu texto, investigar e fazer experimentos antes de implementar a abordagem de limpeza inicial.
For attribution, please cite this work as
Cleaver (2020, March 17). Fulljoin: Manipulação de texto com stringr - parte IV. Retrieved from https://www.fulljoin.com.br/posts/2020-03-02-manipulao-de-texto-com-stringr-parte-iv/
BibTeX citation
@misc{cleaver2020manipulação, author = {Cleaver, Miguel}, title = {Fulljoin: Manipulação de texto com stringr - parte IV}, url = {https://www.fulljoin.com.br/posts/2020-03-02-manipulao-de-texto-com-stringr-parte-iv/}, year = {2020} }