14 Strings
14.1 Introdução
Até agora, você utilizou várias cadeias de caracteres (strings) sem compreender os detalhes por trás delas. Contudo, chegou o momento de explorar as strings, compreender o seu funcionamento e dominar algumas das poderosas ferramentas de manipulação de strings que estão à sua disposição.
Vamos iniciar abordando os aspectos envolvidos na criação de strings e vetores de caracteres. Você aprenderá a gerar strings a partir de dados e, em seguida, o inverso: extrair strings dos dados. Em seguida, discutiremos sobre ferramentas que funcionam com letras individuais. Por fim, o capítulo explora funções que lidam com letras individuais e apresenta uma breve discussão sobre como as premissas do inglês podem induzir ao erro ao trabalhar com outros idiomas.
No próximo capítulo, continuaremos trabalhando com strings, mas você aprenderá mais sobre o poder das expressões regulares.
14.1.1 Pré-requisitos
Neste capítulo, vamos utilizar funções do pacote stringr, que faz parte do ecossistema do tidyverse. Também utilizaremos a base de dados bebes do pacote dados, pois ela oferece algumas strings divertidas para a manipulação.
Você facilmente reconhecerá quando estiver utilizando uma função do stringr, pois todas as funções do pacote iniciam com str_
. Essa característica é particularmente útil se você estiver utilizando o RStudio, pois a funcionalidade de autocompletar permitirá que você se lembre das funções disponíveis ao digitar str_.
14.2 Criando uma string
Em outras seções do livro, nós criamos strings de forma rápida, porém não abordamos os detalhes. Em primeiro lugar, você pode criar uma string utilizando tanto aspas simples ('
) quanto aspas duplas ("
). Não há qualquer distinção entre as duas formas. Entretanto, para uma maior consistência, o guia de estilo do tidyverse sugere o uso de "
, a menos que a string contenha múltiplas "
.
string1 <- "Esta é uma string"
string2 <- 'Se eu quiser incluir "aspas" em uma string, uso aspas simpes'
Caso esqueça de fechar aspas, você verá +
, o qual indica à continuação do prompt:
> "Esta é uma string sem fechar aspas
+
+
+ SOCORRO, ESTOU PRESO EM UMA STRING
Se isso acontecer com você e não conseguir descobrir qual aspas fechar, pressione a tecla Escape (Esc) para cancelar e tente novamente.
14.2.1 Caracteres de escape
Para incluir literalmente uma aspas simples ou duplas em uma string, você pode utilizar \
para “escapar” do significado reservado e inserir o caractere de modo literal:
aspas_duplas <- "\"" # ou '"'
aspas_simples <- '\'' # ou "'"
Assim, se você desejar incluir uma barra invertida literal em sua string, será necessário escapar do significado reservado: "\\"
:
barra_invertida <- "\\"
Observe que a representação impressa de uma string não é o mesmo que a própria string, pois a representação impressa mostra os escapes (em outras palavras, quando você imprime uma string, pode copiar e colar a saída para recriar essa string). Para visualizar o conteúdo bruto da string, use str_view()
1:
14.2.2 Strings brutas
Criar uma string com várias aspas ou barras invertidas pode se tornar uma tarefa complexa rapidamente. Para ilustrar o problema, vamos criar uma string que contenha o conteúdo do bloco de código que definimos as variáveis aspas_duplas
e aspas_simples
.
string_complexa <- "aspas_duplas <- \"\\\"\" # ou '\"'
aspas_simples <- '\\'' # ou \"'\""
str_view(string_complexa)
#> [1] │ aspas_duplas <- "\"" # ou '"'
#> │ aspas_simples <- '\'' # ou "'"
Realmente, são muitas barras invertidas! (Isso é comumente chamado de leaning toothpick syndrome.) Para eliminar a necessidade de escapar de caracteres com significado reservado, você pode utilizar uma string bruta2:
string_complexa <- r"(aspas_duplas <- "\"" # ou '"'
aspas_simples <- '\'' # ou "'")"
str_view(string_complexa)
#> [1] │ aspas_duplas <- "\"" # ou '"'
#> │ aspas_simples <- '\'' # ou "'"
Uma string bruta geralmente começa com r"(
e termina com )"
. Mas, se a sua string contém )"
, você pode usar r"[]"
ou r"{}"
. Se isso ainda não for suficiente, você pode inserir hífens adicionais para tornar os pares de abertura e fechamento únicos, por exemplo, r"--()--"
, r"---()---"
, etc. As strings brutas são flexíveis o suficiente para lidar com qualquer texto.
14.2.3 Outros caracteres especiais
Além de \"
, \'
e \\
, existem alguns outros caracteres especiais que podem ser úteis. Os mais comuns são \n
, que representa uma nova linha, e \t
, que representa uma tabulação. Às vezes, você também verá strings contendo escapes Unicode que começam com \u
ou \U
. Essa é uma forma de escrever caracteres não ingleses, mas que funcionam em todos os sistemas. Você pode ver a lista completa de outros caracteres especiais em ?Quotes
.
Observe que str_view()
utiliza chaves para tabulações, facilitando a identificação delas3. Um dos desafios ao trabalhar com texto é a variedade de formas pelas quais espaços em branco podem surgir. Portanto, esse comportamento da função pode ajudar você a reconhecer quando algo incomum está ocorrendo.
14.2.4 Exercícios
-
Crie strings que contenham os seguintes valores:
Ele disse: "Isso é incrível!"
\a\b\c\d
\\\\\\
-
Crie a string na sua sessão do R e imprima-a. O que acontece com o caractere especial “\u00a0”? Como a função
str_view()
o exibe? Você poderia fazer uma busca rápida no Google para descobrir o que é esse caractere especial?x <- "Isso\u00a0é\u00a0complexo"
14.3 Criando várias strings a partir de dados
Agora que você já sabe o básico sobre como criar uma ou duas strings “manualmente”, vamos conhecer os detalhes de como criar strings a partir de outras strings. Isso ajudará você a resolver o problema comum de ter algum texto escrito que deseja combinar com strings de um data frame. Por exemplo, você pode combinar “Olá” com uma variável nome
para criar uma saudação. Mostraremos como fazer isso com as funções str_c()
e str_glue()
e como você pode usá-las com a função mutate()
. Isso naturalmente levanta a questão de quais funções do pacote stringr você poderia utilizar com summarize()
, então finalizaremos esta seção com uma discussão sobre str_flatten()
, uma função de sumarização para strings.
14.3.1 str_c()
A função str_c()
recebe qualquer número de vetores como argumentos e retorna um vetor de caracteres:
str_c()
é muito similar à função base paste0()
, mas foi implementada para ser utilizada com mutate()
, seguindo as regras usuais do tidyverse para reciclagem e propagação de valores faltantes (missing values):
Se você deseja que os valores faltantes (missing values) sejam exibidos de outra forma, use coalesce()
para substituí-los. Neste caso, você pode usar a função dentro ou fora da função str_c()
:
df |>
mutate(
saudacao1 = str_c("Oi ", coalesce(nome, "para você"), "!"),
saudacao2 = coalesce(str_c("Oi ", nome, "!"), "Oi!")
)
#> # A tibble: 4 × 3
#> nome saudacao1 saudacao2
#> <chr> <chr> <chr>
#> 1 Flora Oi Flora! Oi Flora!
#> 2 David Oi David! Oi David!
#> 3 Terra Oi Terra! Oi Terra!
#> 4 <NA> Oi para você! Oi!
14.3.2 str_glue()
Se você estiver combinando muitas strings fixas e variáveis com str_c()
, vai perceber que digitar muitas "
s torna difícil visualizar o objetivo geral do código. Uma abordagem alternativa é fornecida pela função str_glue()
4 do pacote glue. Você fornece a função uma única string que possui uma característica especial: tudo dentro de {}
será avaliado como se estivesse fora das aspas:
Como você pode ver, a função str_glue()
atualmente converte valores faltantes (missing values) na string "NA"
, o que, infelizmente, torna-a inconsistente com a função str_c()
.
Você também pode estar se perguntando o que acontece se precisar incluir {
ou }
em sua string. Se você supôs que precisará escapar, de alguma forma, do significado reservado, você está no caminho certo. O truque é que o pacote glue utiliza uma técnica de escape ligeiramente diferente: em vez de prefixar com um caractere especial como \
, você deve duplicar os caracteres especiais:
14.3.3 str_flatten()
As funções str_c()
e str_glue()
funcionam bem com mutate()
porque suas saídas têm o mesmo comprimento que suas entradas. Mas, se você quisesse uma função que funcionasse bem com summarize()
, isto é, que sempre retornasse uma única string? Neste caso, str_flatten()
5 é a função adequada para essa tarefa: ela recebe um vetor de caracteres e concatena cada elemento do vetor em uma única string:
str_flatten(c("x", "y", "z"))
#> [1] "xyz"
str_flatten(c("x", "y", "z"), ", ")
#> [1] "x, y, z"
str_flatten(c("x", "y", "z"), ", ", last = " e ")
#> [1] "x, y e z"
Essa função funciona bem com summarize()
:
df <- tribble(
~ nome, ~ fruta,
"Carmen", "banana",
"Carmen", "maça",
"Marvin", "nectarina",
"Terence", "melão",
"Terence", "mamão",
"Terence", "tangerina"
)
df |>
group_by(nome) |>
summarize(frutas = str_flatten(fruta, ", "))
#> # A tibble: 3 × 2
#> nome frutas
#> <chr> <chr>
#> 1 Carmen banana, maça
#> 2 Marvin nectarina
#> 3 Terence melão, mamão, tangerina
14.3.4 Exercícios
-
Compare e contraste os resultados de
paste0()
comstr_c()
para as seguintes entradas: Qual é a diferença entre
paste()
epaste0()
? Como você pode criar o equivalente da funçãopaste()
comstr_c()
?-
Converta as seguintes expressões de
str_c()
parastr_glue()
ou vice-versa:str_c("O preço do(a) ", alimento, " é ", preco)
str_glue("Eu tenho {idade} anos e moro no(a) {pais}")
str_c("\\seção{", titulo, "}")
14.4 Extraindo dados de strings
É muito comum que múltiplas variáveis sejam agrupadas em uma única string. Nesta seção, você aprenderá a como usar quatro funções do tidyr para extrair múltiplas variáveis:
df |> separate_longer_delim(col, delim)
df |> separate_longer_position(col, width)
df |> separate_wider_delim(col, delim, names)
df |> separate_wider_position(col, widths)
Se você observar atentamente, poderá perceber que há um padrão comum entre os nomes das funções: separate_
, seguido por longer
ou wider
, depois _
, e, por fim, delim
ou position
. Isso se deve ao fato de que essas quatro funções são baseadas em duas primitivas mais simples:
- Assim como com
pivot_longer()
epivot_wider()
, as funções com terminação_longer
tornam o data frame de entrada mais longo, criando novas linhas, enquanto as funções com terminação_wider
tornam o data frame de entrada mais largo, gerando novas colunas. -
delim
divide uma string por um delimitador, como", "
ou" "
;position
divide em larguras específicas, comoc(3, 5, 2)
.
Falaremos sobre o último membro dessa família, separate_wider_regex()
, no Capítulo 15. Essa é a mais flexível das funções wider
, mas você precisa saber um pouco sobre expressões regulares antes para poder usá-la.
As duas seções a seguir fornecerão a ideia básica por trás dessas funções para separação, primeiro separando em linhas (o que é um pouco mais simples) e, depois, separando em colunas. Finalizaremos discutindo as ferramentas que as funções wider
fornecem para diagnosticar problemas.
14.4.1 Separando em linhas
Separar uma string em linhas geralmente é mais útil quando o número de componentes varia de linha para linha. O caso mais comum é utilizar separate_longer_delim()
para dividir com base em um delimitador:
df1 <- tibble(x = c("a,b,c", "d,e", "f"))
df1 |>
separate_longer_delim(x, delim = ",")
#> # A tibble: 6 × 1
#> x
#> <chr>
#> 1 a
#> 2 b
#> 3 c
#> 4 d
#> 5 e
#> 6 f
É mais raro empregar separate_longer_position()
na prática, mas alguns conjuntos de dados mais antigos utilizam um formato bastante compacto, no qual cada caractere é empregado para registrar um valor:
df2 <- tibble(x = c("1211", "131", "21"))
df2 |>
separate_longer_position(x, width = 1)
#> # A tibble: 9 × 1
#> x
#> <chr>
#> 1 1
#> 2 2
#> 3 1
#> 4 1
#> 5 1
#> 6 3
#> # ℹ 3 more rows
14.4.2 Separando em colunas
Separar uma string em colunas tende a ser mais útil quando há um número fixo de componentes em cada string e você deseja distribuí-los em colunas. No entanto, as funções empregadas nesse caso são ligeiramente mais complicadas do que suas equivalentes longer
porque você precisa nomear as colunas. Por exemplo, no conjunto de dados a seguir, x
é composto por um código, um número de edição e um ano, estando separados por "."
. Para utilizar a função separate_wider_delim()
, fornecemos o delimitador e os nomes em dois argumentos distintos:
df3 <- tibble(x = c("a10.1.2022", "b10.2.2011", "e15.1.2015"))
df3 |>
separate_wider_delim(
x,
delim = ".",
names = c("código", "edição", "ano")
)
#> # A tibble: 3 × 3
#> código edição ano
#> <chr> <chr> <chr>
#> 1 a10 1 2022
#> 2 b10 2 2011
#> 3 e15 1 2015
Se uma variável em particular não for útil, você pode utilizar NA
, no lugar do nome, para omiti-la dos resultados:
df3 |>
separate_wider_delim(
x,
delim = ".",
names = c("código", NA, "ano")
)
#> # A tibble: 3 × 2
#> código ano
#> <chr> <chr>
#> 1 a10 2022
#> 2 b10 2011
#> 3 e15 2015
A função separate_wider_position()
funciona de forma um pouco diferente, pois normalmente você deseja especificar a largura de cada coluna. Então, você deve fornecer à função um vetor de inteiros nomeado, em que o nome corresponde ao nome da nova coluna e o valor é a quantidade de caracteres que constitui a string correspondente. Você pode omitir valores da saída ao não nomeá-los:
df4 <- tibble(x = c("202215TX", "202122LA", "202325CA"))
df4 |>
separate_wider_position(
x,
widths = c(ano = 4, idade = 2, estado = 2)
)
#> # A tibble: 3 × 3
#> ano idade estado
#> <chr> <chr> <chr>
#> 1 2022 15 TX
#> 2 2021 22 LA
#> 3 2023 25 CA
14.4.3 Diagnosticando problemas de alargamento
A função separate_wider_delim()
6 requer um conjunto fixo e conhecido de colunas. Mas, o que acontece se algumas das linhas não tiverem o número esperado de caracteres? Nesse caso, existem dois problemas possíveis, poucos ou muitos caracteres, e a função separate_wider_delim()
fornece dois argumentos para ajudar: too_few
e too_many
. Primeiramente, vamos olhar para o caso de too_few
com o seguinte conjunto de dados de exemplo:
df <- tibble(x = c("1-1-1", "1-1-2", "1-3", "1-3-2", "1"))
df |>
separate_wider_delim(
x,
delim = "-",
names = c("x", "y", "z")
)
#> Error in `separate_wider_delim()`:
#> ! Expected 3 pieces in each element of `x`.
#> ! 2 values were too short.
#> ℹ Use `too_few = "debug"` to diagnose the problem.
#> ℹ Use `too_few = "align_start"/"align_end"` to silence this message.
Você notará que obtemos um erro, mas o erro nos dá algumas sugestões sobre como podemos proceder. Vamos começar depurando o problema:
debug <- df |>
separate_wider_delim(
x,
delim = "-",
names = c("x", "y", "z"),
too_few = "debug"
)
#> Warning: Debug mode activated: adding variables `x_ok`, `x_pieces`, and
#> `x_remainder`.
debug
#> # A tibble: 5 × 6
#> x y z x_ok x_pieces x_remainder
#> <chr> <chr> <chr> <lgl> <int> <chr>
#> 1 1-1-1 1 1 TRUE 3 ""
#> 2 1-1-2 1 2 TRUE 3 ""
#> 3 1-3 3 <NA> FALSE 2 ""
#> 4 1-3-2 3 2 TRUE 3 ""
#> 5 1 <NA> <NA> FALSE 1 ""
Ao usar o modo de depuração, você observará que a saída contém três colunas extras: x_ok
, x_pieces
e x_remainder
(se você separar uma variável com um nome diferente, obterá um prefixo diferente). Aqui, x_ok
permite que você encontre rapidamente as entradas que falharam:
debug |> filter(!x_ok)
#> # A tibble: 2 × 6
#> x y z x_ok x_pieces x_remainder
#> <chr> <chr> <chr> <lgl> <int> <chr>
#> 1 1-3 3 <NA> FALSE 2 ""
#> 2 1 <NA> <NA> FALSE 1 ""
A coluna x_pieces
nos informa quantas partes foram encontradas, comparadas com o número esperado de 3 (o comprimento de names
). Já a coluna x_remainder
não é útil quando há poucas partes, mas a veremos novamente em breve.
Às vezes, analisar essas informações de depuração revelará a você problemas com sua escolha de delimitador ou mostrará que é ainda necessário fazer algum pré-processamento antes de separar. Nesse caso, corrija o problema raiz e certifique-se de remover too_few = "debug"
para garantir que novos problemas se tornem erros quando a função for executada.
Em outros casos, você pode querer preencher as partes ausentes com NA
s e seguir em frente. Nesse caso, isso pode ser feito por too_few = "align_start"
e too_few = "align_end"
, que permitem controlar onde os NA
s devem ser inseridos:
df |>
separate_wider_delim(
x,
delim = "-",
names = c("x", "y", "z"),
too_few = "align_start"
)
#> # A tibble: 5 × 3
#> x y z
#> <chr> <chr> <chr>
#> 1 1 1 1
#> 2 1 1 2
#> 3 1 3 <NA>
#> 4 1 3 2
#> 5 1 <NA> <NA>
O mesmo é válido se você tiver um número excessivo de caracteres:
df <- tibble(x = c("1-1-1", "1-1-2", "1-3-5-6", "1-3-2", "1-3-5-7-9"))
df |>
separate_wider_delim(
x,
delim = "-",
names = c("x", "y", "z")
)
#> Error in `separate_wider_delim()`:
#> ! Expected 3 pieces in each element of `x`.
#> ! 2 values were too long.
#> ℹ Use `too_many = "debug"` to diagnose the problem.
#> ℹ Use `too_many = "drop"/"merge"` to silence this message.
Agora, ao fazer a depuração, você perceberá a importância de x_remainder
:
debug <- df |>
separate_wider_delim(
x,
delim = "-",
names = c("x", "y", "z"),
too_many = "debug"
)
#> Warning: Debug mode activated: adding variables `x_ok`, `x_pieces`, and
#> `x_remainder`.
debug |> filter(!x_ok)
#> # A tibble: 2 × 6
#> x y z x_ok x_pieces x_remainder
#> <chr> <chr> <chr> <lgl> <int> <chr>
#> 1 1-3-5-6 3 5 FALSE 4 -6
#> 2 1-3-5-7-9 3 5 FALSE 5 -7-9
Você tem um conjunto ligeiramente diferente de opções para lidar com um número excessivo de partes: você pode simplesmente “descartar” silenciosamente os caracteres adicionais ou “unir” todos eles na última coluna:
df |>
separate_wider_delim(
x,
delim = "-",
names = c("x", "y", "z"),
too_many = "drop"
)
#> # A tibble: 5 × 3
#> x y z
#> <chr> <chr> <chr>
#> 1 1 1 1
#> 2 1 1 2
#> 3 1 3 5
#> 4 1 3 2
#> 5 1 3 5
df |>
separate_wider_delim(
x,
delim = "-",
names = c("x", "y", "z"),
too_many = "merge"
)
#> # A tibble: 5 × 3
#> x y z
#> <chr> <chr> <chr>
#> 1 1 1 1
#> 2 1 1 2
#> 3 1 3 5-6
#> 4 1 3 2
#> 5 1 3 5-7-9
14.5 Letras
Nesta seção, vamos apresentar funções que permitem trabalhar com letras individuais em uma string. Você aprenderá como encontrar o comprimento de uma string, extrair substrings e lidar com strings longas em gráficos e tabelas.
14.5.1 Comprimento
A função str_length()
informa o número de letras presentes na string:
str_length(c("a", "R para ciência de dados", NA))
#> [1] 1 23 NA
Você poderia usar essa função em conjunto com count()
para encontrar a distribuição dos comprimentos dos nomes de bebês dos EUA e, em seguida, usar filter()
para ver os nomes mais longos, que têm 15 letras7:
bebes |>
count(n_letras = str_length(nome), wt = n)
#> # A tibble: 14 × 2
#> n_letras n
#> <int> <int>
#> 1 2 338150
#> 2 3 8589596
#> 3 4 48506739
#> 4 5 87011607
#> 5 6 90749404
#> 6 7 72120767
#> # ℹ 8 more rows
bebes |>
filter(str_length(nome) == 15) |>
count(nome, wt = n, sort = TRUE)
#> # A tibble: 34 × 2
#> nome n
#> <chr> <int>
#> 1 Franciscojavier 123
#> 2 Christopherjohn 118
#> 3 Johnchristopher 118
#> 4 Christopherjame 108
#> 5 Christophermich 52
#> 6 Ryanchristopher 45
#> # ℹ 28 more rows
14.5.2 Subconjunto
Você pode extrair partes de uma string utilizando str_sub(string, start, end)
, em que start
e end
são as posições onde a substring deve começar e terminar. Os argumentos start
e end
são inclusivos, então o comprimento da string resultante será end - start + 1
:
Você pode usar valores negativos para contar a partir do final da string: -1 é o último caractere, -2 é o penúltimo caractere e assim por diante.
str_sub(x, -3, -1)
#> [1] "aça" "ana" "era"
Note que str_sub()
não falhará se a string for muito curta: a função simplesmente retornará a quantidade máxima de letras possível:
str_sub("a", 1, 5)
#> [1] "a"
Poderíamos utilizar a função str_sub()
com mutate()
para encontrar a primeira e a última letra de cada nome:
bebes |>
mutate(
primeira = str_sub(nome, 1, 1),
ultima = str_sub(nome, -1, -1)
)
#> # A tibble: 1,924,665 × 7
#> ano sexo nome n prop primeira ultima
#> <dbl> <chr> <chr> <int> <dbl> <chr> <chr>
#> 1 1880 F Mary 7065 0.0724 M y
#> 2 1880 F Anna 2604 0.0267 A a
#> 3 1880 F Emma 2003 0.0205 E a
#> 4 1880 F Elizabeth 1939 0.0199 E h
#> 5 1880 F Minnie 1746 0.0179 M e
#> 6 1880 F Margaret 1578 0.0162 M t
#> # ℹ 1,924,659 more rows
14.5.3 Exercícios
- Ao calcular a distribuição do comprimento dos nomes de bebês, por que usamos
wt = n
? - Utilize
str_length()
estr_sub()
para extrair a letra do meio de cada nome de bebê. O que você pode fazer se a string tiver um número par de caracteres? - Existem tendências significativas no comprimento dos nomes de bebês ao longo do tempo? E quanto à popularidade das primeiras e últimas letras?
14.6 Texto em outro idioma
Até agora, focamos em características de textos em inglês, os quais são particularmente fáceis de trabalhar por duas razões. Em primeiro lugar, o alfabeto em inglês é relativamente simples: há apenas 26 letras. Em segundo lugar (e talvez o mais importante), a infraestrutura computacional que usamos atualmente foi predominantemente projetada por falantes de inglês. Infelizmente, não conseguiremos abordar com profundidade outros idiomas que não sejam o inglês. Mesmo assim, gostaríamos de chamar sua atenção para alguns dos maiores desafios que você pode encontrar: codificação de caracteres, variações de letras e funções dependentes de localidade.
14.6.1 Codificação de caracteres
Ao trabalhar com textos que não sejam em inglês, o primeiro desafio geralmente é a codificação de caracteres (encoding). Para entender o que está acontecendo, precisamos compreender primeiro como os computadores representam as strings. No R, podemos acessar a representação implícita de uma string usando charToRaw()
:
charToRaw("Hadley")
#> [1] 48 61 64 6c 65 79
Cada um desses seis números hexadecimais representa uma letra: 48
é H, 61
é a, e assim por diante. A correspondência do número hexadecimal com o caractere é chamada de codificação de caracteres (encoding) e, neste caso, a codificação é chamada de ASCII. ASCII faz um excelente trabalho ao representar caracteres em inglês porque é o Código Padrão Americano para o Intercâmbio de Informações (American Standard Code for Information Interchange).
Mas não é tão simples para idiomas diferentes do inglês. Nos primórdios da computação, existiam muitos padrões concorrentes para a codificação de caracteres não ingleses. Por exemplo, havia duas codificações diferentes para a Europa: o Latin1 (também conhecido como ISO-8859-1) era utilizado para idiomas da Europa Ocidental e o Latin2 (também conhecido como ISO-8859-2) era utilizado para idiomas da Europa Central. No Latin1, o byte b1
representa “±”, mas, no Latin2, representa “ą”! Felizmente, hoje existe um padrão que é suportado praticamente em todos os lugares: UTF-8. O padrão UTF-8 pode codificar praticamente todos os caracteres usados atualmente e muitos símbolos extras, como emojis.
O pacote readr utiliza o padrão UTF-8. Essa é uma configuração padrão boa, mas pode falhar para dados produzidos por sistemas mais antigos que não utilizam o UTF-8. Se isso acontecer, suas strings serão exibidas de forma estranha ao imprimi-las. Às vezes, apenas um ou dois caracteres podem estar incorretos. Em outras situações, você pode obter um conjunto de caracteres completamente sem sentido. Por exemplo, aqui estão dois CSVs com codificações incomuns8:
Para lê-los corretamente, você deve especificar a codificação por meio do argumento locale
:
Como encontrar a codificação correta? Se tiver sorte, ela estará incluída em algum lugar na documentação dos dados. Infelizmente, isso raramente é o caso, então o pacote readr fornece a função guess_encoding()
para ajudar você a descobrir. Ela não é infalível e tem melhor desempenho quando o texto é mais extenso (diferente do caso atual), porém é um bom ponto de partida. É provável que você precise experimentar várias codificações diferentes antes de encontrar a correta.
As codificações são um tema rico e complexo; apenas começamos a explorá-la aqui. Se você deseja aprender mais, recomendamos ler a explicação detalhada em http://kunststube.net/encoding/.
14.6.2 Variações de letras
Trabalhar com idiomas que possuem acentos representa um desafio significativo ao determinar a posição das letras (por exemplo, com str_length()
e str_sub()
), pois as letras acentuadas podem ser codificadas de duas formas: como um único caractere individual (por exemplo, ü) ou como dois caracteres, combinando uma letra sem acento (por exemplo, u) com um diacrítico (por exemplo, ¨). Por exemplo, este código mostra duas maneiras de representar ü que aparentam ser idênticas:
No entanto, as strings diferem em comprimento e seus caracteres iniciais são diferentes:
str_length(u)
#> [1] 1 2
str_sub(u, 1, 1)
#> [1] "ü" "u"
Por fim, observe que uma comparação dessas strings com ==
as interpreta como diferentes, enquanto a função str_equal()
do pacote stringr reconhece que ambas têm a mesma aparência:
u[[1]] == u[[2]]
#> [1] FALSE
str_equal(u[[1]], u[[2]])
#> [1] TRUE
14.6.3 Funções dependentes de localidade
Por fim, existem algumas funções do pacote stringr cujo comportamento depende da sua localidade. Uma localidade é semelhante a um idioma, mas inclui um especificador opcional de região para lidar com variações regionais dentro de um idioma. Uma localidade é especificada por uma abreviação do idioma em minúsculo, seguida opcionalmente por um _
e um identificador da região em maiúsculo. Por exemplo, “en” corresponde ao inglês, “en_GB” ao inglês britânico e “en_US” ao inglês americano. Se você ainda não conhece o código para o seu idioma, o Wikipedia possui uma boa lista e você pode verificar quais são suportados pelo pacote stringr empregando a função stringi::stri_locale_list()
.
As funções de string do R base usam automaticamente a localidade definida pelo seu sistema operacional. Isso significa que as funções de string do R base fazem o que você espera para o seu idioma, mas seu código pode funcionar de maneira diferente se for compartilhado com alguém que vive em um país diferente. Para evitar esse problema, o stringr utiliza, por padrão, as regras do inglês, usando a localidade como “en”, e exige que você especifique o argumento locale
para substituir a localidade. Felizmente, há dois conjuntos de funções onde a localidade realmente importa: a função para modificar a caixa das letras e a função para ordenar as letras.
As regras para alterar maiúsculas e minúsculas diferem entre os idiomas. Por exemplo, o turco possui dois i’s: com e sem um ponto. Como são duas letras distintas, elas são capitalizadas de forma diferente:
str_to_upper(c("i", "ı"))
#> [1] "I" "I"
str_to_upper(c("i", "ı"), locale = "tr")
#> [1] "İ" "I"
Ordenar strings depende da ordem alfabética, mas essa ordem não é a mesma em todos os idiomas9! Aqui, está um exemplo: em tcheco, “ch” é uma letra composta que aparece após o h
no alfabeto.
Isso também é relevante ao ordenar strings com dplyr::arrange()
, o que justifica o fato dessa função também possuir um argumento locale
.
14.7 Resumo
Neste capítulo, você aprendeu sobre o poder do pacote stringr: como criar, combinar e extrair strings, além de entender alguns dos desafios que podem surgir com strings em idiomas diferentes do inglês. Agora, é hora de aprender uma das ferramentas mais importantes e poderosas para trabalhar com strings: as expressões regulares. Expressões regulares são uma linguagem concisa, porém expressiva, para descrever padrões em strings e são o tema do próximo capítulo.
Ou use a função base do R
writeLines()
.↩︎Disponível no R 4.0.0 e versões posteriores.↩︎
str_view()
também utiliza cores para chamar a sua atenção para tabulações, espaços, correspondências, etc. As cores atualmente não são exibidas no livro, mas você as notará ao executar o código de forma interativa.↩︎Se não estiver usando o pacote stringr, você também pode acessá-la diretamente com
glue::glue()
.↩︎O equivalente no R base é a função
paste()
utilizada com o argumentocollapse
.↩︎Os mesmos princípios se aplicam às funções
separate_wider_position()
eseparate_wider_regex()
.↩︎Olhando para essas entradas, nós deduziríamos que a base de dados referentes aos nomes de bebês eliminam espaços ou hífens e truncam após 15 letras.↩︎
Aqui, o caractere especial
\x
é utilizado para codificar dados binários diretamente em uma string.↩︎Ordenar em idiomas que não possuem um alfabeto, como o chinês, é ainda mais complicado.↩︎