From 1456c6905824ee776bb6f774e20c76ff18580251 Mon Sep 17 00:00:00 2001 From: Eric Scopinho Date: Mon, 4 Dec 2023 21:07:11 -0300 Subject: [PATCH 1/8] =?UTF-8?q?Tradu=C3=A7=C3=A3o:=20regexps.qmd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- regexps.qmd | 734 ++++++++++++++++++++++++++-------------------------- 1 file changed, 367 insertions(+), 367 deletions(-) diff --git a/regexps.qmd b/regexps.qmd index 3d33048a3..7f663dae2 100644 --- a/regexps.qmd +++ b/regexps.qmd @@ -1,140 +1,140 @@ -# Regular expressions {#sec-regular-expressions} +# Expressões regulares {#sec-regular-expressions} ```{r} #| echo: false #| results: asis source("_common.R") -mensagem_capitulo_sem_traducao() + ``` -## Introduction +## Introdução -In @sec-strings, you learned a whole bunch of useful functions for working with strings. -This chapter will focus on functions that use **regular expressions**, a concise and powerful language for describing patterns within strings. -The term "regular expression" is a bit of a mouthful, so most people abbreviate it to "regex"[^regexps-1] or "regexp". +No @sec-strings, você aprendeu diversas funções úteis para trabalhar com *strings*. +Este capítulo se concentrará em funções que usam **expressões regulares**, uma linguagem concisa e poderosa para descrever padrões dentro de *strings*. +O termo "expressão regular" é um pouco longo, então a maioria das pessoas o abrevia para "regex"[^regexps-1] ou "regexp". -[^regexps-1]: You can pronounce it with either a hard-g (reg-x) or a soft-g (rej-x). +[^regexps-1]: Você pode pronunciá-lo com um g-seco (reg-x) ou um g-suave (rej-x). -The chapter starts with the basics of regular expressions and the most useful stringr functions for data analysis. -We'll then expand your knowledge of patterns and cover seven important new topics (escaping, anchoring, character classes, shorthand classes, quantifiers, precedence, and grouping). -Next, we'll talk about some of the other types of patterns that stringr functions can work with and the various "flags" that allow you to tweak the operation of regular expressions. -We'll finish with a survey of other places in the tidyverse and base R where you might use regexes. +O capítulo começa com os conceitos básicos de expressões regulares e as funções do pacote stringr mais úteis para análise de dados. +Em seguida, expandiremos seu conhecimento sobre padrões (*patterns*) e cobriremos sete novos tópicos importantes (escape, ancoragem, classes de caracteres, classes de abreviações, quantificadores, precedência de agrupamento). +A seguir, falaremos sobre alguns dos outros tipos de padrões com os quais as funções stringr podem trabalhar e os vários "sinalizadores" que permitem ajustar a operação de expressões regulares. +Terminaremos com uma pesquisa de outros lugares do tidyverse e no R base onde você pode usar expressões regulares. -### Prerequisites +### Pré-requisitos -In this chapter, we'll use regular expression functions from stringr and tidyr, both core members of the tidyverse, as well as data from the babynames package. +Neste capítulo, usaremos funções de expressão regular dos pacotes stringr e tidyr, ambos membros principais do tidyverse, bem como dados bebes do pacote dados. ```{r} #| label: setup #| message: false library(tidyverse) -library(babynames) +library(dados) ``` -Through this chapter, we'll use a mix of very simple inline examples so you can get the basic idea, the baby names data, and three character vectors from stringr: +Ao longo deste capítulo, usaremos uma combinação de exemplos muito simples em linha (*inline*) para você entender a ideia geral, o conjunto de dados bebes e três vetores de caracteres do pacote stringr que estão em inglês: -- `fruit` contains the names of 80 fruits. -- `words` contains 980 common English words. -- `sentences` contains 720 short sentences. +- `fruit` contém nomes de 80 frutas. +- `words` contém 980 palavras comuns da lingua inglesa. +- `sentences` contém 720 pequenas sentenças. -## Pattern basics {#sec-reg-basics} +## Padrões básicos {#sec-reg-basics} -We'll use `str_view()` to learn how regex patterns work. -We used `str_view()` in the last chapter to better understand a string vs. its printed representation, and now we'll use it with its second argument, a regular expression. -When this is supplied, `str_view()` will show only the elements of the string vector that match, surrounding each match with `<>`, and, where possible, highlighting the match in blue. +Usaremos a função `str_view()` para aprender como os padrões (*patterns*) regex funcionam. +Usamos `str_view()` no capítulo anterior para entender melhor uma *string* vs. sua representação impressa, e agora, a usaremos com seu segundo argumento, uma expressão regular. +Quando este argumento é fornecido, a `str_view()` irá mostrar apenas os elementos correspondentes (*match*) do vetor de *string* , colocando `<>` ao redor de cada correspondência, e, quando possível, enfatizando a correpondência na cor azul. -The simplest patterns consist of letters and numbers which match those characters exactly: +Os padrões mais simples consistem em letras e números que correspondem exatamente a esses caracteres: ```{r} str_view(fruit, "berry") ``` -Letters and numbers match exactly and are called **literal characters**. -Most punctuation characters, like `.`, `+`, `*`, `[`, `]`, and `?`, have special meanings[^regexps-2] and are called **metacharacters**. For example, `.` -will match any character[^regexps-3], so `"a."` will match any string that contains an "a" followed by another character +Letras e números correspondem exatamente à seus respectivos caracteres e são chamados **caracteres literais**. +A maioria dos caracteres de pontuação, como `.`, `+`, `*`, `[`, `]` e `?`, tem significados especiais[^regexps-2] e são chamado de **metacaracteres**. Por exemplo, `.` +corresponde a qualquer caractere[^regexps-3], portanto `"a."` irá corresponder a qualquer *string* que contenha a letra "a" seguida por qualquer outro caractere : -[^regexps-2]: You'll learn how to escape these special meanings in @sec-regexp-escaping. +[^regexps-2]: Você aprenderá como escapar destes significados especiais na @sec-regexp-escaping. -[^regexps-3]: Well, any character apart from `\n`. +[^regexps-3]: Bem, qualquer caractere exceto `\n`. ```{r} str_view(c("a", "ab", "ae", "bd", "ea", "eab"), "a.") ``` -Or we could find all the fruits that contain an "a", followed by three letters, followed by an "e": +Ou, podemos encontrar todas as frutas que contèm um "a", seguido por três letras, seguidas por um "e": ```{r} str_view(fruit, "a...e") ``` -**Quantifiers** control how many times a pattern can match: +**Quantificadores** controlam quantas vezes um padrão pode ser encontrado: -- `?` makes a pattern optional (i.e. it matches 0 or 1 times) -- `+` lets a pattern repeat (i.e. it matches at least once) -- `*` lets a pattern be optional or repeat (i.e. it matches any number of times, including 0). +- `?` torna um padrão opcional (e.x. corresponde a 0 ou 1 vezes) +- `+` permite que o padrão se repita (e.x. corresponde ao menos uma vez) +- `*` permite que o padrão seja opcional ou se repita (e.x. corresponde a qualquer número de vezes, incluindo 0). ```{r} -# ab? matches an "a", optionally followed by a "b". +# ab? corresponde a "a", opcionalmente seguida por um "b". str_view(c("a", "ab", "abb"), "ab?") -# ab+ matches an "a", followed by at least one "b". +# ab+ corresponde a um "a", seguido por ao menos um "b". str_view(c("a", "ab", "abb"), "ab+") -# ab* matches an "a", followed by any number of "b"s. +# ab* corresponde um "a", seguido por qualquer número de "b"s. str_view(c("a", "ab", "abb"), "ab*") ``` -**Character classes** are defined by `[]` and let you match a set of characters, e.g., `[abcd]` matches "a", "b", "c", or "d". -You can also invert the match by starting with `^`: `[^abcd]` matches anything **except** "a", "b", "c", or "d". -We can use this idea to find the words containing an "x" surrounded by vowels, or a "y" surrounded by consonants: +**Classes de caracteres** são definidas por `[]` e permitem que você defina um conjunto de caracteres, e.x., `[abcd]` corresponde a "a", "b", "c" ou "d". +Você também pode inverter a correspondência com um `^`: `[^abcd]` corresponde a qualquer coisa, **exceto** "a", "b", "c" ou "d". +Podemos usar esta ideia para encontrar palavras que contenham um "x" cercado por vogais, ou um "y" cercado por consoantes: ```{r} str_view(words, "[aeiou]x[aeiou]") str_view(words, "[^aeiou]y[^aeiou]") ``` -You can use **alternation**, `|`, to pick between one or more alternative patterns. -For example, the following patterns look for fruits containing "apple", "melon", or "nut", or a repeated vowel. +Você pode usar a **alternância**, `|`, para selecionar entre um ou mais padrões alternativos. +Por exemplo, os padrões a seguir procuram por frutas contendo "apple", "melon", ou "nut", ou uma vogal repetida. ```{r} str_view(fruit, "apple|melon|nut") str_view(fruit, "aa|ee|ii|oo|uu") ``` -Regular expressions are very compact and use a lot of punctuation characters, so they can seem overwhelming and hard to read at first. -Don't worry; you'll get better with practice, and simple patterns will soon become second nature. -Let's kick off that process by practicing with some useful stringr functions. +Expressões regulares são bastante compactas e usam diversos caracteres de pontuação, portanto elas podem parecer difíceis de ler e entender no início. +Não se preocupe; você as entenderá melhor com a prática, e padrões simples se tornarão naturais. +Vamos iniciar este processo praticando com algumas funções úteis do stringr. -## Key functions {#sec-stringr-regex-funs} +## Funções chaves {#sec-stringr-regex-funs} -Now that you've got the basics of regular expressions under your belt, let's use them with some stringr and tidyr functions. -In the following section, you'll learn how to detect the presence or absence of a match, how to count the number of matches, how to replace a match with fixed text, and how to extract text using a pattern. +Agora que você tem o básico das expressões regulares em seu cinto de utilidades, vamos usá-las em algumas funções dos pacotes stringr e tidyr. +Na seção a seguir, você aprenderá como detectar a presença ou ausência de correspondências (*matches*), como contar o número de correspondências, como trocar uma correspondência por um texto fixo e como extrair textos usando um padrão (*pattern*). -### Detect matches +### Detectando correspondência -`str_detect()` returns a logical vector that is `TRUE` if the pattern matches an element of the character vector and `FALSE` otherwise: +`str_detect()` retorna uma vetor lógico que ém `TRUE` se o padrão corresponde a um elemento do vetor de caracteres e `FALSE` se não corresponde: ```{r} str_detect(c("a", "b", "c"), "[aeiou]") ``` -Since `str_detect()` returns a logical vector of the same length as the initial vector, it pairs well with `filter()`. -For example, this code finds all the most popular names containing a lower-case "x": +Uma vez que `str_detect()` retorna um vetor lógico de mesmo tamanho que o vetor inicial, ele vai muito bem com a função `filter()`. +Por exemplo, este código encontra todos os nomes mais populares contendo um "x" minúsculo: ```{r} -babynames |> - filter(str_detect(name, "x")) |> - count(name, wt = n, sort = TRUE) +bebes |> + filter(str_detect(nome, "x")) |> + count(nome, wt = n, sort = TRUE) ``` -We can also use `str_detect()` with `summarize()` by pairing it with `sum()` or `mean()`: `sum(str_detect(x, pattern))` tells you the number of observations that match and `mean(str_detect(x, pattern))` tells you the proportion that match. -For example, the following snippet computes and visualizes the proportion of baby names[^regexps-4] that contain "x", broken down by year. -It looks like they've radically increased in popularity lately! +Podemos também usar a`str_detect()` com `summarize()` combinando-a com `sum()` ou `mean()`: `sum(str_detect(x, padrao))` informa o numero de observações correspondentes e `mean(str_detect(x, padrao))` informa a proporção da correspondência. +Por exemplo, o código a seguir, calcula e visualiza a proporção de nomes de bebês [^regexps-4] que contém "x", agrupados por ano. +Parece que eles aumentaram em popularidade ultimamente! -[^regexps-4]: This gives us the proportion of **names** that contain an "x"; if you wanted the proportion of babies with a name containing an x, you'd need to perform a weighted mean. +[^regexps-4]: Isto nos dá a proporção de **nomes** que contém um "x"; se você quiser a proporção de bebês com um nome contendo um x, você deveria usar uma média ponderada. ```{r} #| fig-alt: | @@ -142,106 +142,106 @@ It looks like they've radically increased in popularity lately! #| The proportion declines gradually from 8 per 1000 in 1880 to 4 per 1000 in #| 1980, then increases rapidly to 16 per 1000 in 2019. -babynames |> - group_by(year) |> - summarize(prop_x = mean(str_detect(name, "x"))) |> - ggplot(aes(x = year, y = prop_x)) + +bebes |> + group_by(ano) |> + summarize(prop_x = mean(str_detect(nome, "x"))) |> + ggplot(aes(x = ano, y = prop_x)) + geom_line() ``` -There are two functions that are closely related to `str_detect()`: `str_subset()` and `str_which()`. -`str_subset()` returns a character vector containing only the strings that match. -`str_which()` returns an integer vector giving the positions of the strings that match. +Existem duas funções que se relacionam muito com a `str_detect()`: `str_subset()` e `str_which()`. +`str_subset()` retorna um vetor de caractere contendo apenas as *strings* que correspondem ao padrão. +`str_which()` retorna um vetor de inteiro informando a posição das *strings* que correspondem ao padrão. -### Count matches +### Contando correspondências (*matches*) -The next step up in complexity from `str_detect()` is `str_count()`: rather than a true or false, it tells you how many matches there are in each string. +O próximo passo em complexidade de `str_detect()` é `str_count()`: em vez de verdadeiro (*true*) ou falso (*false*), ele informa quantas correspondências existem em cada *string*. ```{r} x <- c("apple", "banana", "pear") str_count(x, "p") ``` -Note that each match starts at the end of the previous match, i.e. regex matches never overlap. -For example, in `"abababa"`, how many times will the pattern `"aba"` match? -Regular expressions say two, not three: +Note que cada correspondência começa ao final da anterior, e.x. regex nunca se sobrepõem. +Por exemplo, em `"abababa"`, quantas vezes o padrçao `"aba"` é correspondido? +A expressão regular dizem, duas, não três vezes: ```{r} str_count("abababa", "aba") str_view("abababa", "aba") ``` -It's natural to use `str_count()` with `mutate()`. -The following example uses `str_count()` with character classes to count the number of vowels and consonants in each name. +É comum usar a `str_count()` com `mutate()`. +Os exemplos a seguir, usam `str_count()` com classes de caracteres para contar o número de vogais e consoantes em cada nome. ```{r} -babynames |> - count(name) |> +bebes |> + count(nome) |> mutate( - vowels = str_count(name, "[aeiou]"), - consonants = str_count(name, "[^aeiou]") + vogais = str_count(nome, "[aeiou]"), + consoantes = str_count(nome, "[^aeiou]") ) ``` -If you look closely, you'll notice that there's something off with our calculations: "Aaban" contains three "a"s, but our summary reports only two vowels. -That's because regular expressions are case sensitive. -There are three ways we could fix this: +Se você observar bem, você verá que há algo estranho em nossa contagem: "Aaban" contém três "a"s, mas nosso relatório sumarizado mostra apenas 2 vogais. +Isto é porque as expressões regulares consideram a diferença entre letras maiúsculas e minúsculas (*case sensitive*). +Existem três formas de arrumar isto: -- Add the upper case vowels to the character class: `str_count(name, "[aeiouAEIOU]")`. -- Tell the regular expression to ignore case: `str_count(name, regex("[aeiou]", ignore_case = TRUE))`. We'll talk about more in @sec-flags. -- Use `str_to_lower()` to convert the names to lower case: `str_count(str_to_lower(name), "[aeiou]")`. +- Adicionar as vogais maiúsculas nas classes de caracteres: `str_count(nome, "[aeiouAEIOU]")`. +- Informar a expressão regular para ignnorar esta diferença: `str_count(nome, regex("[aeiou]", ignore_case = TRUE))`. Falaremos mais sobre isto na @sec-flags. +- Usar a `str_to_lower()` para converter os nomes para letras minúsculas: `str_count(str_to_lower(nome), "[aeiou]")`. -This variety of approaches is pretty typical when working with strings --- there are often multiple ways to reach your goal, either by making your pattern more complicated or by doing some preprocessing on your string. -If you get stuck trying one approach, it can often be useful to switch gears and tackle the problem from a different perspective. +Esta variedade de abordagens é muito comum quando trabalhamos com *strings* --- em geral, há várias formas de atingir seus objetivos, seja formando padrões mais complexos ou efetuando algum pré-processamento em sua *string*. +Se você estiver bloqueado em uma abordagem, em geral, pode ser melhor mudar a marcha e atacar seu problema sob uma perspectiva diferente. -In this case, since we're applying two functions to the name, I think it's easier to transform it first: +Nesta caso, como estamos usando duas funções na variável nome, acredito ser mais fácil transformá-la antes: ```{r} -babynames |> - count(name) |> +bebes |> + count(nome) |> mutate( - name = str_to_lower(name), - vowels = str_count(name, "[aeiou]"), - consonants = str_count(name, "[^aeiou]") + nome = str_to_lower(nome), + vogais = str_count(nome, "[aeiou]"), + consoantes = str_count(nome, "[^aeiou]") ) ``` -### Replace values +### Trocando valores -As well as detecting and counting matches, we can also modify them with `str_replace()` and `str_replace_all()`. -`str_replace()` replaces the first match, and as the name suggests, `str_replace_all()` replaces all matches. +Assim como detectar e contar correspondências, você também pode modificá-las com `str_replace()` e `str_replace_all()`. +`str_replace()` troca a primeira correspondência e, como o nome pode sugerir, a `str_replace_all()` troca todas as correspondências. ```{r} x <- c("apple", "pear", "banana") str_replace_all(x, "[aeiou]", "-") ``` -`str_remove()` and `str_remove_all()` are handy shortcuts for `str_replace(x, pattern, "")`: +`str_remove()` e `str_remove_all()` são funções atalhos para `str_replace(x, padrão, "")`: ```{r} x <- c("apple", "pear", "banana") str_remove_all(x, "[aeiou]") ``` -These functions are naturally paired with `mutate()` when doing data cleaning, and you'll often apply them repeatedly to peel off layers of inconsistent formatting. +Estas funções combinam natualmente com `mutate()` quando fazemos limpeza de dados, e você geralmente as aplicará repetidamente para remover camadas de incosistência de formatação. -### Extract variables {#sec-extract-variables} +### Extraindo variáveis {#sec-extract-variables} -The last function we'll discuss uses regular expressions to extract data out of one column into one or more new columns: `separate_wider_regex()`. -It's a peer of the `separate_wider_position()` and `separate_wider_delim()` functions that you learned about in @sec-string-columns. -These functions live in tidyr because they operate on (columns of) data frames, rather than individual vectors. +A última função que discutiremos usa expressões regulares para extrair dados de uma coluna para um ou mais nova(s) coluna(s): `separate_wider_regex()`. +É uma função par da `separate_wider_position()` e `separate_wider_delim()` que você aprendeu na @sec-string-columns. +Estas funções estão no pacote tidyr pois elas operam em colunas de *data frames* ou invés de vetores individuais. -Let's create a simple dataset to show how it works. -Here we have some data derived from `babynames` where we have the name, gender, and age of a bunch of people in a rather weird format[^regexps-5]: +Vamos criar um conjunto simples de dados para mostrar como funciona. +Aqui temos alguns dados derivados de `dados::bebes` onde, temos nome, sexo biológico e idade de várias pessoas em um formato meio estranho[^regexps-5]: -[^regexps-5]: We wish we could reassure you that you'd never see something this weird in real life, but unfortunately over the course of your career you're likely to see much weirder! +[^regexps-5]: Gostaríamos de poder garantir que você nunca veria algo tão estranho na vida real, mas infelizmente, ao longo de sua carreira, é provável que você veja coisas muito mais estranhas! ```{r} df <- tribble( ~str, "-F_34", "-F_45", - "-N_33", + "-M_33", "-F_38", "-F_58", "-M_41", @@ -249,8 +249,8 @@ df <- tribble( ) ``` -To extract this data using `separate_wider_regex()` we just need to construct a sequence of regular expressions that match each piece. -If we want the contents of that piece to appear in the output, we give it a name: +Para extrair estes dados com `separate_wider_regex()`, precisamos apenas construir uma sequência de expressões regulares que correspondem a cada parte. +Se quisermos que o conteído de cada parte apareção na saída, damos um nome: ```{r} df |> @@ -258,72 +258,72 @@ df |> str, patterns = c( "<", - name = "[A-Za-z]+", + nome = "[A-Za-z]+", ">-", - gender = ".", + sexo_biologico = ".", "_", - age = "[0-9]+" + idade = "[0-9]+" ) ) ``` -If the match fails, you can use `too_short = "debug"` to figure out what went wrong, just like `separate_wider_delim()` and `separate_wider_position()`. +Se a correpondência falhar, você pode usar `too_short = "debug"` para entender o que deu errado, assim como na `separate_wider_delim()` e `separate_wider_position()`. -### Exercises +### Exercícios -1. What baby name has the most vowels? - What name has the highest proportion of vowels? - (Hint: what is the denominator?) +1. Qual nome de bebê tem a menor quantidade de vogais? + QUal nome tem a maior proporção de vogais? + (Dica: qual o denominador?) -2. Replace all forward slashes in `"a/b/c/d/e"` with backslashes. - What happens if you attempt to undo the transformation by replacing all backslashes with forward slashes? - (We'll discuss the problem very soon.) +2. Troque todas as barras em `"a/b/c/d/e"` por barras invertidas. + O que acontece se você tentar desfazer esta mudança trocando as barras invertidas por barras? + (Discutiremos este problema em breve.) -3. Implement a simple version of `str_to_lower()` using `str_replace_all()`. +3. Implemente uma versão simples da `str_to_lower()` usando `str_replace_all()`. -4. Create a regular expression that will match telephone numbers as commonly written in your country. +4. Crie uma expressão regular para correpondências de números telefônicos da forma que são comumente escritas em seu país. -## Pattern details +## Detalhes de padrões -Now that you understand the basics of the pattern language and how to use it with some stringr and tidyr functions, it's time to dig into more of the details. -First, we'll start with **escaping**, which allows you to match metacharacters that would otherwise be treated specially. -Next, you'll learn about **anchors** which allow you to match the start or end of the string. -Then, you'll learn more about **character classes** and their shortcuts which allow you to match any character from a set. -Next, you'll learn the final details of **quantifiers** which control how many times a pattern can match. -Then, we have to cover the important (but complex) topic of **operator precedence** and parentheses. -And we'll finish off with some details of **grouping** components of the pattern. +Agora que você entende o básico da linguagem dos padrões e como usá-la com algumas funções do pacote stringr e tidyr, é hora de nos aprofundarmos mais nos detalhes. +Primeiro, começaremos com **escapadas** (*escaping*), que permite combinar metacaracteres que de outra forma seriam tratados de maneira especial. +A seguir, você aprenderá sobre **âncoras** (*anchors*) que permitem combinar com o início ou o fim da *string*. +Em seguida, você aprenderá mais sobre **classes de caracteres** (*character classes*) e seus atalhos que permitem combinar qualquer caractere de um conjunto. +A seguir, você aprenderá os detalhes finais dos **quantificadores** (*quantifiers*) que controlam quantas vezes um padrão pode corresponder. +Então, temos que cobrir o importante (mas complexo) tópico de **precedência de operador** (*operator precedence*) e parênteses. +E terminaremos com alguns detalhes de **agrupamento** (*grouping*) de componentes de um padrão. -The terms we use here are the technical names for each component. -They're not always the most evocative of their purpose, but it's very helpful to know the correct terms if you later want to Google for more details. +Os termos que usamos aqui são os nomes técnicos de cada componente. +Eles nem sempre são os que combinam melhor ao seu propósito, mas é muito útil saber os termos corretos se você quiser procurar mais detalhes no *Google* posteriormente. -### Escaping {#sec-regexp-escaping} +### Escapadas {#sec-regexp-escaping} -In order to match a literal `.`, you need an **escape** which tells the regular expression to match metacharacters[^regexps-6] literally. -Like strings, regexps use the backslash for escaping. -So, to match a `.`, you need the regexp `\.`. Unfortunately this creates a problem. -We use strings to represent regular expressions, and `\` is also used as an escape symbol in strings. -So to create the regular expression `\.` we need the string `"\\."`, as the following example shows. +Para correponder um `.` literal, você precisa **escapar**, o que informa a expressão regular para correponder ao metacaractere [^regexps-6] literalmente. +Assim como as *strings*, regexp usa a barra invertidade para escapadas (*escaping*). +Portanto, para corresponder a um `.`, você preacisa da regexp `\.`. Infelizmente isto cria um problema. +Nós usamos strings para representar expressões regulares, e `\` é também usada como símbolo de escapadas nas *strings*. +Portanto, para criar a expressão regular `\.` , precismos da *string* `"\\."`, como mostra os exemplos a seguir. -[^regexps-6]: The complete set of metacharacters is `.^$\|*+?{}[]()` +[^regexps-6]: A lista completa de metacaractere é `.^$\|*+?{}[]()` ```{r} -# To create the regular expression \., we need to use \\. -dot <- "\\." +# Para criar a expressão regular \., precisamos usar \\. +ponto <- "\\." -# But the expression itself only contains one \ -str_view(dot) +# Mas a expressão regular em si, contém apenas uma \ +str_view(ponto) -# And this tells R to look for an explicit . +# E isto diz ao R para procurar por um . explicitamente. str_view(c("abc", "a.c", "bef"), "a\\.c") ``` In this book, we'll usually write regular expression without quotes, like `\.`. If we need to emphasize what you'll actually type, we'll surround it with quotes and add extra escapes, like `"\\."`. -If `\` is used as an escape character in regular expressions, how do you match a literal `\`? -Well, you need to escape it, creating the regular expression `\\`. -To create that regular expression, you need to use a string, which also needs to escape `\`. -That means to match a literal `\` you need to write `"\\\\"` --- you need four backslashes to match one! +Neste livro, normalmente escreveremos expressões regulares sem aspas, como `\.`. +Se precisarmos enfatizar o que você realmente digitará, colocaremos aspas e adicionaremos escapadas extras, como `"\\."`. +Para criar essa expressão regular, você precisa usar uma string, que também precisa escapar de `\`. +Isso significa que para corresponder a um `\` literal você precisa escrever `"\\\\"` --- você precisa de quatro barras invertidas para corresponder a uma! ```{r} x <- "a\\b" @@ -338,36 +338,36 @@ That lets you avoid one layer of escaping: str_view(x, r"{\\}") ``` -If you're trying to match a literal `.`, `$`, `|`, `*`, `+`, `?`, `{`, `}`, `(`, `)`, there's an alternative to using a backslash escape: you can use a character class: `[.]`, `[$]`, `[|]`, \... -all match the literal values. +Se você está tentando correponder um `.`, `$`, `|`, `*`, `+`, `?`, `{`, `}`, `(`, `)` literal, há uma alternativa para não usar a escapada com barra invertida: você pode usar uma classe de caractere: `[.]`, `[$]`, `[|]`, \... +todas correspondem aos valores literais. ```{r} str_view(c("abc", "a.c", "a*c", "a c"), "a[.]c") str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c") ``` -### Anchors +### Âncoras -By default, regular expressions will match any part of a string. -If you want to match at the start or end you need to **anchor** the regular expression using `^` to match the start or `$` to match the end: +Por padrão, as expressões regulares corresponderão a qualquer parte de uma string. +Se você quiser corresponder no início ou no final, você precisa **ancorar** a expressão regular usando `^` para corresponder ao início ou `$` para corresponder ao final: ```{r} str_view(fruit, "^a") str_view(fruit, "a$") ``` -It's tempting to think that `$` should match the start of a string, because that's how we write dollar amounts, but that's not what regular expressions want. +É tentador pensar que `$` deve corresponder ao início de uma string, porque é assim que escrevemos valores monetários, mas não é isso que as expressões regulares desejam. -To force a regular expression to match only the full string, anchor it with both `^` and `$`: +Para forçar uma expressão regular a corresponder apenas à string completa, ancore-a com `^` e `$`: ```{r} str_view(fruit, "apple") str_view(fruit, "^apple$") ``` -You can also match the boundary between words (i.e. the start or end of a word) with `\b`. -This can be particularly useful when using RStudio's find and replace tool. -For example, if to find all uses of `sum()`, you can search for `\bsum\b` to avoid matching `summarize`, `summary`, `rowsum` and so on: +Você também pode combinar o limite entre as palavras (ou seja, o início ou o fim de uma palavra) com `\b`. +Isso pode ser particularmente útil ao usar a ferramenta localizar e substituir do RStudio. +Por exemplo, se quiser encontrar todos os usos de `sum()`, você pode procurar por `\bsum\b` para evitar a correspondência de `summarize`, `summary`, `rowsum` e assim por diante: ```{r} x <- c("summary(x)", "summarize(df)", "rowsum(x)", "sum(x)") @@ -375,28 +375,28 @@ str_view(x, "sum") str_view(x, "\\bsum\\b") ``` -When used alone, anchors will produce a zero-width match: +Quando usadas sozinhas, as âncoras produzirão uma correspondência de largura-zero (*zero-width*): ```{r} str_view("abc", c("$", "^", "\\b")) ``` -This helps you understand what happens when you replace a standalone anchor: +Isso ajuda você a entender o que acontece quando você substitui uma âncora independente: ```{r} str_replace_all("abc", c("$", "^", "\\b"), "--") ``` -### Character classes +### Classes de caracteres -A **character class**, or character **set**, allows you to match any character in a set. -As we discussed above, you can construct your own sets with `[]`, where `[abc]` matches "a", "b", or "c" and `[^abc]` matches any character except "a", "b", or "c". -Apart from `^` there are two other characters that have special meaning inside of `[]:` +Uma **classe de caracteres** (*character class*), ou um **conjunto** de caracteres, permite que você faça a correspondência de qualquer caractere deste conjunto. +Como discutido acima, você pode construir seus próprios conjuntos com `[]`, onde `[abc]` correponde a "a", "b" ou "c" e `[^abc]` corresponde a qualquer caractere exceto "a", "b" ou "c". +Além do `^` existem outros dois caracteres com significados especiais quando estão dentro de `[]:` -- `-` defines a range, e.g., `[a-z]` matches any lower case letter and `[0-9]` matches any number. -- `\` escapes special characters, so `[\^\-\]]` matches `^`, `-`, or `]`. +- `-` define um intervalo (*range*), e.x., `[a-z]` corresponde a qualquer letra minúscula e `[0-9]` a qualquer número. +- `\` caracteres especiais de escapadas, então `[\^\-\]]` corresponde `^`, `-` ou `]`. -Here are few examples: +Aqui estão alguns exemplos: ```{r} x <- "abcd ABCD 12345 -!@#%." @@ -404,26 +404,26 @@ str_view(x, "[abc]+") str_view(x, "[a-z]+") str_view(x, "[^a-z0-9]+") -# You need an escape to match characters that are otherwise -# special inside of [] +# Você precisa de uma escapada para caracteres especiais +# dentro de [] str_view("a-b-c", "[a-c]") str_view("a-b-c", "[a\\-c]") ``` -Some character classes are used so commonly that they get their own shortcut. -You've already seen `.`, which matches any character apart from a newline. -There are three other particularly useful pairs[^regexps-7]: +Algumas classes de caracteres são tão comuns que possuem atalhos próprios. +Você já viu `.`, que corresponde a qualquer caractere exceto o de nova linha. +Existem três outras classes que são particularmente úteis[^regexps-7]: -[^regexps-7]: Remember, to create a regular expression containing `\d` or `\s`, you'll need to escape the `\` for the string, so you'll type `"\\d"` or `"\\s"`. +[^regexps-7]: Lembre-se, para criar uma expressão regular contendo `\d` ou `\s`, você precisará escapar a `\` da *string*, então você digitará `"\\d"` ou `"\\s"`. -- `\d` matches any digit;\ - `\D` matches anything that isn't a digit. -- `\s` matches any whitespace (e.g., space, tab, newline);\ - `\S` matches anything that isn't whitespace. -- `\w` matches any "word" character, i.e. letters and numbers;\ - `\W` matches any "non-word" character. +- `\d` corresponde a qualquer digito;\ + `\D` corresponde a qualquer coisa que não seja um digito. +- `\s` corresponde a qualquer espaço em branco (e.x., espaço, tab, nova linha);\ + `\S` corresponde a qualquer coisa que não seja um espaço em branco. +- `\w` corresponde a qualquer caractere de "palavra" (*word*) , e.x. letras e números;\ + `\W` corresponde a qualquer caractere de "não-palavra" (*non-word*). -The following code demonstrates the six shortcuts with a selection of letters, numbers, and punctuation characters. +O código a seguir demonstra os seis atalhos com a seleção de letras, números e caracteres de pontuação. ```{r} x <- "abcd ABCD 12345 -!@#%." @@ -435,50 +435,50 @@ str_view(x, "\\w+") str_view(x, "\\W+") ``` -### Quantifiers {#sec-quantifiers} +### Quantificadores {#sec-quantifiers} -**Quantifiers** control how many times a pattern matches. -In @sec-reg-basics you learned about `?` (0 or 1 matches), `+` (1 or more matches), and `*` (0 or more matches). -For example, `colou?r` will match American or British spelling, `\d+` will match one or more digits, and `\s?` will optionally match a single item of whitespace. -You can also specify the number of matches precisely with `{}`: +**Quantificadores** (*Quantifies*) controlam quantas vezes um padrão é correspondido. +Na @sec-reg-basics você aprendeu sobre o `?` (0 ou 1 correspondência), `+` (1 ou mais correspondência) e `*` (0 ou mais correspondências). +Por exemplo, `colou?r` irá corresponder à escrita em inglês estadunidense (*color*) e britânico (*colour*), `\d+` irá corresponder a um ou mais digitos, e `\s?` irá opcionalmente corresponder a um único espaço em branco. +Você também pode especificar o número exato de correspondências com `{}`: -- `{n}` matches exactly n times. -- `{n,}` matches at least n times. -- `{n,m}` matches between n and m times. +- `{n}` corresponde exatamente n vezes. +- `{n,}` corresponde ao menos n vezes. +- `{n,m}` corresponde entre n e m vezes. -### Operator precedence and parentheses +### Precedência de operadores e parênteses -What does `ab+` match? -Does it match "a" followed by one or more "b"s, or does it match "ab" repeated any number of times? -What does `^a|b$` match? -Does it match the complete string a or the complete string b, or does it match a string starting with a or a string ending with b? +Ao que `ab+` corresponde? +Corresponde a um "a" seguido por um ou mais "b"s, ou corresponde a "ab" repetidos qualquer número de vezes? +Ao que corresponde `^a|b$`? +Corresponde a uma *string* a completa OU um *string* b, ou a uma *string* que começa com a OU uma *string* que termina com b? -The answer to these questions is determined by operator precedence, similar to the PEMDAS or BEDMAS rules you might have learned in school. -You know that `a + b * c` is equivalent to `a + (b * c)` not `(a + b) * c` because `*` has higher precedence and `+` has lower precedence: you compute `*` before `+`. +As respostas para estas perguntas são determinadas pela precedência de operadores, similar a regras PEMDAS ou BEDMAS que você deve ter aprendido na escola. +Você sabe que `a + b * c` é equivalente a `a + (b * c)` e não `(a + b) * c` pois `*` tem uma prioridade mais alta de precedência e `+` tem uma precedência mais baixa: você calcula o `*` antes do `+`. -Similarly, regular expressions have their own precedence rules: quantifiers have high precedence and alternation has low precedence which means that `ab+` is equivalent to `a(b+)`, and `^a|b$` is equivalent to `(^a)|(b$)`. -Just like with algebra, you can use parentheses to override the usual order. -But unlike algebra you're unlikely to remember the precedence rules for regexes, so feel free to use parentheses liberally. +Da mesma forma, expressões regulares têm suas próprias regras de precedência: quantificadores têm precedência alta e alternância têm precedência baixa, o que significa que `ab+` é equivalente a `a(b+)`, e `^a|b$` é equivalente a `(^a )|(b$)`. +Assim como na álgebra, você pode usar parênteses para substituir a ordem normal. +Mas, diferentemente da álgebra, é improvável que você se lembre das regras de precedência para expressões regulares, então sinta-se à vontade para usar parênteses livremente.. -### Grouping and capturing +### Agrupando e capturando -As well as overriding operator precedence, parentheses have another important effect: they create **capturing groups** that allow you to use sub-components of the match. +Além de substituir a precedência do operador, os parênteses têm outro efeito importante: eles criam **grupos de captura** que permitem usar subcomponentes da correspondência. -The first way to use a capturing group is to refer back to it within a match with **back reference**: `\1` refers to the match contained in the first parenthesis, `\2` in the second parenthesis, and so on. -For example, the following pattern finds all fruits that have a repeated pair of letters: +A primeira maneira de usar um grupo de captura é fazer referência a ele dentro de uma correspondência com **referência anterior**: `\1` refere-se à correspondência contida no primeiro parêntese, `\2` no segundo parêntese, e assim sobre. +Por exemplo, o padrão a seguir encontra todas as frutas que possuem um par de letras repetido: ```{r} str_view(fruit, "(..)\\1") ``` -And this one finds all words that start and end with the same pair of letters: +E este encontra todas as palavras que começam e terminam com o mesmo par de letras: ```{r} str_view(words, "^(..).*\\1$") ``` -You can also use back references in `str_replace()`. -For example, this code switches the order of the second and third words in `sentences`: +Você também pode usar referências anteriores (*back references*) em `str_replace()`. +Por exemplo, este código muda a ordem da segunda e terceira palavras em `sentences`: ```{r} sentences |> @@ -486,10 +486,10 @@ sentences |> str_view() ``` -If you want to extract the matches for each group you can use `str_match()`. -But `str_match()` returns a matrix, so it's not particularly easy to work with[^regexps-8]: +Se você deseja extrair as correspondências de cada grupo você pode usar `str_match()`. +Mas `str_match()` retorna uma matriz, então não é particularmente fácil trabalhar com ela[^regexps-8]: -[^regexps-8]: Mostly because we never discuss matrices in this book! +[^regexps-8]: Principalmente porque nunca discutimos sobre matrizes neste livro! ```{r} sentences |> @@ -503,45 +503,45 @@ You could convert to a tibble and name the columns: sentences |> str_match("the (\\w+) (\\w+)") |> as_tibble(.name_repair = "minimal") |> - set_names("match", "word1", "word2") + set_names("correspondencia", "palavra1", "palavra2") ``` -But then you've basically recreated your own version of `separate_wider_regex()`. -Indeed, behind the scenes, `separate_wider_regex()` converts your vector of patterns to a single regex that uses grouping to capture the named components. +Mas então você basicamente recriou sua própria versão de `separate_wider_regex()`. +Na verdade, nos bastidores, `separate_wider_regex()` converte seu vetor de padrões em um único regex que usa agrupamento para capturar os componentes nomeados. -Occasionally, you'll want to use parentheses without creating matching groups. -You can create a non-capturing group with `(?:)`. +Ocasionalmente, você desejará usar parênteses sem criar grupos correspondentes. +Você pode criar um grupo sem captura com `(?:)`. ```{r} -x <- c("a gray cat", "a grey dog") -str_match(x, "gr(e|a)y") -str_match(x, "gr(?:e|a)y") +x <- c("uma gata faminta", "um cão faminto") +str_match(x, "famint(o|a)") +str_match(x, "famint(?:o|a)") ``` -### Exercises +### Exercícios -1. How would you match the literal string `"'\`? How about `"$^$"`? +1. Como você faria para corresponder a *string* literal `"'\`? E a `"$^$"`? -2. Explain why each of these patterns don't match a `\`: `"\"`, `"\\"`, `"\\\"`. +2. Explique porque cada um desses padrões não correspondem a `\`: `"\"`, `"\\"`, `"\\\"`. -3. Given the corpus of common words in `stringr::words`, create regular expressions that find all words that: +3. Dado o conjunto de palavras comuns em `stringr::words`, crie expressões regulares para encontrar todas as palavras que: - a. Start with "y". - b. Don't start with "y". - c. End with "x". - d. Are exactly three letters long. (Don't cheat by using `str_length()`!) - e. Have seven letters or more. - f. Contain a vowel-consonant pair. - g. Contain at least two vowel-consonant pairs in a row. - h. Only consist of repeated vowel-consonant pairs. + a. Comecem com "y". + b. Não comecem com um "y". + c. Terminem com "x". + d. Possuem exatamente três letras. (Não trapaceie usando a `str_length()`!) + e. Possuem sete letras ou mais. + f. Contém um par consoante-vogal. + g. Contém ao menos um par consoante-vogal em sequência. + h. É formada apenas por pares repetidos de consoantes-vogais. -4. Create 11 regular expressions that match the British or American spellings for each of the following words: airplane/aeroplane, aluminum/aluminium, analog/analogue, ass/arse, center/centre, defense/defence, donut/doughnut, gray/grey, modeling/modelling, skeptic/sceptic, summarize/summarise. - Try and make the shortest possible regex! +4. Crie 11 expressões regulares que correspondem a ortografias estadunidenses e britânicas para cada uma das seguintes palavras: airplane/aeroplane, aluminum/aluminium, analog/analogue, ass/arse, center/centre, defense/defence, donut/doughnut, gray/grey, modeling/modelling, skeptic/sceptic, summarize/summarise. + Tente e crie a menor expressão regular possível! -5. Switch the first and last letters in `words`. - Which of those strings are still `words`? +5. Troque a primeira e última letra em `words`. + Quais desses *strings* ainda são `palavras`? -6. Describe in words what these regular expressions match: (read carefully to see if each entry is a regular expression or a string that defines a regular expression.) +6. Descreva em palavras ao que correspondem as expressões abaixo: (leia com atenção para ver se cada parte corresponde a uma expressão regular ou a uma *string* que define uma expressão regular.) a. `^.*$` b. `"\\{.+\\}"` @@ -551,19 +551,19 @@ str_match(x, "gr(?:e|a)y") f. `(.)\1\1` g. `"(..)\\1"` -7. Solve the beginner regexp crosswords at . +7. Resolva a palavra-cruzada de regex para iniciantes em . -## Pattern control +## Controle de padrões -It's possible to exercise extra control over the details of the match by using a pattern object instead of just a string. -This allows you to control the so called regex flags and match various types of fixed strings, as described below. +É possível exercer controle extra sobre os detalhes da correspondência usando um objeto padrão em vez de apenas uma string. +Isto permite que você controle os chamados sinalizadores (*flgas*) regex e combine vários tipos de strings fixas, conforme descrito abaixo. -### Regex flags {#sec-flags} +### Sinalizadores regex {#sec-flags} -There are a number of settings that can be used to control the details of the regexp. -These settings are often called **flags** in other programming languages. -In stringr, you can use these by wrapping the pattern in a call to `regex()`. -The most useful flag is probably `ignore_case = TRUE` because it allows characters to match either their uppercase or lowercase forms: +Existem várias configurações que podem ser usadas para controlar os detalhes do regexp. +Essas configurações costumam ser chamadas de **sinalizadores** (*flgas*) em outras linguagens de programação. +No pacote stringr, você pode usá-los agrupando o padrão em uma chamada para `regex()`. +O sinalizador mais útil é provavelmente `ignore_case = TRUE` porque permite que os caracteres correspondam às suas formas maiúsculas ou minúsculas: ```{r} bananas <- c("banana", "Banana", "BANANA") @@ -571,167 +571,167 @@ str_view(bananas, "banana") str_view(bananas, regex("banana", ignore_case = TRUE)) ``` -If you're doing a lot of work with multiline strings (i.e. strings that contain `\n`), `dotall`and `multiline` may also be useful: +Se você trabalha muito com *strings* multilinhas (ou seja, *strings* que contêm `\n`), `dotall` e `multiline` também podem ser úteis: -- `dotall = TRUE` lets `.` match everything, including `\n`: +- `dotall = TRUE` faz com que o `.` corresponda a qualquer coisa, incluindo o `\n`: ```{r} - x <- "Line 1\nLine 2\nLine 3" - str_view(x, ".Line") - str_view(x, regex(".Line", dotall = TRUE)) + x <- "Linha 1\nLinha 2\nLinha 3" + str_view(x, ".Linha") + str_view(x, regex(".Linha", dotall = TRUE)) ``` -- `multiline = TRUE` makes `^` and `$` match the start and end of each line rather than the start and end of the complete string: +- `multiline = TRUE` faz o `^` e o `$` corresponder ao início e fim de cada linha ao invés de do início e fim de cada *string* completa: ```{r} - x <- "Line 1\nLine 2\nLine 3" - str_view(x, "^Line") - str_view(x, regex("^Line", multiline = TRUE)) + x <- "Linha 1\nLinha 2\nLinha 3" + str_view(x, "^Linha") + str_view(x, regex("^Linha", multiline = TRUE)) ``` -Finally, if you're writing a complicated regular expression and you're worried you might not understand it in the future, you might try `comments = TRUE`. -It tweaks the pattern language to ignore spaces and new lines, as well as everything after `#`. -This allows you to use comments and whitespace to make complex regular expressions more understandable[^regexps-9], as in the following example: +Finalmente, se você estiver escrevendo uma expressão regular complicada e estiver preocupado em não entendê-la no futuro, você pode tentar `comments = TRUE`. +Ele ajusta a linguagem padrão para ignorar espaços e novas linhas, bem como tudo depois de `#`. +Isso permite que você use comentários e espaços em branco para tornar expressões regulares complexas mais compreensíveis[^regexps-9], como no exemplo a seguir: -[^regexps-9]: `comments = TRUE` is particularly effective in combination with a raw string, as we use here. +[^regexps-9]: `comments = TRUE` é particularmente eficaz em combinação com uma string bruta (*raw*), como usamos aqui. ```{r} -phone <- regex( +telefone <- regex( r"( - \(? # optional opening parens - (\d{3}) # area code - [)\-]? # optional closing parens or dash - \ ? # optional space - (\d{3}) # another three numbers - [\ -]? # optional space or dash - (\d{4}) # four more numbers + \(? # opcional abertura de parantese + (\d{3}) # codigo de area + [)\-]? # opcional fechamento de parantese ou traço + \ ? # opcional espaço + (\d{3}) # outros três números + [\ -]? # opcional espaço ou traço + (\d{4}) # quatro outros números )", comments = TRUE ) -str_extract(c("514-791-8141", "(123) 456 7890", "123456"), phone) +str_extract(c("514-791-8141", "(123) 456 7890", "123456"), telefone) ``` -If you're using comments and want to match a space, newline, or `#`, you'll need to escape it with `\`. +Se você estiver usando comentários e quiser corresponder a um espaço, nova linha ou `#`, você precisará escapar usando a `\`. -### Fixed matches +### Correspondências fixas -You can opt-out of the regular expression rules by using `fixed()`: +Você pode optar por cancelar as regras de expressão regular usando `fixed()`: ```{r} str_view(c("", "a", "."), fixed(".")) ``` -`fixed()` also gives you the ability to ignore case: +`fixed()` também te dá a habilidate de ignorar formas maiúsculas e minúsculas: ```{r} str_view("x X", "X") str_view("x X", fixed("X", ignore_case = TRUE)) ``` -If you're working with non-English text, you will probably want `coll()` instead of `fixed()`, as it implements the full rules for capitalization as used by the `locale` you specify. -See @sec-other-languages for more details on locales. +Se você estiver trabalhando com textos que não estão em Inglês, você provavelmente irá querer usar a `coll()` ao invés da `fixed()`, uma vez que ela implementa todas as regras de capitalização (*capitalization*) usadas pela `localização` (*locale*) que você especificou. +Veja a @sec-other-languages para maiores detalhes sobre localização. ```{r} str_view("i İ ı I", fixed("İ", ignore_case = TRUE)) str_view("i İ ı I", coll("İ", ignore_case = TRUE, locale = "tr")) ``` -## Practice +## Prática -To put these ideas into practice we'll solve a few semi-authentic problems next. -We'll discuss three general techniques: +Para colocar essas ideias em prática, resolveremos a seguir alguns problemas semi-autênticos. +Discutiremos três técnicas gerais: -1. checking your work by creating simple positive and negative controls -2. combining regular expressions with Boolean algebra -3. creating complex patterns using string manipulation +1. verificar seu trabalho criando controles simples positivos e negativos +2. combinando expressões regulares com álgebra booleana +3. criação de padrões complexos usando manipulação de *strings* -### Check your work +### Verifique seu trabalho -First, let's find all sentences that start with "The". -Using the `^` anchor alone is not enough: +Primeiro, vamos encontrar todas as frases que começam com “The”. +Usar a âncora `^` por si só não é suficiente: ```{r} str_view(sentences, "^The") ``` -Because that pattern also matches sentences starting with words like `They` or `These`. -We need to make sure that the "e" is the last letter in the word, which we can do by adding a word boundary: +Porque esse padrão também corresponde a frases que começam com palavras como `They` ou `These`. +Precisamos ter certeza de que o “e” é a última letra da palavra, o que podemos fazer adicionando um limite (*boundary*) de palavra: ```{r} str_view(sentences, "^The\\b") ``` -What about finding all sentences that begin with a pronoun? +Que tal encontrar todas as frases que começam com um pronome? ```{r} str_view(sentences, "^She|He|It|They\\b") ``` -A quick inspection of the results shows that we're getting some spurious matches. -That's because we've forgotten to use parentheses: +Uma rápida inspeção dos resultados mostra que estamos obtendo algumas correspondências falsas. +Isso porque esquecemos de usar parênteses: ```{r} str_view(sentences, "^(She|He|It|They)\\b") ``` -You might wonder how you might spot such a mistake if it didn't occur in the first few matches. -A good technique is to create a few positive and negative matches and use them to test that your pattern works as expected: +Você deve estar se perguntando como identificar tal erro se ele não ocorreu nas primeiras correspondências. +Uma boa técnica é criar algumas correspondências positivas e negativas e usá-las para testar se o seu padrão funciona conforme o esperado: ```{r} -pos <- c("He is a boy", "She had a good time") -neg <- c("Shells come from the sea", "Hadley said 'It's a great day'") +pos <- c("Ele é jovem", "Elas se divertiram") +neg <- c("Eleitores não compareceram na data correta", "Hadley nos disse que é um bom dia!") -pattern <- "^(She|He|It|They)\\b" -str_detect(pos, pattern) -str_detect(neg, pattern) +padrao <- "^(Eu|Tu|Ele|Ela|Nós|Vós|Eles|Elas)\\b" +str_detect(pos, padrao) +str_detect(neg, padrao) ``` -It's typically much easier to come up with good positive examples than negative examples, because it takes a while before you're good enough with regular expressions to predict where your weaknesses are. -Nevertheless, they're still useful: as you work on the problem you can slowly accumulate a collection of your mistakes, ensuring that you never make the same mistake twice. +Normalmente é muito mais fácil encontrar bons exemplos positivos do que negativos, porque leva um tempo até que você seja bom o suficiente com expressões regulares para prever onde estão seus pontos fracos. +No entanto, eles ainda são úteis: à medida que você trabalha no problema, você pode acumular lentamente uma coleção de seus erros, garantindo que nunca cometerá o mesmo erro duas vezes. -### Boolean operations {#sec-boolean-operations} +### Operações booleanas {#sec-boolean-operations} -Imagine we want to find words that only contain consonants. -One technique is to create a character class that contains all letters except for the vowels (`[^aeiou]`), then allow that to match any number of letters (`[^aeiou]+`), then force it to match the whole string by anchoring to the beginning and the end (`^[^aeiou]+$`): +Imagine que queremos encontrar palavras que contenham apenas consoantes. +Uma técnica é criar uma classe de caracteres que contenha todas as letras, exceto as vogais (`[^aeiou]`), então permitir que corresponda a qualquer número de letras (`[^aeiou]+`) e, em seguida, forçá-la a corresponder a *string* inteira ancorando no início e no final (`^[^aeiou]+$`): ```{r} str_view(words, "^[^aeiou]+$") ``` -But you can make this problem a bit easier by flipping the problem around. -Instead of looking for words that contain only consonants, we could look for words that don't contain any vowels: +Mas você pode tornar esse problema um pouco mais fácil invertendo-o. +Em vez de procurar palavras que contenham apenas consoantes, poderíamos procurar palavras que não contenham vogais: ```{r} str_view(words[!str_detect(words, "[aeiou]")]) ``` -This is a useful technique whenever you're dealing with logical combinations, particularly those involving "and" or "not". -For example, imagine if you want to find all words that contain "a" and "b". -There's no "and" operator built in to regular expressions so we have to tackle it by looking for all words that contain an "a" followed by a "b", or a "b" followed by an "a": +Esta é uma técnica útil sempre que você estiver lidando com combinações lógicas, especialmente aquelas que envolvem “e” ou “não”. +Por exemplo, imagine se você deseja encontrar todas as palavras que contenham “a” e “b”. +Não há nenhum operador "e" embutido nas expressões regulares, então temos que resolver isso procurando todas as palavras que contenham um "a" seguido por um "b" ou um "b" seguido por um "a": ```{r} str_view(words, "a.*b|b.*a") ``` -It's simpler to combine the results of two calls to `str_detect()`: +É mais simples combinar os resultados de duas chamadas para `str_detect()`: ```{r} words[str_detect(words, "a") & str_detect(words, "b")] ``` -What if we wanted to see if there was a word that contains all vowels? -If we did it with patterns we'd need to generate 5! -(120) different patterns: +E se quiséssemos ver se existe uma palavra que contém todas as vogais? +Se fizéssemos isso com padrões precisaríamos gerar 5! +(120) padrões diferentes: ```{r} -#| results: false +#| resultado: falso words[str_detect(words, "a.*e.*i.*o.*u")] # ... words[str_detect(words, "u.*o.*i.*e.*a")] ``` -It's much simpler to combine five calls to `str_detect()`: +É muito mais simples combinar cinco chamadas para `str_detect()`: ```{r} words[ @@ -743,129 +743,129 @@ words[ ] ``` -In general, if you get stuck trying to create a single regexp that solves your problem, take a step back and think if you could break the problem down into smaller pieces, solving each challenge before moving onto the next one. +Em geral, se você ficar preso tentando criar um único regexp que resolva seu problema, dê um passo para trás e pense se você poderia dividir o problema em partes menores, resolvendo cada desafio antes de passar para o próximo.. -### Creating a pattern with code +### Criando um padrão com código -What if we wanted to find all `sentences` that mention a color? -The basic idea is simple: we just combine alternation with word boundaries. +E se quiséssemos encontrar todas as `sentences` que mencionam uma cor? +A ideia básica é simples: apenas combinamos alternância com limites de palavras (*boundary*). ```{r} str_view(sentences, "\\b(red|green|blue)\\b") ``` -But as the number of colors grows, it would quickly get tedious to construct this pattern by hand. -Wouldn't it be nice if we could store the colors in a vector? +Mas à medida que o número de cores aumenta, rapidamente se tornaria entediante construir esse padrão manualmente. +Não seria legal se pudéssemos armazenar as cores em um vetor? ```{r} rgb <- c("red", "green", "blue") ``` -Well, we can! -We'd just need to create the pattern from the vector using `str_c()` and `str_flatten()`: +Bem, nós podemos! +Precisaríamos apenas criar o padrão a partir do vetor usando `str_c()` e `str_flatten()`: ```{r} str_c("\\b(", str_flatten(rgb, "|"), ")\\b") ``` -We could make this pattern more comprehensive if we had a good list of colors. -One place we could start from is the list of built-in colors that R can use for plots: +Poderíamos tornar esse padrão mais abrangente se tivéssemos uma boa lista de cores. +Um lugar onde poderíamos começar é a lista de cores integradas que R usa para gerar gráficos: ```{r} str_view(colors()) ``` -But lets first eliminate the numbered variants: +Mas vamos primeiro eliminar as variantes numeradas: ```{r} -cols <- colors() -cols <- cols[!str_detect(cols, "\\d")] -str_view(cols) +cores <- colors() +cores <- cores[!str_detect(cores, "\\d")] +str_view(cores) ``` -Then we can turn this into one giant pattern. -We won't show the pattern here because it's huge, but you can see it working: +Então podemos transformar isso em um padrão gigante. +Não mostraremos o padrão aqui porque é enorme, mas você pode ver como ele funciona: ```{r} -pattern <- str_c("\\b(", str_flatten(cols, "|"), ")\\b") -str_view(sentences, pattern) +padrao <- str_c("\\b(", str_flatten(cores, "|"), ")\\b") +str_view(sentences, padrao) ``` -In this example, `cols` only contains numbers and letters so you don't need to worry about metacharacters. -But in general, whenever you create patterns from existing strings it's wise to run them through `str_escape()` to ensure they match literally. +Neste exemplo, `cores` contém apenas números e letras, então você não precisa se preocupar com metacaracteres. +Mas, em geral, sempre que você criar padrões a partir de strings existentes, é aconselhável executá-los em `str_escape()` para garantir que eles correspondam literalmente. -### Exercises +### Exercícios -1. For each of the following challenges, try solving it by using both a single regular expression, and a combination of multiple `str_detect()` calls. +1. Para cada um dos desafios a seguir, tente resolvê-los usando uma única expressão regular e uma combinação de múltiplas chamadas `str_detect()`. - a. Find all `words` that start or end with `x`. - b. Find all `words` that start with a vowel and end with a consonant. - c. Are there any `words` that contain at least one of each different vowel? + a. Encontre todas as palavras em `words` que começam ou terminam com `x`. + b. Encontre todas as palavras em `words` que começam com vogal e terminam com consoante. + c. Existe alguma palavra em `words` que contenha pelo menos uma de cada vogal diferente? -2. Construct patterns to find evidence for and against the rule "i before e except after c"? +2. Construa padrões para encontrar evidências a favor e contra a regra "i antes de e exceto depois de c"? -3. `colors()` contains a number of modifiers like "lightgray" and "darkblue". - How could you automatically identify these modifiers? - (Think about how you might detect and then remove the colors that are modified). +3. `colors()` contém vários modificadores de cores como "lightgray" (cinza claro) e "darkblue" (azul escuro). + Como você poderia identificar automaticamente esses modificadores (light, dark) ? + (Pense em como você pode detectar e remover as cores modificadas). -4. Create a regular expression that finds any base R dataset. - You can get a list of these datasets via a special use of the `data()` function: `data(package = "datasets")$results[, "Item"]`. - Note that a number of old datasets are individual vectors; these contain the name of the grouping "data frame" in parentheses, so you'll need to strip those off. +4. Crie uma expressão regular que encontre qualquer conjunto de dados do R. + Você pode obter uma lista desses conjuntos de dados através do uso especial da função `data()`: `data(package = "datasets")$results[, "Item"]`. + Observe que vários conjuntos de dados antigos são vetores individuais; eles contêm o nome do "*data frame*" agrupado entre parênteses, então você precisará retirá-los. -## Regular expressions in other places +## Expressões regulares em outros lugares -Just like in the stringr and tidyr functions, there are many other places in R where you can use regular expressions. -The following sections describe some other useful functions in the wider tidyverse and base R. +Assim como nas funções stringr e tidyr, existem muitos outros lugares no R onde você pode usar expressões regulares. +As seções a seguir descrevem algumas outras funções úteis no tidyverse e no R base. ### tidyverse -There are three other particularly useful places where you might want to use a regular expressions +Existem três outros lugares particularmente úteis onde você pode querer usar expressões regulares -- `matches(pattern)` will select all variables whose name matches the supplied pattern. - It's a "tidyselect" function that you can use anywhere in any tidyverse function that selects variables (e.g., `select()`, `rename_with()` and `across()`). +- `matches(padrao)` selecionará todas as variáveis ​​cujo nome corresponda ao padrão fornecido. + É uma função "tidyselect" que você pode usar em qualquer lugar em qualquer função tidyverse que selecione variáveis ​​(por exemplo, `select()`, `rename_with()` e `across()`). -- `pivot_longer()'s` `names_pattern` argument takes a vector of regular expressions, just like `separate_wider_regex()`. - It's useful when extracting data out of variable names with a complex structure +- O argumento `names_pattern` da função `pivot_longer()` recebe um vetor de expressão regular, assim como na `separate_wider_regex()`. + É útil ao extrair dados de nomes de variáveis ​​com uma estrutura complexa -- The `delim` argument in `separate_longer_delim()` and `separate_wider_delim()` usually matches a fixed string, but you can use `regex()` to make it match a pattern. - This is useful, for example, if you want to match a comma that is optionally followed by a space, i.e. `regex(", ?")`. +- O argumento `delim` na `separate_longer_delim()` e na `separate_wider_delim()` geralmente corresponde a uma string fixa, mas você pode usar `regex()` para fazê-lo corresponder a um padrão. + Isso é útil, por exemplo, se você deseja corresponder uma vírgula que é opcionalmente seguida por um espaço, ou seja, `regex(", ?")`. -### Base R +### R base -`apropos(pattern)` searches all objects available from the global environment that match the given pattern. -This is useful if you can't quite remember the name of a function: +`apropos(padrao)` pesquisa todos os objetos disponíveis no ambiente global que correspondem ao padrão fornecido. +Isto é útil se você não consegue lembrar o nome de uma função: ```{r} apropos("replace") ``` -`list.files(path, pattern)` lists all files in `path` that match a regular expression `pattern`. -For example, you can find all the R Markdown files in the current directory with: +`list.files(caminho, padrao)` lista todos os arquivos em `caminho` que correspondem a uma expressão regular `padrao`. +Por exemplo, você pode encontrar todos os arquivos R Markdown no diretório atual com: ```{r} head(list.files(pattern = "\\.Rmd$")) ``` -It's worth noting that the pattern language used by base R is very slightly different to that used by stringr. -That's because stringr is built on top of the [stringi package](https://stringi.gagolewski.com), which is in turn built on top of the [ICU engine](https://unicode-org.github.io/icu/userguide/strings/regexp.html), whereas base R functions use either the [TRE engine](https://github.com/laurikari/tre) or the [PCRE engine](https://www.pcre.org), depending on whether or not you've set `perl = TRUE`. -Fortunately, the basics of regular expressions are so well established that you'll encounter few variations when working with the patterns you'll learn in this book. -You only need to be aware of the difference when you start to rely on advanced features like complex Unicode character ranges or special features that use the `(?…)` syntax. +É importante notar que a linguagem de padrão usada pelo R base é ligeiramente diferente daquela usada pelo stringr.. +Isso ocorre porque stringr é construído sobre o [pacote stringi](https://stringi.gagolewski.com), que por sua vez é construído sobre o [mecanismo ICU](https://unicode-org.github.io /icu/userguide/strings/regexp.html), enquanto as funções base R usam o [mecanismo TRE](https://github.com/laurikari/tre) ou o [mecanismo PCRE](https://www.pcre .org), dependendo se você configurou ou não `perl = TRUE`. +Felizmente, os princípios básicos das expressões regulares estão tão bem estabelecidos que você encontrará poucas variações ao trabalhar com os padrões que aprenderá neste livro. +Você só precisa estar ciente da diferença quando começar a confiar em recursos avançados, como intervalos complexos de caracteres Unicode ou recursos especiais que usam a sintaxe `(?…)`. -## Summary +## Resumo -With every punctuation character potentially overloaded with meaning, regular expressions are one of the most compact languages out there. -They're definitely confusing at first but as you train your eyes to read them and your brain to understand them, you unlock a powerful skill that you can use in R and in many other places. +Com cada caractere de pontuação potencialmente sobrecarregado de significado, as expressões regulares são uma das linguagens mais compactas que existem. +Eles são definitivamente confusos no início, mas à medida que você treina seus olhos para lê-los e seu cérebro para entendê-los, você desbloqueia uma habilidade poderosa que pode ser usada em R e em muitos outros lugares. -In this chapter, you've started your journey to become a regular expression master by learning the most useful stringr functions and the most important components of the regular expression language. -And there are plenty of resources to learn more. +Neste capítulo, você iniciou sua jornada para se tornar um mestre em expressões regulares aprendendo as funções stringr mais úteis e os componentes mais importantes da linguagem de expressões regulares. +E ainda há muitos recursos para aprender mais. -A good place to start is `vignette("regular-expressions", package = "stringr")`: it documents the full set of syntax supported by stringr. -Another useful reference is [https://www.regular-expressions.info/](https://www.regular-expressions.info/tutorial.html). -It's not R specific, but you can use it to learn about the most advanced features of regexes and how they work under the hood. +Um bom lugar para começar é na `vignette("regular-expressions", package = "stringr")`: ela documenta o conjunto completo de sintaxe suportada pelo pacote stringr. +Outra referência útil é [https://www.regular-expressions.info/](https://www.regular-expressions.info/tutorial.html). +Não é específico do R, mas você pode usá-lo para aprender sobre os recursos mais avançados das expressões regulares e como elas funcionam. -It's also good to know that stringr is implemented on top of the stringi package by Marek Gagolewski. -If you're struggling to find a function that does what you need in stringr, don't be afraid to look in stringi. -You'll find stringi very easy to pick up because it follows many of the the same conventions as stringr. +Também é bom saber que stringr é implementado tendo o pacote stringi do Marek Gagolewski como base. +Se você está lutando para encontrar uma função que faça o que você precisa em stringr, não tenha medo de procurar no pacote stringi. +Você achará que stringi é muito fácil de aprender porque segue muitas das mesmas convenções que o stringr. -In the next chapter, we'll talk about a data structure closely related to strings: factors. -Factors are used to represent categorical data in R, i.e. data with a fixed and known set of possible values identified by a vector of strings. +No próximo capítulo, falaremos sobre uma estrutura de dados intimamente relacionada às *strings*: os fatores. +Fatores são usados ​​para representar dados categóricos em R, ou seja, dados com um conjunto fixo e conhecido de valores possíveis identificados por um vetor de *strings*. From 8faafdb0a251bca3cd43de7b8f0631d766006a91 Mon Sep 17 00:00:00 2001 From: Eric Scopinho Date: Tue, 5 Dec 2023 08:52:05 -0300 Subject: [PATCH 2/8] Fix: typo and some english words added --- regexps.qmd | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/regexps.qmd b/regexps.qmd index 7f663dae2..ac7f521e6 100644 --- a/regexps.qmd +++ b/regexps.qmd @@ -11,19 +11,19 @@ source("_common.R") ## Introdução No @sec-strings, você aprendeu diversas funções úteis para trabalhar com *strings*. -Este capítulo se concentrará em funções que usam **expressões regulares**, uma linguagem concisa e poderosa para descrever padrões dentro de *strings*. -O termo "expressão regular" é um pouco longo, então a maioria das pessoas o abrevia para "regex"[^regexps-1] ou "regexp". +Este capítulo se concentrará em funções que usam **expressões regulares**, uma linguagem concisa e poderosa para descrever padrões (*patterns*) dentro de cadeia de caracteres (*strings*). +O termo "expressão regular" é um pouco longo, então a maioria das pessoas o abrevia como "regex"[^regexps-1] ou "regexp". [^regexps-1]: Você pode pronunciá-lo com um g-seco (reg-x) ou um g-suave (rej-x). O capítulo começa com os conceitos básicos de expressões regulares e as funções do pacote stringr mais úteis para análise de dados. Em seguida, expandiremos seu conhecimento sobre padrões (*patterns*) e cobriremos sete novos tópicos importantes (escape, ancoragem, classes de caracteres, classes de abreviações, quantificadores, precedência de agrupamento). -A seguir, falaremos sobre alguns dos outros tipos de padrões com os quais as funções stringr podem trabalhar e os vários "sinalizadores" que permitem ajustar a operação de expressões regulares. +A seguir, falaremos sobre alguns dos outros tipos de padrões com os quais as funções do pacote stringr podem trabalhar e os vários "sinalizadores" (*flags*) que permitem ajustar a operação das expressões regulares. Terminaremos com uma pesquisa de outros lugares do tidyverse e no R base onde você pode usar expressões regulares. ### Pré-requisitos -Neste capítulo, usaremos funções de expressão regular dos pacotes stringr e tidyr, ambos membros principais do tidyverse, bem como dados bebes do pacote dados. +Neste capítulo, usaremos funções com expressão regular dos pacotes stringr e tidyr, ambos membros principais do tidyverse, bem como o conjunto de dados bebes do pacote dados. ```{r} #| label: setup @@ -51,20 +51,20 @@ Os padrões mais simples consistem em letras e números que correspondem exatame str_view(fruit, "berry") ``` -Letras e números correspondem exatamente à seus respectivos caracteres e são chamados **caracteres literais**. -A maioria dos caracteres de pontuação, como `.`, `+`, `*`, `[`, `]` e `?`, tem significados especiais[^regexps-2] e são chamado de **metacaracteres**. Por exemplo, `.` +Letras e números tem uma correspondência direta à seus respectivos caracteres e são chamados **caracteres literais**. +A maioria dos caracteres de pontuação, como `.`, `+`, `*`, `[`, `]` e `?`, tem significados especiais[^regexps-2] e são chamados de **metacaracteres**. Por exemplo, `.` corresponde a qualquer caractere[^regexps-3], portanto `"a."` irá corresponder a qualquer *string* que contenha a letra "a" seguida por qualquer outro caractere : -[^regexps-2]: Você aprenderá como escapar destes significados especiais na @sec-regexp-escaping. +[^regexps-2]: Você aprenderá como escapar (*escape*) destes significados especiais na @sec-regexp-escaping. -[^regexps-3]: Bem, qualquer caractere exceto `\n`. +[^regexps-3]: Bem, qualquer caractere exceto `\n` (nova linha). ```{r} str_view(c("a", "ab", "ae", "bd", "ea", "eab"), "a.") ``` -Ou, podemos encontrar todas as frutas que contèm um "a", seguido por três letras, seguidas por um "e": +Ou, podemos encontrar todas as frutas que contém um "a", seguido por três letras, seguidas por um "e": ```{r} str_view(fruit, "a...e") @@ -72,7 +72,7 @@ str_view(fruit, "a...e") **Quantificadores** controlam quantas vezes um padrão pode ser encontrado: -- `?` torna um padrão opcional (e.x. corresponde a 0 ou 1 vezes) +- `?` torna um padrão opcional (e.x. corresponde a 0 ou 1 vez) - `+` permite que o padrão se repita (e.x. corresponde ao menos uma vez) - `*` permite que o padrão seja opcional ou se repita (e.x. corresponde a qualquer número de vezes, incluindo 0). @@ -105,7 +105,7 @@ str_view(fruit, "aa|ee|ii|oo|uu") ``` Expressões regulares são bastante compactas e usam diversos caracteres de pontuação, portanto elas podem parecer difíceis de ler e entender no início. -Não se preocupe; você as entenderá melhor com a prática, e padrões simples se tornarão naturais. +Não se preocupe; você as entenderá melhor com a prática, e padrões simples se tornarão mais naturais com o tempo. Vamos iniciar este processo praticando com algumas funções úteis do stringr. ## Funções chaves {#sec-stringr-regex-funs} @@ -113,7 +113,7 @@ Vamos iniciar este processo praticando com algumas funções úteis do stringr. Agora que você tem o básico das expressões regulares em seu cinto de utilidades, vamos usá-las em algumas funções dos pacotes stringr e tidyr. Na seção a seguir, você aprenderá como detectar a presença ou ausência de correspondências (*matches*), como contar o número de correspondências, como trocar uma correspondência por um texto fixo e como extrair textos usando um padrão (*pattern*). -### Detectando correspondência +### Detectando correspondências `str_detect()` retorna uma vetor lógico que ém `TRUE` se o padrão corresponde a um elemento do vetor de caracteres e `FALSE` se não corresponde: @@ -121,7 +121,7 @@ Na seção a seguir, você aprenderá como detectar a presença ou ausência de str_detect(c("a", "b", "c"), "[aeiou]") ``` -Uma vez que `str_detect()` retorna um vetor lógico de mesmo tamanho que o vetor inicial, ele vai muito bem com a função `filter()`. +Uma vez que `str_detect()` retorna um vetor lógico de mesmo tamanho que o vetor inicial, ele combina muito bem com a função `filter()`. Por exemplo, este código encontra todos os nomes mais populares contendo um "x" minúsculo: ```{r} @@ -130,17 +130,17 @@ bebes |> count(nome, wt = n, sort = TRUE) ``` -Podemos também usar a`str_detect()` com `summarize()` combinando-a com `sum()` ou `mean()`: `sum(str_detect(x, padrao))` informa o numero de observações correspondentes e `mean(str_detect(x, padrao))` informa a proporção da correspondência. +Podemos também usar a`str_detect()` com `summarize()` combinando-a com `sum()` ou `mean()`: `sum(str_detect(x, padrao))` informa o numero de observações correspondentes ao padrão e `mean(str_detect(x, padrao))` informa a proporção da correspondência. Por exemplo, o código a seguir, calcula e visualiza a proporção de nomes de bebês [^regexps-4] que contém "x", agrupados por ano. Parece que eles aumentaram em popularidade ultimamente! -[^regexps-4]: Isto nos dá a proporção de **nomes** que contém um "x"; se você quiser a proporção de bebês com um nome contendo um x, você deveria usar uma média ponderada. +[^regexps-4]: Isto nos dá a proporção de **nomes** que contém um "x"; se você quiser a proporção de bebês com um nome contendo um x, você deve usar uma média ponderada. ```{r} #| fig-alt: | -#| A time series showing the proportion of baby names that contain the letter x. -#| The proportion declines gradually from 8 per 1000 in 1880 to 4 per 1000 in -#| 1980, then increases rapidly to 16 per 1000 in 2019. +#| Uma série temporal mostrando a proporção de nomes de bebês que contém a letra x. +#| A proporção cai gradualmente de 8 por 1000 em 1880 para 4 por 1000 em +#| 1980, então aumenta rapidamente para 16 por 1000 em 2019. bebes |> group_by(ano) |> @@ -155,7 +155,7 @@ Existem duas funções que se relacionam muito com a `str_detect()`: `str_subset ### Contando correspondências (*matches*) -O próximo passo em complexidade de `str_detect()` é `str_count()`: em vez de verdadeiro (*true*) ou falso (*false*), ele informa quantas correspondências existem em cada *string*. +O próximo passo em complexidade de `str_detect()` é `str_count()`: em vez de verdadeiro (*true*) ou falso (*false*), ele informa quantas correspondências (*matches*) existem em cada *string*. ```{r} x <- c("apple", "banana", "pear") @@ -163,8 +163,8 @@ str_count(x, "p") ``` Note que cada correspondência começa ao final da anterior, e.x. regex nunca se sobrepõem. -Por exemplo, em `"abababa"`, quantas vezes o padrçao `"aba"` é correspondido? -A expressão regular dizem, duas, não três vezes: +Por exemplo, em `"abababa"`, quantas vezes o padrão (*pattern*) `"aba"` é correspondido? +A expressão regular diz, duas e não três vezes: ```{r} str_count("abababa", "aba") From 3125268303c7160f4a5e5ae7350ef04f26d64182 Mon Sep 17 00:00:00 2001 From: Eric Scopinho Date: Tue, 5 Dec 2023 08:58:05 -0300 Subject: [PATCH 3/8] fix:removed extra lines left in english --- regexps.qmd | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/regexps.qmd b/regexps.qmd index ac7f521e6..ce7081258 100644 --- a/regexps.qmd +++ b/regexps.qmd @@ -317,9 +317,6 @@ str_view(ponto) str_view(c("abc", "a.c", "bef"), "a\\.c") ``` -In this book, we'll usually write regular expression without quotes, like `\.`. -If we need to emphasize what you'll actually type, we'll surround it with quotes and add extra escapes, like `"\\."`. - Neste livro, normalmente escreveremos expressões regulares sem aspas, como `\.`. Se precisarmos enfatizar o que você realmente digitará, colocaremos aspas e adicionaremos escapadas extras, como `"\\."`. Para criar essa expressão regular, você precisa usar uma string, que também precisa escapar de `\`. @@ -331,7 +328,7 @@ str_view(x) str_view(x, "\\\\") ``` -Alternatively, you might find it easier to use the raw strings you learned about in @sec-raw-strings). +Alternativamente, você pode achar mais fácil usar *strings* brutas (*raw*) que você emprendeu na @sec-raw-strings. That lets you avoid one layer of escaping: ```{r} From 5bfa9456b4e44da93164d77634098ea056445d51 Mon Sep 17 00:00:00 2001 From: Eric Scopinho Date: Tue, 5 Dec 2023 11:11:39 -0300 Subject: [PATCH 4/8] =?UTF-8?q?Tradu=C3=A7=C3=A3o:=20factors.qmd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- factors.qmd | 435 ++++++++++++++++++++++++++-------------------------- 1 file changed, 218 insertions(+), 217 deletions(-) diff --git a/factors.qmd b/factors.qmd index 37eb77451..1ca83bc12 100644 --- a/factors.qmd +++ b/factors.qmd @@ -1,440 +1,441 @@ -# Factors {#sec-factors} +# Fatores {#sec-factors} ```{r} #| echo: false #| results: asis source("_common.R") -mensagem_capitulo_sem_traducao() + ``` -## Introduction +## Introdução -Factors are used for categorical variables, variables that have a fixed and known set of possible values. -They are also useful when you want to display character vectors in a non-alphabetical order. +Fatores (*factors*) são usados ​​para variáveis ​​categóricas, variáveis ​​que possuem um conjunto fixo e conhecido de valores possíveis. +Eles também são úteis quando você deseja exibir vetores de caracteres em ordem não alfabética. -We'll start by motivating why factors are needed for data analysis[^factors-1] and how you can create them with `factor()`. We'll then introduce you to the `gss_cat` dataset which contains a bunch of categorical variables to experiment with. -You'll then use that dataset to practice modifying the order and values of factors, before we finish up with a discussion of ordered factors. +Começaremos motivando porque os fatores são necessários para a análise de dados[^factors-1] e como você pode criá-los com a fnução `factor()`. Em seguida, apresentaremos o conjunto de dados `questionario` do pacote dados, que contém um monte de variáveis ​​​​categóricas para experimentarmos. +Em seguida, você usará esse conjunto de dados para praticar a modificação da ordem e dos valores dos fatores, antes de terminarmos com uma discussão sobre fatores ordenados. -[^factors-1]: They're also really important for modelling. +[^factors-1]: Eles também são muito importantes para modelagem. -### Prerequisites +### Pré-requisitos -Base R provides some basic tools for creating and manipulating factors. -We'll supplement these with the **forcats** package, which is part of the core tidyverse. -It provides tools for dealing with **cat**egorical variables (and it's an anagram of factors!) using a wide range of helpers for working with factors. +O R base fornece algumas ferramentas básicas para criar e manipular fatores. +Iremos complementá-las com o pacote **forcats**, que é um integrante do tidyverse. +Ele fornece ferramentas para lidar com variáveis ​​**cat**egóricas (e é um anagrama de fatores!) usando uma ampla gama de funções auxiliares para trabalhar com fatores. ```{r} #| label: setup #| message: false library(tidyverse) +library(dados) ``` -## Factor basics +## O básico sobre fatores -Imagine that you have a variable that records month: +Imagine que você tem uma variável que registra o mês: ```{r} -x1 <- c("Dec", "Apr", "Jan", "Mar") +x1 <- c("Dez", "Abr", "Jan", "Mar") ``` -Using a string to record this variable has two problems: +Usar uma string para registrar esta variável tem dois problemas: -1. There are only twelve possible months, and there's nothing saving you from typos: +1. Existem apenas doze meses possíveis e não há nada que o salve de erros de digitação: ```{r} - x2 <- c("Dec", "Apr", "Jam", "Mar") + x2 <- c("Dez", "Abr", "Jam", "Mar") ``` -2. It doesn't sort in a useful way: +2. Não ordena de maneira útil: ```{r} sort(x1) ``` -You can fix both of these problems with a factor. -To create a factor you must start by creating a list of the valid **levels**: +Você pode corrigir esses dois problemas com um fator. +Para criar um fator você deve começar criando uma lista com **níveis** (*levels*) válidos: ```{r} -month_levels <- c( - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +niveis_meses <- c( + "Jan", "Fev", "Mar", "Abr", "Mai", "Jun", + "Jul", "Ago", "Set", "Out", "Nov", "Dez" ) ``` -Now you can create a factor: +Agora você pode criar o fator: ```{r} -y1 <- factor(x1, levels = month_levels) +y1 <- factor(x1, levels = niveis_meses) y1 sort(y1) ``` -And any values not in the level will be silently converted to NA: +E quaisquer valores que não estejam nos níveis serão convertidos silenciosamente para NA: ```{r} -y2 <- factor(x2, levels = month_levels) +y2 <- factor(x2, levels = niveis_meses) y2 ``` -This seems risky, so you might want to use `forcats::fct()` instead: +Isso parece arriscado, então você pode querer usar `forcats::fct()` em vez disso: ```{r} #| error: true -y2 <- fct(x2, levels = month_levels) +y2 <- fct(x2, levels = niveis_meses) ``` -If you omit the levels, they'll be taken from the data in alphabetical order: +Se você omitir os níveis, eles serão retirados dos dados em ordem alfabética: ```{r} factor(x1) ``` -Sorting alphabetically is slightly risky because not every computer will sort strings in the same way. -So `forcats::fct()` orders by first appearance: +Classificar em ordem alfabética é um pouco arriscado porque nem todo computador classificará as *strings* da mesma maneira. +Então `forcats::fct()` ordena pela primeira aparição: ```{r} fct(x1) ``` -If you ever need to access the set of valid levels directly, you can do so with `levels()`: +Se você precisar acessar diretamente o conjunto de níveis válidos, poderá fazê-lo com `levels()`: ```{r} levels(y2) ``` -You can also create a factor when reading your data with readr with `col_factor()`: +Você também pode criar um fator ao ler seus dados com readr usando a `col_factor()`: ```{r} csv <- " -month,value +mes,valor Jan,12 -Feb,56 +Fev,56 Mar,12" -df <- read_csv(csv, col_types = cols(month = col_factor(month_levels))) -df$month +df <- read_csv(csv, col_types = cols(mes = col_factor(niveis_meses))) +df$mes ``` -## General Social Survey +## Questionario -For the rest of this chapter, we're going to use `forcats::gss_cat`. -It's a sample of data from the [General Social Survey](https://gss.norc.org), a long-running US survey conducted by the independent research organization NORC at the University of Chicago. -The survey has thousands of questions, so in `gss_cat` Hadley selected a handful that will illustrate some common challenges you'll encounter when working with factors. +No restante deste capítulo, usaremos `dados::questionario`. +É uma amostra de dados da [Pesquisa Social Geral](https://gss.norc.org), uma pesquisa de longa data nos EUA conduzida pela organização de pesquisa independente NORC da Universidade de Chicago. +A pesquisa tem milhares de perguntas, então em `questionario`, Hadley selecionou algumas que ilustrarão alguns desafios comuns que você encontrará ao trabalhar com fatores. ```{r} -gss_cat +questionario ``` -(Remember, since this dataset is provided by a package, you can get more information about the variables with `?gss_cat`.) +(Lembre-se, como este conjunto de dados é fornecido por um pacote, você pode obter mais informações sobre as variáveis ​​com `?questionario`.) -When factors are stored in a tibble, you can't see their levels so easily. -One way to view them is with `count()`: +Quando os fatores são armazenados em um tibble, você não consegue ver seus níveis tão facilmente. +Uma maneira de visualizá-los é com `count()`: ```{r} -gss_cat |> - count(race) +questionario |> + count(raca) ``` -When working with factors, the two most common operations are changing the order of the levels, and changing the values of the levels. -Those operations are described in the sections below. +Ao trabalhar com fatores, as duas operações mais comuns são alterar a ordem dos níveis e alterar os valores dos níveis. +Essas operações são descritas nas seções abaixo. -### Exercise +### Exercícios -1. Explore the distribution of `rincome` (reported income). - What makes the default bar chart hard to understand? - How could you improve the plot? +1. Explore a distribuição de `renda` (renda informada). + O que torna o gráfico de barras padrão difícil de ser entendido? + Como você pode melhorar o gráfico? -2. What is the most common `relig` in this survey? - What's the most common `partyid`? +2. Qual a `religiao` mais comum neste questionário? + Qual o `partido` mais comum? -3. Which `relig` does `denom` (denomination) apply to? - How can you find out with a table? - How can you find out with a visualization? +3. Qual `denominacao` se aplica a qual `religiao`? + Como você pode descobrir isso com uma tabela? + Como você pode descobrir isso com uma visualização? -## Modifying factor order {#sec-modifying-factor-order} +## Modificando a ordem de um fator {#sec-modifying-factor-order} -It's often useful to change the order of the factor levels in a visualization. -For example, imagine you want to explore the average number of hours spent watching TV per day across religions: +Muitas vezes é útil alterar a ordem dos níveis dos fatores em uma visualização. +Por exemplo, imagine que você deseja explorar o número médio de horas gastas assistindo TV por dia em todas as religiões: ```{r} #| fig-alt: | -#| A scatterplot of with tvhours on the x-axis and religion on the y-axis. -#| The y-axis is ordered seemingly aribtrarily making it hard to get -#| any sense of overall pattern. -relig_summary <- gss_cat |> - group_by(relig) |> +#| Im gráfico de dispersão com horas_tv no eixo-x e religião no eixo-y. +#| O eixo-y parece estar ordenado de forma arbitrária, tornando difícil entender +#| qualquer padrão geral. +sumario_religiao <- questionario |> + group_by(religiao) |> summarize( - tvhours = mean(tvhours, na.rm = TRUE), + horas_tv = mean(horas_tv, na.rm = TRUE), n = n() ) -ggplot(relig_summary, aes(x = tvhours, y = relig)) + +ggplot(sumario_religiao, aes(x = horas_tv, y = religiao)) + geom_point() ``` -It is hard to read this plot because there's no overall pattern. -We can improve it by reordering the levels of `relig` using `fct_reorder()`. -`fct_reorder()` takes three arguments: +É difícil ler esse gráfico porque não existe um padrão geral. +Podemos melhorá-lo reordenando os níveis de `religiao` usando `fct_reorder()`. +`fct_reorder()` possui três argumentos: -- `f`, the factor whose levels you want to modify. -- `x`, a numeric vector that you want to use to reorder the levels. -- Optionally, `fun`, a function that's used if there are multiple values of `x` for each value of `f`. The default value is `median`. +- `f`, o fator cujos níveis você deseja modificar. +- `x`, um vetor numérico que você deseja usar para reordenar os níveis. +- Opcionalmente, `fun`, uma função que é usada se houver vários valores de `x` para cada valor de `f`. A função padrão é `median`. ```{r} #| fig-alt: | -#| The same scatterplot as above, but now the religion is displayed in -#| increasing order of tvhours. "Other eastern" has the fewest tvhours -#| under 2, and "Don't know" has the highest (over 5). -ggplot(relig_summary, aes(x = tvhours, y = fct_reorder(relig, tvhours))) + +#| O mesmo gráfico de dispersão acima, mas agora a religião é exibida em +#| ordem crescente de horas_tv. "Outra religião oriental Leste" tem o menor número de horas de TV +#| menos de 2, e “Não sabe” tem o valor mais alto (acima de 5). +ggplot(sumario_religiao, aes(x = horas_tv, y = fct_reorder(religiao, horas_tv))) + geom_point() ``` -Reordering religion makes it much easier to see that people in the "Don't know" category watch much more TV, and Hinduism & Other Eastern religions watch much less. +Reordenar a religião torna muito mais fácil ver que as pessoas na categoria “Não sabe” assistem muito mais TV, e o Hinduísmo e outras religiões orientais assistem muito menos. -As you start making more complicated transformations, we recommend moving them out of `aes()` and into a separate `mutate()` step. -For example, you could rewrite the plot above as: +À medida que você começa a fazer transformações mais complicadas, recomendamos movê-las de `aes()` para uma etapa separada `mutate()`. +Por exemplo, você poderia reescrever o gráfico acima como: ```{r} #| eval: false -relig_summary |> +sumario_religiao |> mutate( - relig = fct_reorder(relig, tvhours) + religiao = fct_reorder(religiao, horas_tv) ) |> - ggplot(aes(x = tvhours, y = relig)) + + ggplot(aes(x = horas_tv, y = religiao)) + geom_point() ``` -What if we create a similar plot looking at how average age varies across reported income level? +E se criarmos um gráfico semelhante observando como a idade média varia de acordo com o nível de renda informada? ```{r} #| fig-alt: | -#| A scatterplot with age on the x-axis and income on the y-axis. Income -#| has been reordered in order of average age which doesn't make much -#| sense. One section of the y-axis goes from $6000-6999, then <$1000, -#| then $8000-9999. -rincome_summary <- gss_cat |> - group_by(rincome) |> +#| Um gráfico de dispersão com idade no eixo-x e renda no eixo-y. Renda +#| foi reordenado em ordem de idade média, o que não faz muito +#| sentido. Uma seção do eixo-y vai de US$ 6.000 a 6.999, depois < US$ 1.000, +#| então $8000-9999. +sumario_renda <- questionario |> + group_by(renda) |> summarize( - age = mean(age, na.rm = TRUE), + idade = mean(idade, na.rm = TRUE), n = n() ) -ggplot(rincome_summary, aes(x = age, y = fct_reorder(rincome, age))) + +ggplot(sumario_renda, aes(x = idade, y = fct_reorder(renda, idade))) + geom_point() ``` -Here, arbitrarily reordering the levels isn't a good idea! -That's because `rincome` already has a principled order that we shouldn't mess with. -Reserve `fct_reorder()` for factors whose levels are arbitrarily ordered. +Aqui, reordenar arbitrariamente os níveis não é uma boa ideia! +Isso porque o `renda` já tem uma ordem de princípios com a qual não devemos mexer. +Reserve `fct_reorder()` para fatores cujos níveis são ordenados arbitrariamente. -However, it does make sense to pull "Not applicable" to the front with the other special levels. -You can use `fct_relevel()`. -It takes a factor, `f`, and then any number of levels that you want to move to the front of the line. +No entanto, faz sentido colocar "Não se aplica" na frente com os outros níveis especiais. +Você pode usar `fct_relevel()`. +É necessário um fator, `f`, e então qualquer número de níveis que você deseja mover para o início da linha. ```{r} #| fig-alt: | -#| The same scatterplot but now "Not Applicable" is displayed at the -#| bottom of the y-axis. Generally there is a positive association -#| between income and age, and the income band with the highethst average -#| age is "Not applicable". +#| O mesmo gráfico de dispersão, mas agora "Não se aplica", é exibido no +#| parte inferior do eixo-y. Geralmente há uma associação positiva +#| entre renda e idade, e a faixa de renda com maior média +#| idade é "Não se aplica". -ggplot(rincome_summary, aes(x = age, y = fct_relevel(rincome, "Not applicable"))) + +ggplot(sumario_renda, aes(x = idade, y = fct_relevel(renda, "Não se aplica"))) + geom_point() ``` -Why do you think the average age for "Not applicable" is so high? +Por que você acha que a média de idade para “Não se aplica” é tão alta? -Another type of reordering is useful when you are coloring the lines on a plot. -`fct_reorder2(f, x, y)` reorders the factor `f` by the `y` values associated with the largest `x` values. -This makes the plot easier to read because the colors of the line at the far right of the plot will line up with the legend. +Outro tipo de reordenação é útil quando você está colorindo as linhas em um gráfico. +`fct_reorder2(f, x, y)` reordena o fator `f` pelos valores `y` associados aos maiores valores `x`. +Isso torna o gráfico mais fácil de ler porque as cores da linha na extremidade direita do gráfico se alinharão com a legenda. ```{r} #| layout-ncol: 2 #| fig-width: 3 #| fig-alt: | -#| A line plot with age on the x-axis and proportion on the y-axis. -#| There is one line for each category of marital status: no answer, -#| never married, separated, divorced, widowed, and married. It is -#| a little hard to read the plot because the order of the legend is -#| unrelated to the lines on the plot. Rearranging the legend makes -#| the plot easier to read because the legend colors now match the -#| order of the lines on the far right of the plot. You can see some -#| unsurprising patterns: the proportion never married decreases with -#| age, married forms an upside down U shape, and widowed starts off -#| low but increases steeply after age 60. -by_age <- gss_cat |> - filter(!is.na(age)) |> - count(age, marital) |> - group_by(age) |> +#| Um gráfico de linha com idade no eixo-x e proporção no eixo-y. +#| Há uma linha para cada categoria de estado civil: sem resposta, +#| nunca casou, separado(a), divorciado(a), viúvo(a) e casado(a). Isso é +#| um pouco difícil de ler o gráfico porque a ordem da legenda não é +#| relacionada às linhas do gráfico. Reorganizar a legenda faz +#| o gráfico ser mais fácil de ser lido porque as cores da legenda agora correspondem à +#| ordem das linhas na extremidade direita do gráfico. Você pode ver alguns +#| padrões não surpreendentes: a proporção de nunca casados(as) ​​diminui comh +#| idade, casado(a) forma um U de cabeça para baixo e viúvo(a) começa +#| baixo, mas aumenta acentuadamente após os 60 anos. +por_idade <- questionario |> + filter(!is.na(idade)) |> + count(idade, estado_civil) |> + group_by(idade) |> mutate( prop = n / sum(n) ) -ggplot(by_age, aes(x = age, y = prop, color = marital)) + +ggplot(por_idade, aes(x = idade, y = prop, color = estado_civil)) + geom_line(linewidth = 1) + scale_color_brewer(palette = "Set1") -ggplot(by_age, aes(x = age, y = prop, color = fct_reorder2(marital, age, prop))) + +ggplot(por_idade, aes(x = idade, y = prop, color = fct_reorder2(estado_civil, idade, prop))) + geom_line(linewidth = 1) + scale_color_brewer(palette = "Set1") + - labs(color = "marital") + labs(color = "estado civil") ``` -Finally, for bar plots, you can use `fct_infreq()` to order levels in decreasing frequency: this is the simplest type of reordering because it doesn't need any extra variables. -Combine it with `fct_rev()` if you want them in increasing frequency so that in the bar plot largest values are on the right, not the left. +Finalmente, para gráficos de barras, você pode usar `fct_infreq()` para ordenar os níveis em frequência decrescente: este é o tipo mais simples de reordenação porque não precisa de nenhuma variável extra.. +Combine-a com a `fct_rev()` se desejar que eles aumentem a frequência, de modo que no gráfico de barras os maiores valores fiquem à direita, não à esquerda. ```{r} #| fig-alt: | -#| A bar char of marital status ordered in from least to most common: -#| no answer (~0), separated (~1,000), widowed (~2,000), divorced -#| (~3,000), never married (~5,000), married (~10,000). -gss_cat |> - mutate(marital = marital |> fct_infreq() |> fct_rev()) |> - ggplot(aes(x = marital)) + +#| Um gráfico de barras de estado cicil ordenado do menos para mais comum: +#| sem resposta (~0), separado(a) (~1,000), viúvo(a) (~2,000), divorciado(a) +#| (~3,000), nuca casou (~5,000), casado(a) (~10,000). +questionario |> + mutate(estado_civil = estado_civil |> fct_infreq() |> fct_rev()) |> + ggplot(aes(x = estado_civil)) + geom_bar() ``` -### Exercises +### Exercícios -1. There are some suspiciously high numbers in `tvhours`. - Is the mean a good summary? +1. Existem alguns números suspeitosamente altos em `horas_tv`. + A média é uma boa sumarização? -2. For each factor in `gss_cat` identify whether the order of the levels is arbitrary or principled. +2. Para cada fator em `quesitonario` identifique se a ordem dos níveis é arbitrária ou baseada em princípios. -3. Why did moving "Not applicable" to the front of the levels move it to the bottom of the plot? +3. Por que mover "Não se aplica" para a frente dos níveis o moveu para a parte inferior do gráfico?? -## Modifying factor levels +## Modificando os níveis do fator -More powerful than changing the orders of the levels is changing their values. -This allows you to clarify labels for publication, and collapse levels for high-level displays. -The most general and powerful tool is `fct_recode()`. -It allows you to recode, or change, the value of each level. -For example, take the `partyid` variable from the `gss_cat` data frame: +Mais poderoso do que alterar as ordens dos níveis é alterar os seus valores. +Isso permite esclarecer rótulos para publicação e recolher níveis para exibições de alto nível. +A ferramenta mais geral e poderosa é `fct_recode()`. +Ele permite recodificar ou alterar o valor de cada nível. +Por exemplo, pegue a variável `partido` do *data frame* `questionario`: ```{r} -gss_cat |> count(partyid) +questionario |> count(partido) ``` -The levels are terse and inconsistent. -Let's tweak them to be longer and use a parallel construction. -Like most rename and recoding functions in the tidyverse, the new values go on the left and the old values go on the right: +Os níveis são concisos e inconsistentes. +Vamos ajustá-los para serem mais longos e usar uma construção paralela. +Como a maioria das funções de renomeação e recodificação no tidyverse, os novos valores ficam à esquerda e os valores antigos à direita: ```{r} -gss_cat |> +questionario |> mutate( - partyid = fct_recode(partyid, - "Republican, strong" = "Strong republican", - "Republican, weak" = "Not str republican", - "Independent, near rep" = "Ind,near rep", - "Independent, near dem" = "Ind,near dem", - "Democrat, weak" = "Not str democrat", - "Democrat, strong" = "Strong democrat" + partido = fct_recode(partido, + "Indivíduo fortemente republicano" = "Fortemente republicano", + "Indivíduo não fortemente republicano" = "Não fortemente repubicano", + "Indivíduo independente com inclinação republicana" = "Independente, inclinação republicana", + "Indivíduo independente com inclinação democrata" = "Independente, inclinação democrata", + "Indivíduo não fortemente democrata" = "Não fortemente democrata", + "Indivíduo fortemente democrata" = "Fortemente democrata" ) ) |> - count(partyid) + count(partido) ``` -`fct_recode()` will leave the levels that aren't explicitly mentioned as is, and will warn you if you accidentally refer to a level that doesn't exist. +`fct_recode()` deixará os níveis que não são explicitamente mencionados como estão e irá avisá-lo se você acidentalmente se referir a um nível que não existe. -To combine groups, you can assign multiple old levels to the same new level: +Para combinar grupos, você pode atribuir vários níveis antigos a um mesmo nível novo: ```{r} #| results: false -gss_cat |> +questionario |> mutate( - partyid = fct_recode(partyid, - "Republican, strong" = "Strong republican", - "Republican, weak" = "Not str republican", - "Independent, near rep" = "Ind,near rep", - "Independent, near dem" = "Ind,near dem", - "Democrat, weak" = "Not str democrat", - "Democrat, strong" = "Strong democrat", - "Other" = "No answer", - "Other" = "Don't know", - "Other" = "Other party" + partido = fct_recode(partido, + "Indivíduo fortemente republicano" = "Fortemente republicano", + "Indivíduo não fortemente republicano" = "Não fortemente repubicano", + "Indivíduo independente com inclinação republicana" = "Independente, inclinação republicana", + "Indivíduo independente com inclinação democrata" = "Independente, inclinação democrata", + "Indivíduo não fortemente democrata" = "Não fortemente democrata", + "Indivíduo fortemente democrata" = "Fortemente democrata", + "Outro" = "Sem resposta", + "Outro" = "Não sabe", + "Outro" = "Outro partido" ) ) ``` -Use this technique with care: if you group together categories that are truly different you will end up with misleading results. +Use esta técnica com cuidado: se você agrupar categorias que são realmente diferentes, você acabará com resultados enganosos. -If you want to collapse a lot of levels, `fct_collapse()` is a useful variant of `fct_recode()`. -For each new variable, you can provide a vector of old levels: +Se você deseja recolher vários níveis, `fct_collapse()` é uma variante útil de `fct_recode()`. +Para cada nova variável, você pode fornecer um vetor de níveis antigos: ```{r} -gss_cat |> +questionario |> mutate( - partyid = fct_collapse(partyid, - "other" = c("No answer", "Don't know", "Other party"), - "rep" = c("Strong republican", "Not str republican"), - "ind" = c("Ind,near rep", "Independent", "Ind,near dem"), - "dem" = c("Not str democrat", "Strong democrat") + partido = fct_collapse(partido, + "other" = c("Sem resposta", "Não sabe", "Outro partido"), + "rep" = c("Fortemente republicano", "Não fortemente repubicano"), + "ind" = c("Independente, inclinação republicana", "Independente", "Independente, inclinação democrata"), + "dem" = c("Não fortemente democrata", "Fortemente democrata") ) ) |> - count(partyid) + count(partido) ``` -Sometimes you just want to lump together the small groups to make a plot or table simpler. -That's the job of the `fct_lump_*()` family of functions. -`fct_lump_lowfreq()` is a simple starting point that progressively lumps the smallest groups categories into "Other", always keeping "Other" as the smallest category. +Às vezes, você só quer juntar em pequenos grupos para simplificar um gráfico ou uma tabela. +Esse é o trabalho da família de funções `fct_lump_*()`. +`fct_lump_lowfreq()` é um ponto de partida simples que agrupa progressivamente as categorias dos menores grupos em "Outros", sempre mantendo "Outros" como a menor categoria. ```{r} -gss_cat |> - mutate(relig = fct_lump_lowfreq(relig)) |> - count(relig) +questionario |> + mutate(religiao = fct_lump_lowfreq(religiao)) |> + count(religiao) ``` -In this case it's not very helpful: it is true that the majority of Americans in this survey are Protestant, but we'd probably like to see some more details! -Instead, we can use the `fct_lump_n()` to specify that we want exactly 10 groups: +Neste caso, não ajuda muito: é verdade que a maioria dos americanos nesta pesquisa são protestantes, mas provavelmente gostaríamos de ver mais alguns detalhes! +Em vez disso, podemos usar `fct_lump_n()` para especificar que queremos exatamente 10 grupos: ```{r} -gss_cat |> - mutate(relig = fct_lump_n(relig, n = 10)) |> - count(relig, sort = TRUE) +questionario |> + mutate(religiao = fct_lump_n(religiao, n = 10)) |> + count(religiao, sort = TRUE) ``` -Read the documentation to learn about `fct_lump_min()` and `fct_lump_prop()` which are useful in other cases. +Leia a documentação para aprender sobre `fct_lump_min()` e `fct_lump_prop()` que são úteis em outros casos. -### Exercises +### Exercícios -1. How have the proportions of people identifying as Democrat, Republican, and Independent changed over time? +1. Como mudaram ao longo do tempo as proporções de pessoas que se identificam como Democratas, Republicanas e Independentes?? -2. How could you collapse `rincome` into a small set of categories? +2. Como você poderia agrupar a “renda” em um pequeno conjunto de categorias?? -3. Notice there are 9 groups (excluding other) in the `fct_lump` example above. - Why not 10? - (Hint: type `?fct_lump`, and find the default for the argument `other_level` is "Other".) +3. Observe que existem 9 grupos (excluindo outros) no exemplo `fct_lump` acima. + Por que não 10? + (Dica: digite `?fct_lump` e encontre o padrão para o argumento `other_level` é "Other".) -## Ordered factors {#sec-ordered-factors} +## Fatores ordenados {#sec-ordered-factors} -Before we go on, there's a special type of factor that needs to be mentioned briefly: ordered factors. -Ordered factors, created with `ordered()`, imply a strict ordering and equal distance between levels: the first level is "less than" the second level by the same amount that the second level is "less than" the third level, and so on. -You can recognize them when printing because they use `<` between the factor levels: +Antes de prosseguirmos, há um tipo especial de fator que precisa ser mencionado brevemente: fatores ordenados. +Fatores ordenados, criados com `ordered()`, implicam uma ordenação estrita e distância igual entre os níveis: o primeiro nível é "menor que" o segundo nível na mesma quantidade que o segundo nível é "menor que" o terceiro nível e assim por diante. +Você pode reconhecê-los ao imprimir porque eles usam `<` entre os níveis dos fatores: ```{r} ordered(c("a", "b", "c")) ``` -In practice, `ordered()` factors behave very similarly to regular factors. -There are only two places where you might notice different behavior: +Na prática, os fatores `ordenados()` se comportam de forma muito semelhante aos fatores regulares. +Existem apenas dois lugares onde você pode notar um comportamento diferente: -- If you map an ordered factor to color or fill in ggplot2, it will default to `scale_color_viridis()`/`scale_fill_viridis()`, a color scale that implies a ranking. -- If you use an ordered function in a linear model, it will use "polygonal contrasts". These are mildly useful, but you are unlikely to have heard of them unless you have a PhD in Statistics, and even then you probably don't routinely interpret them. If you want to learn more, we recommend `vignette("contrasts", package = "faux")` by Lisa DeBruine. +- Se você mapear um fator ordenado para colorir ou preencher no ggplot2, o padrão será `scale_color_viridis()`/`scale_fill_viridis()`, uma escala de cores que implica um ranqueamento. +- Se você usar uma função ordenada em um modelo linear, ela usará "contrastes poligonais". Eles são moderadamente úteis, mas é improvável que você já tenha ouvido falar deles, a menos que tenha um PhD em Estatística e, mesmo assim, provavelmente não os interpreta rotineiramente. Se você quiser saber mais, recomendamos a `vignette("contrasts", package = "faux")` de Lisa DeBruine. -Given the arguable utility of these differences, we don't generally recommend using ordered factors. +Dada a utilidade discutível dessas diferenças, geralmente não recomendamos o uso de fatores ordenados. -## Summary +## Resumo -This chapter introduced you to the handy forcats package for working with factors, introducing you to the most commonly used functions. -forcats contains a wide range of other helpers that we didn't have space to discuss here, so whenever you're facing a factor analysis challenge that you haven't encountered before, I highly recommend skimming the [reference index](https://forcats.tidyverse.org/reference/index.html) to see if there's a canned function that can help solve your problem. +Este capítulo apresentou o prático pacote forcats para trabalhar com fatores, apresentando as funções mais comumente usadas. +O pacote forcats contém uma ampla gama de outras funções auxiliares que não tivemos espaço para discutir aqui, portanto, sempre que você estiver enfrentando um desafio de análise com fatores que não encontrou antes, recomendo fortemente dar uma olhada no [índice de referência](https://forcats.tidyverse.org/reference/index.html) para ver se existe uma função pronta que pode ajudar a resolver seu problema. -If you want to learn more about factors after reading this chapter, we recommend reading Amelia McNamara and Nicholas Horton's paper, [*Wrangling categorical data in R*](https://peerj.com/preprints/3163/). -This paper lays out some of the history discussed in [*stringsAsFactors: An unauthorized biography*](https://simplystatistics.org/posts/2015-07-24-stringsasfactors-an-unauthorized-biography/) and [*stringsAsFactors = \*](https://notstatschat.tumblr.com/post/124987394001/stringsasfactors-sigh), and compares the tidy approaches to categorical data outlined in this book with base R methods. -An early version of the paper helped motivate and scope the forcats package; thanks Amelia & Nick! +Se você quiser aprender mais sobre fatores depois de ler este capítulo, recomendamos a leitura do artigo de Amelia McNamara e Nicholas Horton, [*Wrangling categorical data in R*](https://peerj.com/preprints/3163/). +Este artigo apresenta um pouco da história discutida em [*stringsAsFactors: Uma biografia não autorizada*](https://simplystatistics.org/posts/2015-07-24-stringsasfactors-an-unauthorized-biography/) e [*stringsAsFactors = \*](https://notstatschat.tumblr.com/post/124987394001/stringsasfactors-sigh) e compara as abordagens organizadas (*tidy approach*) para dados categóricos descritos neste livro com métodos do R base. +Uma versão inicial do documento ajudou a motivar e definir o escopo do pacote forcats; obrigado Amélia e Nick! -In the next chapter we'll switch gears to start learning about dates and times in R. -Dates and times seem deceptively simple, but as you'll soon see, the more you learn about them, the more complex they seem to get! +No próximo capítulo, mudaremos de assunto para começar a aprender sobre datas e horários no R. +Datas e horários parecem enganosamente simples, mas como você verá em breve, quanto mais você aprende sobre eles, mais complexos eles parecem se tornar! From d45fd905de60513a7766b0b84bbbd183ef29e891 Mon Sep 17 00:00:00 2001 From: Eric Scopinho Date: Tue, 9 Jan 2024 17:17:23 -0300 Subject: [PATCH 5/8] remove regex.qmd --- regexps.qmd | 868 ---------------------------------------------------- 1 file changed, 868 deletions(-) delete mode 100644 regexps.qmd diff --git a/regexps.qmd b/regexps.qmd deleted file mode 100644 index ce7081258..000000000 --- a/regexps.qmd +++ /dev/null @@ -1,868 +0,0 @@ -# Expressões regulares {#sec-regular-expressions} - -```{r} -#| echo: false -#| results: asis - -source("_common.R") - -``` - -## Introdução - -No @sec-strings, você aprendeu diversas funções úteis para trabalhar com *strings*. -Este capítulo se concentrará em funções que usam **expressões regulares**, uma linguagem concisa e poderosa para descrever padrões (*patterns*) dentro de cadeia de caracteres (*strings*). -O termo "expressão regular" é um pouco longo, então a maioria das pessoas o abrevia como "regex"[^regexps-1] ou "regexp". - -[^regexps-1]: Você pode pronunciá-lo com um g-seco (reg-x) ou um g-suave (rej-x). - -O capítulo começa com os conceitos básicos de expressões regulares e as funções do pacote stringr mais úteis para análise de dados. -Em seguida, expandiremos seu conhecimento sobre padrões (*patterns*) e cobriremos sete novos tópicos importantes (escape, ancoragem, classes de caracteres, classes de abreviações, quantificadores, precedência de agrupamento). -A seguir, falaremos sobre alguns dos outros tipos de padrões com os quais as funções do pacote stringr podem trabalhar e os vários "sinalizadores" (*flags*) que permitem ajustar a operação das expressões regulares. -Terminaremos com uma pesquisa de outros lugares do tidyverse e no R base onde você pode usar expressões regulares. - -### Pré-requisitos - -Neste capítulo, usaremos funções com expressão regular dos pacotes stringr e tidyr, ambos membros principais do tidyverse, bem como o conjunto de dados bebes do pacote dados. - -```{r} -#| label: setup -#| message: false - -library(tidyverse) -library(dados) -``` - -Ao longo deste capítulo, usaremos uma combinação de exemplos muito simples em linha (*inline*) para você entender a ideia geral, o conjunto de dados bebes e três vetores de caracteres do pacote stringr que estão em inglês: - -- `fruit` contém nomes de 80 frutas. -- `words` contém 980 palavras comuns da lingua inglesa. -- `sentences` contém 720 pequenas sentenças. - -## Padrões básicos {#sec-reg-basics} - -Usaremos a função `str_view()` para aprender como os padrões (*patterns*) regex funcionam. -Usamos `str_view()` no capítulo anterior para entender melhor uma *string* vs. sua representação impressa, e agora, a usaremos com seu segundo argumento, uma expressão regular. -Quando este argumento é fornecido, a `str_view()` irá mostrar apenas os elementos correspondentes (*match*) do vetor de *string* , colocando `<>` ao redor de cada correspondência, e, quando possível, enfatizando a correpondência na cor azul. - -Os padrões mais simples consistem em letras e números que correspondem exatamente a esses caracteres: - -```{r} -str_view(fruit, "berry") -``` - -Letras e números tem uma correspondência direta à seus respectivos caracteres e são chamados **caracteres literais**. -A maioria dos caracteres de pontuação, como `.`, `+`, `*`, `[`, `]` e `?`, tem significados especiais[^regexps-2] e são chamados de **metacaracteres**. Por exemplo, `.` -corresponde a qualquer caractere[^regexps-3], portanto `"a."` irá corresponder a qualquer *string* que contenha a letra "a" seguida por qualquer outro caractere -: - -[^regexps-2]: Você aprenderá como escapar (*escape*) destes significados especiais na @sec-regexp-escaping. - -[^regexps-3]: Bem, qualquer caractere exceto `\n` (nova linha). - -```{r} -str_view(c("a", "ab", "ae", "bd", "ea", "eab"), "a.") -``` - -Ou, podemos encontrar todas as frutas que contém um "a", seguido por três letras, seguidas por um "e": - -```{r} -str_view(fruit, "a...e") -``` - -**Quantificadores** controlam quantas vezes um padrão pode ser encontrado: - -- `?` torna um padrão opcional (e.x. corresponde a 0 ou 1 vez) -- `+` permite que o padrão se repita (e.x. corresponde ao menos uma vez) -- `*` permite que o padrão seja opcional ou se repita (e.x. corresponde a qualquer número de vezes, incluindo 0). - -```{r} -# ab? corresponde a "a", opcionalmente seguida por um "b". -str_view(c("a", "ab", "abb"), "ab?") - -# ab+ corresponde a um "a", seguido por ao menos um "b". -str_view(c("a", "ab", "abb"), "ab+") - -# ab* corresponde um "a", seguido por qualquer número de "b"s. -str_view(c("a", "ab", "abb"), "ab*") -``` - -**Classes de caracteres** são definidas por `[]` e permitem que você defina um conjunto de caracteres, e.x., `[abcd]` corresponde a "a", "b", "c" ou "d". -Você também pode inverter a correspondência com um `^`: `[^abcd]` corresponde a qualquer coisa, **exceto** "a", "b", "c" ou "d". -Podemos usar esta ideia para encontrar palavras que contenham um "x" cercado por vogais, ou um "y" cercado por consoantes: - -```{r} -str_view(words, "[aeiou]x[aeiou]") -str_view(words, "[^aeiou]y[^aeiou]") -``` - -Você pode usar a **alternância**, `|`, para selecionar entre um ou mais padrões alternativos. -Por exemplo, os padrões a seguir procuram por frutas contendo "apple", "melon", ou "nut", ou uma vogal repetida. - -```{r} -str_view(fruit, "apple|melon|nut") -str_view(fruit, "aa|ee|ii|oo|uu") -``` - -Expressões regulares são bastante compactas e usam diversos caracteres de pontuação, portanto elas podem parecer difíceis de ler e entender no início. -Não se preocupe; você as entenderá melhor com a prática, e padrões simples se tornarão mais naturais com o tempo. -Vamos iniciar este processo praticando com algumas funções úteis do stringr. - -## Funções chaves {#sec-stringr-regex-funs} - -Agora que você tem o básico das expressões regulares em seu cinto de utilidades, vamos usá-las em algumas funções dos pacotes stringr e tidyr. -Na seção a seguir, você aprenderá como detectar a presença ou ausência de correspondências (*matches*), como contar o número de correspondências, como trocar uma correspondência por um texto fixo e como extrair textos usando um padrão (*pattern*). - -### Detectando correspondências - -`str_detect()` retorna uma vetor lógico que ém `TRUE` se o padrão corresponde a um elemento do vetor de caracteres e `FALSE` se não corresponde: - -```{r} -str_detect(c("a", "b", "c"), "[aeiou]") -``` - -Uma vez que `str_detect()` retorna um vetor lógico de mesmo tamanho que o vetor inicial, ele combina muito bem com a função `filter()`. -Por exemplo, este código encontra todos os nomes mais populares contendo um "x" minúsculo: - -```{r} -bebes |> - filter(str_detect(nome, "x")) |> - count(nome, wt = n, sort = TRUE) -``` - -Podemos também usar a`str_detect()` com `summarize()` combinando-a com `sum()` ou `mean()`: `sum(str_detect(x, padrao))` informa o numero de observações correspondentes ao padrão e `mean(str_detect(x, padrao))` informa a proporção da correspondência. -Por exemplo, o código a seguir, calcula e visualiza a proporção de nomes de bebês [^regexps-4] que contém "x", agrupados por ano. -Parece que eles aumentaram em popularidade ultimamente! - -[^regexps-4]: Isto nos dá a proporção de **nomes** que contém um "x"; se você quiser a proporção de bebês com um nome contendo um x, você deve usar uma média ponderada. - -```{r} -#| fig-alt: | -#| Uma série temporal mostrando a proporção de nomes de bebês que contém a letra x. -#| A proporção cai gradualmente de 8 por 1000 em 1880 para 4 por 1000 em -#| 1980, então aumenta rapidamente para 16 por 1000 em 2019. - -bebes |> - group_by(ano) |> - summarize(prop_x = mean(str_detect(nome, "x"))) |> - ggplot(aes(x = ano, y = prop_x)) + - geom_line() -``` - -Existem duas funções que se relacionam muito com a `str_detect()`: `str_subset()` e `str_which()`. -`str_subset()` retorna um vetor de caractere contendo apenas as *strings* que correspondem ao padrão. -`str_which()` retorna um vetor de inteiro informando a posição das *strings* que correspondem ao padrão. - -### Contando correspondências (*matches*) - -O próximo passo em complexidade de `str_detect()` é `str_count()`: em vez de verdadeiro (*true*) ou falso (*false*), ele informa quantas correspondências (*matches*) existem em cada *string*. - -```{r} -x <- c("apple", "banana", "pear") -str_count(x, "p") -``` - -Note que cada correspondência começa ao final da anterior, e.x. regex nunca se sobrepõem. -Por exemplo, em `"abababa"`, quantas vezes o padrão (*pattern*) `"aba"` é correspondido? -A expressão regular diz, duas e não três vezes: - -```{r} -str_count("abababa", "aba") -str_view("abababa", "aba") -``` - -É comum usar a `str_count()` com `mutate()`. -Os exemplos a seguir, usam `str_count()` com classes de caracteres para contar o número de vogais e consoantes em cada nome. - -```{r} -bebes |> - count(nome) |> - mutate( - vogais = str_count(nome, "[aeiou]"), - consoantes = str_count(nome, "[^aeiou]") - ) -``` - -Se você observar bem, você verá que há algo estranho em nossa contagem: "Aaban" contém três "a"s, mas nosso relatório sumarizado mostra apenas 2 vogais. -Isto é porque as expressões regulares consideram a diferença entre letras maiúsculas e minúsculas (*case sensitive*). -Existem três formas de arrumar isto: - -- Adicionar as vogais maiúsculas nas classes de caracteres: `str_count(nome, "[aeiouAEIOU]")`. -- Informar a expressão regular para ignnorar esta diferença: `str_count(nome, regex("[aeiou]", ignore_case = TRUE))`. Falaremos mais sobre isto na @sec-flags. -- Usar a `str_to_lower()` para converter os nomes para letras minúsculas: `str_count(str_to_lower(nome), "[aeiou]")`. - -Esta variedade de abordagens é muito comum quando trabalhamos com *strings* --- em geral, há várias formas de atingir seus objetivos, seja formando padrões mais complexos ou efetuando algum pré-processamento em sua *string*. -Se você estiver bloqueado em uma abordagem, em geral, pode ser melhor mudar a marcha e atacar seu problema sob uma perspectiva diferente. - -Nesta caso, como estamos usando duas funções na variável nome, acredito ser mais fácil transformá-la antes: - -```{r} -bebes |> - count(nome) |> - mutate( - nome = str_to_lower(nome), - vogais = str_count(nome, "[aeiou]"), - consoantes = str_count(nome, "[^aeiou]") - ) -``` - -### Trocando valores - -Assim como detectar e contar correspondências, você também pode modificá-las com `str_replace()` e `str_replace_all()`. -`str_replace()` troca a primeira correspondência e, como o nome pode sugerir, a `str_replace_all()` troca todas as correspondências. - -```{r} -x <- c("apple", "pear", "banana") -str_replace_all(x, "[aeiou]", "-") -``` - -`str_remove()` e `str_remove_all()` são funções atalhos para `str_replace(x, padrão, "")`: - -```{r} -x <- c("apple", "pear", "banana") -str_remove_all(x, "[aeiou]") -``` - -Estas funções combinam natualmente com `mutate()` quando fazemos limpeza de dados, e você geralmente as aplicará repetidamente para remover camadas de incosistência de formatação. - -### Extraindo variáveis {#sec-extract-variables} - -A última função que discutiremos usa expressões regulares para extrair dados de uma coluna para um ou mais nova(s) coluna(s): `separate_wider_regex()`. -É uma função par da `separate_wider_position()` e `separate_wider_delim()` que você aprendeu na @sec-string-columns. -Estas funções estão no pacote tidyr pois elas operam em colunas de *data frames* ou invés de vetores individuais. - -Vamos criar um conjunto simples de dados para mostrar como funciona. -Aqui temos alguns dados derivados de `dados::bebes` onde, temos nome, sexo biológico e idade de várias pessoas em um formato meio estranho[^regexps-5]: - -[^regexps-5]: Gostaríamos de poder garantir que você nunca veria algo tão estranho na vida real, mas infelizmente, ao longo de sua carreira, é provável que você veja coisas muito mais estranhas! - -```{r} -df <- tribble( - ~str, - "-F_34", - "-F_45", - "-M_33", - "-F_38", - "-F_58", - "-M_41", - "-F_84", -) -``` - -Para extrair estes dados com `separate_wider_regex()`, precisamos apenas construir uma sequência de expressões regulares que correspondem a cada parte. -Se quisermos que o conteído de cada parte apareção na saída, damos um nome: - -```{r} -df |> - separate_wider_regex( - str, - patterns = c( - "<", - nome = "[A-Za-z]+", - ">-", - sexo_biologico = ".", - "_", - idade = "[0-9]+" - ) - ) -``` - -Se a correpondência falhar, você pode usar `too_short = "debug"` para entender o que deu errado, assim como na `separate_wider_delim()` e `separate_wider_position()`. - -### Exercícios - -1. Qual nome de bebê tem a menor quantidade de vogais? - QUal nome tem a maior proporção de vogais? - (Dica: qual o denominador?) - -2. Troque todas as barras em `"a/b/c/d/e"` por barras invertidas. - O que acontece se você tentar desfazer esta mudança trocando as barras invertidas por barras? - (Discutiremos este problema em breve.) - -3. Implemente uma versão simples da `str_to_lower()` usando `str_replace_all()`. - -4. Crie uma expressão regular para correpondências de números telefônicos da forma que são comumente escritas em seu país. - -## Detalhes de padrões - -Agora que você entende o básico da linguagem dos padrões e como usá-la com algumas funções do pacote stringr e tidyr, é hora de nos aprofundarmos mais nos detalhes. -Primeiro, começaremos com **escapadas** (*escaping*), que permite combinar metacaracteres que de outra forma seriam tratados de maneira especial. -A seguir, você aprenderá sobre **âncoras** (*anchors*) que permitem combinar com o início ou o fim da *string*. -Em seguida, você aprenderá mais sobre **classes de caracteres** (*character classes*) e seus atalhos que permitem combinar qualquer caractere de um conjunto. -A seguir, você aprenderá os detalhes finais dos **quantificadores** (*quantifiers*) que controlam quantas vezes um padrão pode corresponder. -Então, temos que cobrir o importante (mas complexo) tópico de **precedência de operador** (*operator precedence*) e parênteses. -E terminaremos com alguns detalhes de **agrupamento** (*grouping*) de componentes de um padrão. - -Os termos que usamos aqui são os nomes técnicos de cada componente. -Eles nem sempre são os que combinam melhor ao seu propósito, mas é muito útil saber os termos corretos se você quiser procurar mais detalhes no *Google* posteriormente. - -### Escapadas {#sec-regexp-escaping} - -Para correponder um `.` literal, você precisa **escapar**, o que informa a expressão regular para correponder ao metacaractere [^regexps-6] literalmente. -Assim como as *strings*, regexp usa a barra invertidade para escapadas (*escaping*). -Portanto, para corresponder a um `.`, você preacisa da regexp `\.`. Infelizmente isto cria um problema. -Nós usamos strings para representar expressões regulares, e `\` é também usada como símbolo de escapadas nas *strings*. -Portanto, para criar a expressão regular `\.` , precismos da *string* `"\\."`, como mostra os exemplos a seguir. - -[^regexps-6]: A lista completa de metacaractere é `.^$\|*+?{}[]()` - -```{r} -# Para criar a expressão regular \., precisamos usar \\. -ponto <- "\\." - -# Mas a expressão regular em si, contém apenas uma \ -str_view(ponto) - -# E isto diz ao R para procurar por um . explicitamente. -str_view(c("abc", "a.c", "bef"), "a\\.c") -``` - -Neste livro, normalmente escreveremos expressões regulares sem aspas, como `\.`. -Se precisarmos enfatizar o que você realmente digitará, colocaremos aspas e adicionaremos escapadas extras, como `"\\."`. -Para criar essa expressão regular, você precisa usar uma string, que também precisa escapar de `\`. -Isso significa que para corresponder a um `\` literal você precisa escrever `"\\\\"` --- você precisa de quatro barras invertidas para corresponder a uma! - -```{r} -x <- "a\\b" -str_view(x) -str_view(x, "\\\\") -``` - -Alternativamente, você pode achar mais fácil usar *strings* brutas (*raw*) que você emprendeu na @sec-raw-strings. -That lets you avoid one layer of escaping: - -```{r} -str_view(x, r"{\\}") -``` - -Se você está tentando correponder um `.`, `$`, `|`, `*`, `+`, `?`, `{`, `}`, `(`, `)` literal, há uma alternativa para não usar a escapada com barra invertida: você pode usar uma classe de caractere: `[.]`, `[$]`, `[|]`, \... -todas correspondem aos valores literais. - -```{r} -str_view(c("abc", "a.c", "a*c", "a c"), "a[.]c") -str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c") -``` - -### Âncoras - -Por padrão, as expressões regulares corresponderão a qualquer parte de uma string. -Se você quiser corresponder no início ou no final, você precisa **ancorar** a expressão regular usando `^` para corresponder ao início ou `$` para corresponder ao final: - -```{r} -str_view(fruit, "^a") -str_view(fruit, "a$") -``` - -É tentador pensar que `$` deve corresponder ao início de uma string, porque é assim que escrevemos valores monetários, mas não é isso que as expressões regulares desejam. - -Para forçar uma expressão regular a corresponder apenas à string completa, ancore-a com `^` e `$`: - -```{r} -str_view(fruit, "apple") -str_view(fruit, "^apple$") -``` - -Você também pode combinar o limite entre as palavras (ou seja, o início ou o fim de uma palavra) com `\b`. -Isso pode ser particularmente útil ao usar a ferramenta localizar e substituir do RStudio. -Por exemplo, se quiser encontrar todos os usos de `sum()`, você pode procurar por `\bsum\b` para evitar a correspondência de `summarize`, `summary`, `rowsum` e assim por diante: - -```{r} -x <- c("summary(x)", "summarize(df)", "rowsum(x)", "sum(x)") -str_view(x, "sum") -str_view(x, "\\bsum\\b") -``` - -Quando usadas sozinhas, as âncoras produzirão uma correspondência de largura-zero (*zero-width*): - -```{r} -str_view("abc", c("$", "^", "\\b")) -``` - -Isso ajuda você a entender o que acontece quando você substitui uma âncora independente: - -```{r} -str_replace_all("abc", c("$", "^", "\\b"), "--") -``` - -### Classes de caracteres - -Uma **classe de caracteres** (*character class*), ou um **conjunto** de caracteres, permite que você faça a correspondência de qualquer caractere deste conjunto. -Como discutido acima, você pode construir seus próprios conjuntos com `[]`, onde `[abc]` correponde a "a", "b" ou "c" e `[^abc]` corresponde a qualquer caractere exceto "a", "b" ou "c". -Além do `^` existem outros dois caracteres com significados especiais quando estão dentro de `[]:` - -- `-` define um intervalo (*range*), e.x., `[a-z]` corresponde a qualquer letra minúscula e `[0-9]` a qualquer número. -- `\` caracteres especiais de escapadas, então `[\^\-\]]` corresponde `^`, `-` ou `]`. - -Aqui estão alguns exemplos: - -```{r} -x <- "abcd ABCD 12345 -!@#%." -str_view(x, "[abc]+") -str_view(x, "[a-z]+") -str_view(x, "[^a-z0-9]+") - -# Você precisa de uma escapada para caracteres especiais -# dentro de [] -str_view("a-b-c", "[a-c]") -str_view("a-b-c", "[a\\-c]") -``` - -Algumas classes de caracteres são tão comuns que possuem atalhos próprios. -Você já viu `.`, que corresponde a qualquer caractere exceto o de nova linha. -Existem três outras classes que são particularmente úteis[^regexps-7]: - -[^regexps-7]: Lembre-se, para criar uma expressão regular contendo `\d` ou `\s`, você precisará escapar a `\` da *string*, então você digitará `"\\d"` ou `"\\s"`. - -- `\d` corresponde a qualquer digito;\ - `\D` corresponde a qualquer coisa que não seja um digito. -- `\s` corresponde a qualquer espaço em branco (e.x., espaço, tab, nova linha);\ - `\S` corresponde a qualquer coisa que não seja um espaço em branco. -- `\w` corresponde a qualquer caractere de "palavra" (*word*) , e.x. letras e números;\ - `\W` corresponde a qualquer caractere de "não-palavra" (*non-word*). - -O código a seguir demonstra os seis atalhos com a seleção de letras, números e caracteres de pontuação. - -```{r} -x <- "abcd ABCD 12345 -!@#%." -str_view(x, "\\d+") -str_view(x, "\\D+") -str_view(x, "\\s+") -str_view(x, "\\S+") -str_view(x, "\\w+") -str_view(x, "\\W+") -``` - -### Quantificadores {#sec-quantifiers} - -**Quantificadores** (*Quantifies*) controlam quantas vezes um padrão é correspondido. -Na @sec-reg-basics você aprendeu sobre o `?` (0 ou 1 correspondência), `+` (1 ou mais correspondência) e `*` (0 ou mais correspondências). -Por exemplo, `colou?r` irá corresponder à escrita em inglês estadunidense (*color*) e britânico (*colour*), `\d+` irá corresponder a um ou mais digitos, e `\s?` irá opcionalmente corresponder a um único espaço em branco. -Você também pode especificar o número exato de correspondências com `{}`: - -- `{n}` corresponde exatamente n vezes. -- `{n,}` corresponde ao menos n vezes. -- `{n,m}` corresponde entre n e m vezes. - -### Precedência de operadores e parênteses - -Ao que `ab+` corresponde? -Corresponde a um "a" seguido por um ou mais "b"s, ou corresponde a "ab" repetidos qualquer número de vezes? -Ao que corresponde `^a|b$`? -Corresponde a uma *string* a completa OU um *string* b, ou a uma *string* que começa com a OU uma *string* que termina com b? - -As respostas para estas perguntas são determinadas pela precedência de operadores, similar a regras PEMDAS ou BEDMAS que você deve ter aprendido na escola. -Você sabe que `a + b * c` é equivalente a `a + (b * c)` e não `(a + b) * c` pois `*` tem uma prioridade mais alta de precedência e `+` tem uma precedência mais baixa: você calcula o `*` antes do `+`. - -Da mesma forma, expressões regulares têm suas próprias regras de precedência: quantificadores têm precedência alta e alternância têm precedência baixa, o que significa que `ab+` é equivalente a `a(b+)`, e `^a|b$` é equivalente a `(^a )|(b$)`. -Assim como na álgebra, você pode usar parênteses para substituir a ordem normal. -Mas, diferentemente da álgebra, é improvável que você se lembre das regras de precedência para expressões regulares, então sinta-se à vontade para usar parênteses livremente.. - -### Agrupando e capturando - -Além de substituir a precedência do operador, os parênteses têm outro efeito importante: eles criam **grupos de captura** que permitem usar subcomponentes da correspondência. - -A primeira maneira de usar um grupo de captura é fazer referência a ele dentro de uma correspondência com **referência anterior**: `\1` refere-se à correspondência contida no primeiro parêntese, `\2` no segundo parêntese, e assim sobre. -Por exemplo, o padrão a seguir encontra todas as frutas que possuem um par de letras repetido: - -```{r} -str_view(fruit, "(..)\\1") -``` - -E este encontra todas as palavras que começam e terminam com o mesmo par de letras: - -```{r} -str_view(words, "^(..).*\\1$") -``` - -Você também pode usar referências anteriores (*back references*) em `str_replace()`. -Por exemplo, este código muda a ordem da segunda e terceira palavras em `sentences`: - -```{r} -sentences |> - str_replace("(\\w+) (\\w+) (\\w+)", "\\1 \\3 \\2") |> - str_view() -``` - -Se você deseja extrair as correspondências de cada grupo você pode usar `str_match()`. -Mas `str_match()` retorna uma matriz, então não é particularmente fácil trabalhar com ela[^regexps-8]: - -[^regexps-8]: Principalmente porque nunca discutimos sobre matrizes neste livro! - -```{r} -sentences |> - str_match("the (\\w+) (\\w+)") |> - head() -``` - -You could convert to a tibble and name the columns: - -```{r} -sentences |> - str_match("the (\\w+) (\\w+)") |> - as_tibble(.name_repair = "minimal") |> - set_names("correspondencia", "palavra1", "palavra2") -``` - -Mas então você basicamente recriou sua própria versão de `separate_wider_regex()`. -Na verdade, nos bastidores, `separate_wider_regex()` converte seu vetor de padrões em um único regex que usa agrupamento para capturar os componentes nomeados. - -Ocasionalmente, você desejará usar parênteses sem criar grupos correspondentes. -Você pode criar um grupo sem captura com `(?:)`. - -```{r} -x <- c("uma gata faminta", "um cão faminto") -str_match(x, "famint(o|a)") -str_match(x, "famint(?:o|a)") -``` - -### Exercícios - -1. Como você faria para corresponder a *string* literal `"'\`? E a `"$^$"`? - -2. Explique porque cada um desses padrões não correspondem a `\`: `"\"`, `"\\"`, `"\\\"`. - -3. Dado o conjunto de palavras comuns em `stringr::words`, crie expressões regulares para encontrar todas as palavras que: - - a. Comecem com "y". - b. Não comecem com um "y". - c. Terminem com "x". - d. Possuem exatamente três letras. (Não trapaceie usando a `str_length()`!) - e. Possuem sete letras ou mais. - f. Contém um par consoante-vogal. - g. Contém ao menos um par consoante-vogal em sequência. - h. É formada apenas por pares repetidos de consoantes-vogais. - -4. Crie 11 expressões regulares que correspondem a ortografias estadunidenses e britânicas para cada uma das seguintes palavras: airplane/aeroplane, aluminum/aluminium, analog/analogue, ass/arse, center/centre, defense/defence, donut/doughnut, gray/grey, modeling/modelling, skeptic/sceptic, summarize/summarise. - Tente e crie a menor expressão regular possível! - -5. Troque a primeira e última letra em `words`. - Quais desses *strings* ainda são `palavras`? - -6. Descreva em palavras ao que correspondem as expressões abaixo: (leia com atenção para ver se cada parte corresponde a uma expressão regular ou a uma *string* que define uma expressão regular.) - - a. `^.*$` - b. `"\\{.+\\}"` - c. `\d{4}-\d{2}-\d{2}` - d. `"\\\\{4}"` - e. `\..\..\..` - f. `(.)\1\1` - g. `"(..)\\1"` - -7. Resolva a palavra-cruzada de regex para iniciantes em . - -## Controle de padrões - -É possível exercer controle extra sobre os detalhes da correspondência usando um objeto padrão em vez de apenas uma string. -Isto permite que você controle os chamados sinalizadores (*flgas*) regex e combine vários tipos de strings fixas, conforme descrito abaixo. - -### Sinalizadores regex {#sec-flags} - -Existem várias configurações que podem ser usadas para controlar os detalhes do regexp. -Essas configurações costumam ser chamadas de **sinalizadores** (*flgas*) em outras linguagens de programação. -No pacote stringr, você pode usá-los agrupando o padrão em uma chamada para `regex()`. -O sinalizador mais útil é provavelmente `ignore_case = TRUE` porque permite que os caracteres correspondam às suas formas maiúsculas ou minúsculas: - -```{r} -bananas <- c("banana", "Banana", "BANANA") -str_view(bananas, "banana") -str_view(bananas, regex("banana", ignore_case = TRUE)) -``` - -Se você trabalha muito com *strings* multilinhas (ou seja, *strings* que contêm `\n`), `dotall` e `multiline` também podem ser úteis: - -- `dotall = TRUE` faz com que o `.` corresponda a qualquer coisa, incluindo o `\n`: - - ```{r} - x <- "Linha 1\nLinha 2\nLinha 3" - str_view(x, ".Linha") - str_view(x, regex(".Linha", dotall = TRUE)) - ``` - -- `multiline = TRUE` faz o `^` e o `$` corresponder ao início e fim de cada linha ao invés de do início e fim de cada *string* completa: - - ```{r} - x <- "Linha 1\nLinha 2\nLinha 3" - str_view(x, "^Linha") - str_view(x, regex("^Linha", multiline = TRUE)) - ``` - -Finalmente, se você estiver escrevendo uma expressão regular complicada e estiver preocupado em não entendê-la no futuro, você pode tentar `comments = TRUE`. -Ele ajusta a linguagem padrão para ignorar espaços e novas linhas, bem como tudo depois de `#`. -Isso permite que você use comentários e espaços em branco para tornar expressões regulares complexas mais compreensíveis[^regexps-9], como no exemplo a seguir: - -[^regexps-9]: `comments = TRUE` é particularmente eficaz em combinação com uma string bruta (*raw*), como usamos aqui. - -```{r} -telefone <- regex( - r"( - \(? # opcional abertura de parantese - (\d{3}) # codigo de area - [)\-]? # opcional fechamento de parantese ou traço - \ ? # opcional espaço - (\d{3}) # outros três números - [\ -]? # opcional espaço ou traço - (\d{4}) # quatro outros números - )", - comments = TRUE -) - -str_extract(c("514-791-8141", "(123) 456 7890", "123456"), telefone) -``` - -Se você estiver usando comentários e quiser corresponder a um espaço, nova linha ou `#`, você precisará escapar usando a `\`. - -### Correspondências fixas - -Você pode optar por cancelar as regras de expressão regular usando `fixed()`: - -```{r} -str_view(c("", "a", "."), fixed(".")) -``` - -`fixed()` também te dá a habilidate de ignorar formas maiúsculas e minúsculas: - -```{r} -str_view("x X", "X") -str_view("x X", fixed("X", ignore_case = TRUE)) -``` - -Se você estiver trabalhando com textos que não estão em Inglês, você provavelmente irá querer usar a `coll()` ao invés da `fixed()`, uma vez que ela implementa todas as regras de capitalização (*capitalization*) usadas pela `localização` (*locale*) que você especificou. -Veja a @sec-other-languages para maiores detalhes sobre localização. - -```{r} -str_view("i İ ı I", fixed("İ", ignore_case = TRUE)) -str_view("i İ ı I", coll("İ", ignore_case = TRUE, locale = "tr")) -``` - -## Prática - -Para colocar essas ideias em prática, resolveremos a seguir alguns problemas semi-autênticos. -Discutiremos três técnicas gerais: - -1. verificar seu trabalho criando controles simples positivos e negativos -2. combinando expressões regulares com álgebra booleana -3. criação de padrões complexos usando manipulação de *strings* - -### Verifique seu trabalho - -Primeiro, vamos encontrar todas as frases que começam com “The”. -Usar a âncora `^` por si só não é suficiente: - -```{r} -str_view(sentences, "^The") -``` - -Porque esse padrão também corresponde a frases que começam com palavras como `They` ou `These`. -Precisamos ter certeza de que o “e” é a última letra da palavra, o que podemos fazer adicionando um limite (*boundary*) de palavra: - -```{r} -str_view(sentences, "^The\\b") -``` - -Que tal encontrar todas as frases que começam com um pronome? - -```{r} -str_view(sentences, "^She|He|It|They\\b") -``` - -Uma rápida inspeção dos resultados mostra que estamos obtendo algumas correspondências falsas. -Isso porque esquecemos de usar parênteses: - -```{r} -str_view(sentences, "^(She|He|It|They)\\b") -``` - -Você deve estar se perguntando como identificar tal erro se ele não ocorreu nas primeiras correspondências. -Uma boa técnica é criar algumas correspondências positivas e negativas e usá-las para testar se o seu padrão funciona conforme o esperado: - -```{r} -pos <- c("Ele é jovem", "Elas se divertiram") -neg <- c("Eleitores não compareceram na data correta", "Hadley nos disse que é um bom dia!") - -padrao <- "^(Eu|Tu|Ele|Ela|Nós|Vós|Eles|Elas)\\b" -str_detect(pos, padrao) -str_detect(neg, padrao) -``` - -Normalmente é muito mais fácil encontrar bons exemplos positivos do que negativos, porque leva um tempo até que você seja bom o suficiente com expressões regulares para prever onde estão seus pontos fracos. -No entanto, eles ainda são úteis: à medida que você trabalha no problema, você pode acumular lentamente uma coleção de seus erros, garantindo que nunca cometerá o mesmo erro duas vezes. - -### Operações booleanas {#sec-boolean-operations} - -Imagine que queremos encontrar palavras que contenham apenas consoantes. -Uma técnica é criar uma classe de caracteres que contenha todas as letras, exceto as vogais (`[^aeiou]`), então permitir que corresponda a qualquer número de letras (`[^aeiou]+`) e, em seguida, forçá-la a corresponder a *string* inteira ancorando no início e no final (`^[^aeiou]+$`): - -```{r} -str_view(words, "^[^aeiou]+$") -``` - -Mas você pode tornar esse problema um pouco mais fácil invertendo-o. -Em vez de procurar palavras que contenham apenas consoantes, poderíamos procurar palavras que não contenham vogais: - -```{r} -str_view(words[!str_detect(words, "[aeiou]")]) -``` - -Esta é uma técnica útil sempre que você estiver lidando com combinações lógicas, especialmente aquelas que envolvem “e” ou “não”. -Por exemplo, imagine se você deseja encontrar todas as palavras que contenham “a” e “b”. -Não há nenhum operador "e" embutido nas expressões regulares, então temos que resolver isso procurando todas as palavras que contenham um "a" seguido por um "b" ou um "b" seguido por um "a": - -```{r} -str_view(words, "a.*b|b.*a") -``` - -É mais simples combinar os resultados de duas chamadas para `str_detect()`: - -```{r} -words[str_detect(words, "a") & str_detect(words, "b")] -``` - -E se quiséssemos ver se existe uma palavra que contém todas as vogais? -Se fizéssemos isso com padrões precisaríamos gerar 5! -(120) padrões diferentes: - -```{r} -#| resultado: falso -words[str_detect(words, "a.*e.*i.*o.*u")] -# ... -words[str_detect(words, "u.*o.*i.*e.*a")] -``` - -É muito mais simples combinar cinco chamadas para `str_detect()`: - -```{r} -words[ - str_detect(words, "a") & - str_detect(words, "e") & - str_detect(words, "i") & - str_detect(words, "o") & - str_detect(words, "u") -] -``` - -Em geral, se você ficar preso tentando criar um único regexp que resolva seu problema, dê um passo para trás e pense se você poderia dividir o problema em partes menores, resolvendo cada desafio antes de passar para o próximo.. - -### Criando um padrão com código - -E se quiséssemos encontrar todas as `sentences` que mencionam uma cor? -A ideia básica é simples: apenas combinamos alternância com limites de palavras (*boundary*). - -```{r} -str_view(sentences, "\\b(red|green|blue)\\b") -``` - -Mas à medida que o número de cores aumenta, rapidamente se tornaria entediante construir esse padrão manualmente. -Não seria legal se pudéssemos armazenar as cores em um vetor? - -```{r} -rgb <- c("red", "green", "blue") -``` - -Bem, nós podemos! -Precisaríamos apenas criar o padrão a partir do vetor usando `str_c()` e `str_flatten()`: - -```{r} -str_c("\\b(", str_flatten(rgb, "|"), ")\\b") -``` - -Poderíamos tornar esse padrão mais abrangente se tivéssemos uma boa lista de cores. -Um lugar onde poderíamos começar é a lista de cores integradas que R usa para gerar gráficos: - -```{r} -str_view(colors()) -``` - -Mas vamos primeiro eliminar as variantes numeradas: - -```{r} -cores <- colors() -cores <- cores[!str_detect(cores, "\\d")] -str_view(cores) -``` - -Então podemos transformar isso em um padrão gigante. -Não mostraremos o padrão aqui porque é enorme, mas você pode ver como ele funciona: - -```{r} -padrao <- str_c("\\b(", str_flatten(cores, "|"), ")\\b") -str_view(sentences, padrao) -``` - -Neste exemplo, `cores` contém apenas números e letras, então você não precisa se preocupar com metacaracteres. -Mas, em geral, sempre que você criar padrões a partir de strings existentes, é aconselhável executá-los em `str_escape()` para garantir que eles correspondam literalmente. - -### Exercícios - -1. Para cada um dos desafios a seguir, tente resolvê-los usando uma única expressão regular e uma combinação de múltiplas chamadas `str_detect()`. - - a. Encontre todas as palavras em `words` que começam ou terminam com `x`. - b. Encontre todas as palavras em `words` que começam com vogal e terminam com consoante. - c. Existe alguma palavra em `words` que contenha pelo menos uma de cada vogal diferente? - -2. Construa padrões para encontrar evidências a favor e contra a regra "i antes de e exceto depois de c"? - -3. `colors()` contém vários modificadores de cores como "lightgray" (cinza claro) e "darkblue" (azul escuro). - Como você poderia identificar automaticamente esses modificadores (light, dark) ? - (Pense em como você pode detectar e remover as cores modificadas). - -4. Crie uma expressão regular que encontre qualquer conjunto de dados do R. - Você pode obter uma lista desses conjuntos de dados através do uso especial da função `data()`: `data(package = "datasets")$results[, "Item"]`. - Observe que vários conjuntos de dados antigos são vetores individuais; eles contêm o nome do "*data frame*" agrupado entre parênteses, então você precisará retirá-los. - -## Expressões regulares em outros lugares - -Assim como nas funções stringr e tidyr, existem muitos outros lugares no R onde você pode usar expressões regulares. -As seções a seguir descrevem algumas outras funções úteis no tidyverse e no R base. - -### tidyverse - -Existem três outros lugares particularmente úteis onde você pode querer usar expressões regulares - -- `matches(padrao)` selecionará todas as variáveis ​​cujo nome corresponda ao padrão fornecido. - É uma função "tidyselect" que você pode usar em qualquer lugar em qualquer função tidyverse que selecione variáveis ​​(por exemplo, `select()`, `rename_with()` e `across()`). - -- O argumento `names_pattern` da função `pivot_longer()` recebe um vetor de expressão regular, assim como na `separate_wider_regex()`. - É útil ao extrair dados de nomes de variáveis ​​com uma estrutura complexa - -- O argumento `delim` na `separate_longer_delim()` e na `separate_wider_delim()` geralmente corresponde a uma string fixa, mas você pode usar `regex()` para fazê-lo corresponder a um padrão. - Isso é útil, por exemplo, se você deseja corresponder uma vírgula que é opcionalmente seguida por um espaço, ou seja, `regex(", ?")`. - -### R base - -`apropos(padrao)` pesquisa todos os objetos disponíveis no ambiente global que correspondem ao padrão fornecido. -Isto é útil se você não consegue lembrar o nome de uma função: - -```{r} -apropos("replace") -``` - -`list.files(caminho, padrao)` lista todos os arquivos em `caminho` que correspondem a uma expressão regular `padrao`. -Por exemplo, você pode encontrar todos os arquivos R Markdown no diretório atual com: - -```{r} -head(list.files(pattern = "\\.Rmd$")) -``` - -É importante notar que a linguagem de padrão usada pelo R base é ligeiramente diferente daquela usada pelo stringr.. -Isso ocorre porque stringr é construído sobre o [pacote stringi](https://stringi.gagolewski.com), que por sua vez é construído sobre o [mecanismo ICU](https://unicode-org.github.io /icu/userguide/strings/regexp.html), enquanto as funções base R usam o [mecanismo TRE](https://github.com/laurikari/tre) ou o [mecanismo PCRE](https://www.pcre .org), dependendo se você configurou ou não `perl = TRUE`. -Felizmente, os princípios básicos das expressões regulares estão tão bem estabelecidos que você encontrará poucas variações ao trabalhar com os padrões que aprenderá neste livro. -Você só precisa estar ciente da diferença quando começar a confiar em recursos avançados, como intervalos complexos de caracteres Unicode ou recursos especiais que usam a sintaxe `(?…)`. - -## Resumo - -Com cada caractere de pontuação potencialmente sobrecarregado de significado, as expressões regulares são uma das linguagens mais compactas que existem. -Eles são definitivamente confusos no início, mas à medida que você treina seus olhos para lê-los e seu cérebro para entendê-los, você desbloqueia uma habilidade poderosa que pode ser usada em R e em muitos outros lugares. - -Neste capítulo, você iniciou sua jornada para se tornar um mestre em expressões regulares aprendendo as funções stringr mais úteis e os componentes mais importantes da linguagem de expressões regulares. -E ainda há muitos recursos para aprender mais. - -Um bom lugar para começar é na `vignette("regular-expressions", package = "stringr")`: ela documenta o conjunto completo de sintaxe suportada pelo pacote stringr. -Outra referência útil é [https://www.regular-expressions.info/](https://www.regular-expressions.info/tutorial.html). -Não é específico do R, mas você pode usá-lo para aprender sobre os recursos mais avançados das expressões regulares e como elas funcionam. - -Também é bom saber que stringr é implementado tendo o pacote stringi do Marek Gagolewski como base. -Se você está lutando para encontrar uma função que faça o que você precisa em stringr, não tenha medo de procurar no pacote stringi. -Você achará que stringi é muito fácil de aprender porque segue muitas das mesmas convenções que o stringr. - -No próximo capítulo, falaremos sobre uma estrutura de dados intimamente relacionada às *strings*: os fatores. -Fatores são usados ​​para representar dados categóricos em R, ou seja, dados com um conjunto fixo e conhecido de valores possíveis identificados por um vetor de *strings*. From 0dd1cb29ead96fcbd3fa7ecc4b5095e84cd5f057 Mon Sep 17 00:00:00 2001 From: Eric Scopinho Date: Tue, 9 Jan 2024 17:22:13 -0300 Subject: [PATCH 6/8] add original content for regex --- regexps.qmd | 871 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 871 insertions(+) create mode 100644 regexps.qmd diff --git a/regexps.qmd b/regexps.qmd new file mode 100644 index 000000000..3d33048a3 --- /dev/null +++ b/regexps.qmd @@ -0,0 +1,871 @@ +# Regular expressions {#sec-regular-expressions} + +```{r} +#| echo: false +#| results: asis + +source("_common.R") +mensagem_capitulo_sem_traducao() +``` + +## Introduction + +In @sec-strings, you learned a whole bunch of useful functions for working with strings. +This chapter will focus on functions that use **regular expressions**, a concise and powerful language for describing patterns within strings. +The term "regular expression" is a bit of a mouthful, so most people abbreviate it to "regex"[^regexps-1] or "regexp". + +[^regexps-1]: You can pronounce it with either a hard-g (reg-x) or a soft-g (rej-x). + +The chapter starts with the basics of regular expressions and the most useful stringr functions for data analysis. +We'll then expand your knowledge of patterns and cover seven important new topics (escaping, anchoring, character classes, shorthand classes, quantifiers, precedence, and grouping). +Next, we'll talk about some of the other types of patterns that stringr functions can work with and the various "flags" that allow you to tweak the operation of regular expressions. +We'll finish with a survey of other places in the tidyverse and base R where you might use regexes. + +### Prerequisites + +In this chapter, we'll use regular expression functions from stringr and tidyr, both core members of the tidyverse, as well as data from the babynames package. + +```{r} +#| label: setup +#| message: false + +library(tidyverse) +library(babynames) +``` + +Through this chapter, we'll use a mix of very simple inline examples so you can get the basic idea, the baby names data, and three character vectors from stringr: + +- `fruit` contains the names of 80 fruits. +- `words` contains 980 common English words. +- `sentences` contains 720 short sentences. + +## Pattern basics {#sec-reg-basics} + +We'll use `str_view()` to learn how regex patterns work. +We used `str_view()` in the last chapter to better understand a string vs. its printed representation, and now we'll use it with its second argument, a regular expression. +When this is supplied, `str_view()` will show only the elements of the string vector that match, surrounding each match with `<>`, and, where possible, highlighting the match in blue. + +The simplest patterns consist of letters and numbers which match those characters exactly: + +```{r} +str_view(fruit, "berry") +``` + +Letters and numbers match exactly and are called **literal characters**. +Most punctuation characters, like `.`, `+`, `*`, `[`, `]`, and `?`, have special meanings[^regexps-2] and are called **metacharacters**. For example, `.` +will match any character[^regexps-3], so `"a."` will match any string that contains an "a" followed by another character +: + +[^regexps-2]: You'll learn how to escape these special meanings in @sec-regexp-escaping. + +[^regexps-3]: Well, any character apart from `\n`. + +```{r} +str_view(c("a", "ab", "ae", "bd", "ea", "eab"), "a.") +``` + +Or we could find all the fruits that contain an "a", followed by three letters, followed by an "e": + +```{r} +str_view(fruit, "a...e") +``` + +**Quantifiers** control how many times a pattern can match: + +- `?` makes a pattern optional (i.e. it matches 0 or 1 times) +- `+` lets a pattern repeat (i.e. it matches at least once) +- `*` lets a pattern be optional or repeat (i.e. it matches any number of times, including 0). + +```{r} +# ab? matches an "a", optionally followed by a "b". +str_view(c("a", "ab", "abb"), "ab?") + +# ab+ matches an "a", followed by at least one "b". +str_view(c("a", "ab", "abb"), "ab+") + +# ab* matches an "a", followed by any number of "b"s. +str_view(c("a", "ab", "abb"), "ab*") +``` + +**Character classes** are defined by `[]` and let you match a set of characters, e.g., `[abcd]` matches "a", "b", "c", or "d". +You can also invert the match by starting with `^`: `[^abcd]` matches anything **except** "a", "b", "c", or "d". +We can use this idea to find the words containing an "x" surrounded by vowels, or a "y" surrounded by consonants: + +```{r} +str_view(words, "[aeiou]x[aeiou]") +str_view(words, "[^aeiou]y[^aeiou]") +``` + +You can use **alternation**, `|`, to pick between one or more alternative patterns. +For example, the following patterns look for fruits containing "apple", "melon", or "nut", or a repeated vowel. + +```{r} +str_view(fruit, "apple|melon|nut") +str_view(fruit, "aa|ee|ii|oo|uu") +``` + +Regular expressions are very compact and use a lot of punctuation characters, so they can seem overwhelming and hard to read at first. +Don't worry; you'll get better with practice, and simple patterns will soon become second nature. +Let's kick off that process by practicing with some useful stringr functions. + +## Key functions {#sec-stringr-regex-funs} + +Now that you've got the basics of regular expressions under your belt, let's use them with some stringr and tidyr functions. +In the following section, you'll learn how to detect the presence or absence of a match, how to count the number of matches, how to replace a match with fixed text, and how to extract text using a pattern. + +### Detect matches + +`str_detect()` returns a logical vector that is `TRUE` if the pattern matches an element of the character vector and `FALSE` otherwise: + +```{r} +str_detect(c("a", "b", "c"), "[aeiou]") +``` + +Since `str_detect()` returns a logical vector of the same length as the initial vector, it pairs well with `filter()`. +For example, this code finds all the most popular names containing a lower-case "x": + +```{r} +babynames |> + filter(str_detect(name, "x")) |> + count(name, wt = n, sort = TRUE) +``` + +We can also use `str_detect()` with `summarize()` by pairing it with `sum()` or `mean()`: `sum(str_detect(x, pattern))` tells you the number of observations that match and `mean(str_detect(x, pattern))` tells you the proportion that match. +For example, the following snippet computes and visualizes the proportion of baby names[^regexps-4] that contain "x", broken down by year. +It looks like they've radically increased in popularity lately! + +[^regexps-4]: This gives us the proportion of **names** that contain an "x"; if you wanted the proportion of babies with a name containing an x, you'd need to perform a weighted mean. + +```{r} +#| fig-alt: | +#| A time series showing the proportion of baby names that contain the letter x. +#| The proportion declines gradually from 8 per 1000 in 1880 to 4 per 1000 in +#| 1980, then increases rapidly to 16 per 1000 in 2019. + +babynames |> + group_by(year) |> + summarize(prop_x = mean(str_detect(name, "x"))) |> + ggplot(aes(x = year, y = prop_x)) + + geom_line() +``` + +There are two functions that are closely related to `str_detect()`: `str_subset()` and `str_which()`. +`str_subset()` returns a character vector containing only the strings that match. +`str_which()` returns an integer vector giving the positions of the strings that match. + +### Count matches + +The next step up in complexity from `str_detect()` is `str_count()`: rather than a true or false, it tells you how many matches there are in each string. + +```{r} +x <- c("apple", "banana", "pear") +str_count(x, "p") +``` + +Note that each match starts at the end of the previous match, i.e. regex matches never overlap. +For example, in `"abababa"`, how many times will the pattern `"aba"` match? +Regular expressions say two, not three: + +```{r} +str_count("abababa", "aba") +str_view("abababa", "aba") +``` + +It's natural to use `str_count()` with `mutate()`. +The following example uses `str_count()` with character classes to count the number of vowels and consonants in each name. + +```{r} +babynames |> + count(name) |> + mutate( + vowels = str_count(name, "[aeiou]"), + consonants = str_count(name, "[^aeiou]") + ) +``` + +If you look closely, you'll notice that there's something off with our calculations: "Aaban" contains three "a"s, but our summary reports only two vowels. +That's because regular expressions are case sensitive. +There are three ways we could fix this: + +- Add the upper case vowels to the character class: `str_count(name, "[aeiouAEIOU]")`. +- Tell the regular expression to ignore case: `str_count(name, regex("[aeiou]", ignore_case = TRUE))`. We'll talk about more in @sec-flags. +- Use `str_to_lower()` to convert the names to lower case: `str_count(str_to_lower(name), "[aeiou]")`. + +This variety of approaches is pretty typical when working with strings --- there are often multiple ways to reach your goal, either by making your pattern more complicated or by doing some preprocessing on your string. +If you get stuck trying one approach, it can often be useful to switch gears and tackle the problem from a different perspective. + +In this case, since we're applying two functions to the name, I think it's easier to transform it first: + +```{r} +babynames |> + count(name) |> + mutate( + name = str_to_lower(name), + vowels = str_count(name, "[aeiou]"), + consonants = str_count(name, "[^aeiou]") + ) +``` + +### Replace values + +As well as detecting and counting matches, we can also modify them with `str_replace()` and `str_replace_all()`. +`str_replace()` replaces the first match, and as the name suggests, `str_replace_all()` replaces all matches. + +```{r} +x <- c("apple", "pear", "banana") +str_replace_all(x, "[aeiou]", "-") +``` + +`str_remove()` and `str_remove_all()` are handy shortcuts for `str_replace(x, pattern, "")`: + +```{r} +x <- c("apple", "pear", "banana") +str_remove_all(x, "[aeiou]") +``` + +These functions are naturally paired with `mutate()` when doing data cleaning, and you'll often apply them repeatedly to peel off layers of inconsistent formatting. + +### Extract variables {#sec-extract-variables} + +The last function we'll discuss uses regular expressions to extract data out of one column into one or more new columns: `separate_wider_regex()`. +It's a peer of the `separate_wider_position()` and `separate_wider_delim()` functions that you learned about in @sec-string-columns. +These functions live in tidyr because they operate on (columns of) data frames, rather than individual vectors. + +Let's create a simple dataset to show how it works. +Here we have some data derived from `babynames` where we have the name, gender, and age of a bunch of people in a rather weird format[^regexps-5]: + +[^regexps-5]: We wish we could reassure you that you'd never see something this weird in real life, but unfortunately over the course of your career you're likely to see much weirder! + +```{r} +df <- tribble( + ~str, + "-F_34", + "-F_45", + "-N_33", + "-F_38", + "-F_58", + "-M_41", + "-F_84", +) +``` + +To extract this data using `separate_wider_regex()` we just need to construct a sequence of regular expressions that match each piece. +If we want the contents of that piece to appear in the output, we give it a name: + +```{r} +df |> + separate_wider_regex( + str, + patterns = c( + "<", + name = "[A-Za-z]+", + ">-", + gender = ".", + "_", + age = "[0-9]+" + ) + ) +``` + +If the match fails, you can use `too_short = "debug"` to figure out what went wrong, just like `separate_wider_delim()` and `separate_wider_position()`. + +### Exercises + +1. What baby name has the most vowels? + What name has the highest proportion of vowels? + (Hint: what is the denominator?) + +2. Replace all forward slashes in `"a/b/c/d/e"` with backslashes. + What happens if you attempt to undo the transformation by replacing all backslashes with forward slashes? + (We'll discuss the problem very soon.) + +3. Implement a simple version of `str_to_lower()` using `str_replace_all()`. + +4. Create a regular expression that will match telephone numbers as commonly written in your country. + +## Pattern details + +Now that you understand the basics of the pattern language and how to use it with some stringr and tidyr functions, it's time to dig into more of the details. +First, we'll start with **escaping**, which allows you to match metacharacters that would otherwise be treated specially. +Next, you'll learn about **anchors** which allow you to match the start or end of the string. +Then, you'll learn more about **character classes** and their shortcuts which allow you to match any character from a set. +Next, you'll learn the final details of **quantifiers** which control how many times a pattern can match. +Then, we have to cover the important (but complex) topic of **operator precedence** and parentheses. +And we'll finish off with some details of **grouping** components of the pattern. + +The terms we use here are the technical names for each component. +They're not always the most evocative of their purpose, but it's very helpful to know the correct terms if you later want to Google for more details. + +### Escaping {#sec-regexp-escaping} + +In order to match a literal `.`, you need an **escape** which tells the regular expression to match metacharacters[^regexps-6] literally. +Like strings, regexps use the backslash for escaping. +So, to match a `.`, you need the regexp `\.`. Unfortunately this creates a problem. +We use strings to represent regular expressions, and `\` is also used as an escape symbol in strings. +So to create the regular expression `\.` we need the string `"\\."`, as the following example shows. + +[^regexps-6]: The complete set of metacharacters is `.^$\|*+?{}[]()` + +```{r} +# To create the regular expression \., we need to use \\. +dot <- "\\." + +# But the expression itself only contains one \ +str_view(dot) + +# And this tells R to look for an explicit . +str_view(c("abc", "a.c", "bef"), "a\\.c") +``` + +In this book, we'll usually write regular expression without quotes, like `\.`. +If we need to emphasize what you'll actually type, we'll surround it with quotes and add extra escapes, like `"\\."`. + +If `\` is used as an escape character in regular expressions, how do you match a literal `\`? +Well, you need to escape it, creating the regular expression `\\`. +To create that regular expression, you need to use a string, which also needs to escape `\`. +That means to match a literal `\` you need to write `"\\\\"` --- you need four backslashes to match one! + +```{r} +x <- "a\\b" +str_view(x) +str_view(x, "\\\\") +``` + +Alternatively, you might find it easier to use the raw strings you learned about in @sec-raw-strings). +That lets you avoid one layer of escaping: + +```{r} +str_view(x, r"{\\}") +``` + +If you're trying to match a literal `.`, `$`, `|`, `*`, `+`, `?`, `{`, `}`, `(`, `)`, there's an alternative to using a backslash escape: you can use a character class: `[.]`, `[$]`, `[|]`, \... +all match the literal values. + +```{r} +str_view(c("abc", "a.c", "a*c", "a c"), "a[.]c") +str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c") +``` + +### Anchors + +By default, regular expressions will match any part of a string. +If you want to match at the start or end you need to **anchor** the regular expression using `^` to match the start or `$` to match the end: + +```{r} +str_view(fruit, "^a") +str_view(fruit, "a$") +``` + +It's tempting to think that `$` should match the start of a string, because that's how we write dollar amounts, but that's not what regular expressions want. + +To force a regular expression to match only the full string, anchor it with both `^` and `$`: + +```{r} +str_view(fruit, "apple") +str_view(fruit, "^apple$") +``` + +You can also match the boundary between words (i.e. the start or end of a word) with `\b`. +This can be particularly useful when using RStudio's find and replace tool. +For example, if to find all uses of `sum()`, you can search for `\bsum\b` to avoid matching `summarize`, `summary`, `rowsum` and so on: + +```{r} +x <- c("summary(x)", "summarize(df)", "rowsum(x)", "sum(x)") +str_view(x, "sum") +str_view(x, "\\bsum\\b") +``` + +When used alone, anchors will produce a zero-width match: + +```{r} +str_view("abc", c("$", "^", "\\b")) +``` + +This helps you understand what happens when you replace a standalone anchor: + +```{r} +str_replace_all("abc", c("$", "^", "\\b"), "--") +``` + +### Character classes + +A **character class**, or character **set**, allows you to match any character in a set. +As we discussed above, you can construct your own sets with `[]`, where `[abc]` matches "a", "b", or "c" and `[^abc]` matches any character except "a", "b", or "c". +Apart from `^` there are two other characters that have special meaning inside of `[]:` + +- `-` defines a range, e.g., `[a-z]` matches any lower case letter and `[0-9]` matches any number. +- `\` escapes special characters, so `[\^\-\]]` matches `^`, `-`, or `]`. + +Here are few examples: + +```{r} +x <- "abcd ABCD 12345 -!@#%." +str_view(x, "[abc]+") +str_view(x, "[a-z]+") +str_view(x, "[^a-z0-9]+") + +# You need an escape to match characters that are otherwise +# special inside of [] +str_view("a-b-c", "[a-c]") +str_view("a-b-c", "[a\\-c]") +``` + +Some character classes are used so commonly that they get their own shortcut. +You've already seen `.`, which matches any character apart from a newline. +There are three other particularly useful pairs[^regexps-7]: + +[^regexps-7]: Remember, to create a regular expression containing `\d` or `\s`, you'll need to escape the `\` for the string, so you'll type `"\\d"` or `"\\s"`. + +- `\d` matches any digit;\ + `\D` matches anything that isn't a digit. +- `\s` matches any whitespace (e.g., space, tab, newline);\ + `\S` matches anything that isn't whitespace. +- `\w` matches any "word" character, i.e. letters and numbers;\ + `\W` matches any "non-word" character. + +The following code demonstrates the six shortcuts with a selection of letters, numbers, and punctuation characters. + +```{r} +x <- "abcd ABCD 12345 -!@#%." +str_view(x, "\\d+") +str_view(x, "\\D+") +str_view(x, "\\s+") +str_view(x, "\\S+") +str_view(x, "\\w+") +str_view(x, "\\W+") +``` + +### Quantifiers {#sec-quantifiers} + +**Quantifiers** control how many times a pattern matches. +In @sec-reg-basics you learned about `?` (0 or 1 matches), `+` (1 or more matches), and `*` (0 or more matches). +For example, `colou?r` will match American or British spelling, `\d+` will match one or more digits, and `\s?` will optionally match a single item of whitespace. +You can also specify the number of matches precisely with `{}`: + +- `{n}` matches exactly n times. +- `{n,}` matches at least n times. +- `{n,m}` matches between n and m times. + +### Operator precedence and parentheses + +What does `ab+` match? +Does it match "a" followed by one or more "b"s, or does it match "ab" repeated any number of times? +What does `^a|b$` match? +Does it match the complete string a or the complete string b, or does it match a string starting with a or a string ending with b? + +The answer to these questions is determined by operator precedence, similar to the PEMDAS or BEDMAS rules you might have learned in school. +You know that `a + b * c` is equivalent to `a + (b * c)` not `(a + b) * c` because `*` has higher precedence and `+` has lower precedence: you compute `*` before `+`. + +Similarly, regular expressions have their own precedence rules: quantifiers have high precedence and alternation has low precedence which means that `ab+` is equivalent to `a(b+)`, and `^a|b$` is equivalent to `(^a)|(b$)`. +Just like with algebra, you can use parentheses to override the usual order. +But unlike algebra you're unlikely to remember the precedence rules for regexes, so feel free to use parentheses liberally. + +### Grouping and capturing + +As well as overriding operator precedence, parentheses have another important effect: they create **capturing groups** that allow you to use sub-components of the match. + +The first way to use a capturing group is to refer back to it within a match with **back reference**: `\1` refers to the match contained in the first parenthesis, `\2` in the second parenthesis, and so on. +For example, the following pattern finds all fruits that have a repeated pair of letters: + +```{r} +str_view(fruit, "(..)\\1") +``` + +And this one finds all words that start and end with the same pair of letters: + +```{r} +str_view(words, "^(..).*\\1$") +``` + +You can also use back references in `str_replace()`. +For example, this code switches the order of the second and third words in `sentences`: + +```{r} +sentences |> + str_replace("(\\w+) (\\w+) (\\w+)", "\\1 \\3 \\2") |> + str_view() +``` + +If you want to extract the matches for each group you can use `str_match()`. +But `str_match()` returns a matrix, so it's not particularly easy to work with[^regexps-8]: + +[^regexps-8]: Mostly because we never discuss matrices in this book! + +```{r} +sentences |> + str_match("the (\\w+) (\\w+)") |> + head() +``` + +You could convert to a tibble and name the columns: + +```{r} +sentences |> + str_match("the (\\w+) (\\w+)") |> + as_tibble(.name_repair = "minimal") |> + set_names("match", "word1", "word2") +``` + +But then you've basically recreated your own version of `separate_wider_regex()`. +Indeed, behind the scenes, `separate_wider_regex()` converts your vector of patterns to a single regex that uses grouping to capture the named components. + +Occasionally, you'll want to use parentheses without creating matching groups. +You can create a non-capturing group with `(?:)`. + +```{r} +x <- c("a gray cat", "a grey dog") +str_match(x, "gr(e|a)y") +str_match(x, "gr(?:e|a)y") +``` + +### Exercises + +1. How would you match the literal string `"'\`? How about `"$^$"`? + +2. Explain why each of these patterns don't match a `\`: `"\"`, `"\\"`, `"\\\"`. + +3. Given the corpus of common words in `stringr::words`, create regular expressions that find all words that: + + a. Start with "y". + b. Don't start with "y". + c. End with "x". + d. Are exactly three letters long. (Don't cheat by using `str_length()`!) + e. Have seven letters or more. + f. Contain a vowel-consonant pair. + g. Contain at least two vowel-consonant pairs in a row. + h. Only consist of repeated vowel-consonant pairs. + +4. Create 11 regular expressions that match the British or American spellings for each of the following words: airplane/aeroplane, aluminum/aluminium, analog/analogue, ass/arse, center/centre, defense/defence, donut/doughnut, gray/grey, modeling/modelling, skeptic/sceptic, summarize/summarise. + Try and make the shortest possible regex! + +5. Switch the first and last letters in `words`. + Which of those strings are still `words`? + +6. Describe in words what these regular expressions match: (read carefully to see if each entry is a regular expression or a string that defines a regular expression.) + + a. `^.*$` + b. `"\\{.+\\}"` + c. `\d{4}-\d{2}-\d{2}` + d. `"\\\\{4}"` + e. `\..\..\..` + f. `(.)\1\1` + g. `"(..)\\1"` + +7. Solve the beginner regexp crosswords at . + +## Pattern control + +It's possible to exercise extra control over the details of the match by using a pattern object instead of just a string. +This allows you to control the so called regex flags and match various types of fixed strings, as described below. + +### Regex flags {#sec-flags} + +There are a number of settings that can be used to control the details of the regexp. +These settings are often called **flags** in other programming languages. +In stringr, you can use these by wrapping the pattern in a call to `regex()`. +The most useful flag is probably `ignore_case = TRUE` because it allows characters to match either their uppercase or lowercase forms: + +```{r} +bananas <- c("banana", "Banana", "BANANA") +str_view(bananas, "banana") +str_view(bananas, regex("banana", ignore_case = TRUE)) +``` + +If you're doing a lot of work with multiline strings (i.e. strings that contain `\n`), `dotall`and `multiline` may also be useful: + +- `dotall = TRUE` lets `.` match everything, including `\n`: + + ```{r} + x <- "Line 1\nLine 2\nLine 3" + str_view(x, ".Line") + str_view(x, regex(".Line", dotall = TRUE)) + ``` + +- `multiline = TRUE` makes `^` and `$` match the start and end of each line rather than the start and end of the complete string: + + ```{r} + x <- "Line 1\nLine 2\nLine 3" + str_view(x, "^Line") + str_view(x, regex("^Line", multiline = TRUE)) + ``` + +Finally, if you're writing a complicated regular expression and you're worried you might not understand it in the future, you might try `comments = TRUE`. +It tweaks the pattern language to ignore spaces and new lines, as well as everything after `#`. +This allows you to use comments and whitespace to make complex regular expressions more understandable[^regexps-9], as in the following example: + +[^regexps-9]: `comments = TRUE` is particularly effective in combination with a raw string, as we use here. + +```{r} +phone <- regex( + r"( + \(? # optional opening parens + (\d{3}) # area code + [)\-]? # optional closing parens or dash + \ ? # optional space + (\d{3}) # another three numbers + [\ -]? # optional space or dash + (\d{4}) # four more numbers + )", + comments = TRUE +) + +str_extract(c("514-791-8141", "(123) 456 7890", "123456"), phone) +``` + +If you're using comments and want to match a space, newline, or `#`, you'll need to escape it with `\`. + +### Fixed matches + +You can opt-out of the regular expression rules by using `fixed()`: + +```{r} +str_view(c("", "a", "."), fixed(".")) +``` + +`fixed()` also gives you the ability to ignore case: + +```{r} +str_view("x X", "X") +str_view("x X", fixed("X", ignore_case = TRUE)) +``` + +If you're working with non-English text, you will probably want `coll()` instead of `fixed()`, as it implements the full rules for capitalization as used by the `locale` you specify. +See @sec-other-languages for more details on locales. + +```{r} +str_view("i İ ı I", fixed("İ", ignore_case = TRUE)) +str_view("i İ ı I", coll("İ", ignore_case = TRUE, locale = "tr")) +``` + +## Practice + +To put these ideas into practice we'll solve a few semi-authentic problems next. +We'll discuss three general techniques: + +1. checking your work by creating simple positive and negative controls +2. combining regular expressions with Boolean algebra +3. creating complex patterns using string manipulation + +### Check your work + +First, let's find all sentences that start with "The". +Using the `^` anchor alone is not enough: + +```{r} +str_view(sentences, "^The") +``` + +Because that pattern also matches sentences starting with words like `They` or `These`. +We need to make sure that the "e" is the last letter in the word, which we can do by adding a word boundary: + +```{r} +str_view(sentences, "^The\\b") +``` + +What about finding all sentences that begin with a pronoun? + +```{r} +str_view(sentences, "^She|He|It|They\\b") +``` + +A quick inspection of the results shows that we're getting some spurious matches. +That's because we've forgotten to use parentheses: + +```{r} +str_view(sentences, "^(She|He|It|They)\\b") +``` + +You might wonder how you might spot such a mistake if it didn't occur in the first few matches. +A good technique is to create a few positive and negative matches and use them to test that your pattern works as expected: + +```{r} +pos <- c("He is a boy", "She had a good time") +neg <- c("Shells come from the sea", "Hadley said 'It's a great day'") + +pattern <- "^(She|He|It|They)\\b" +str_detect(pos, pattern) +str_detect(neg, pattern) +``` + +It's typically much easier to come up with good positive examples than negative examples, because it takes a while before you're good enough with regular expressions to predict where your weaknesses are. +Nevertheless, they're still useful: as you work on the problem you can slowly accumulate a collection of your mistakes, ensuring that you never make the same mistake twice. + +### Boolean operations {#sec-boolean-operations} + +Imagine we want to find words that only contain consonants. +One technique is to create a character class that contains all letters except for the vowels (`[^aeiou]`), then allow that to match any number of letters (`[^aeiou]+`), then force it to match the whole string by anchoring to the beginning and the end (`^[^aeiou]+$`): + +```{r} +str_view(words, "^[^aeiou]+$") +``` + +But you can make this problem a bit easier by flipping the problem around. +Instead of looking for words that contain only consonants, we could look for words that don't contain any vowels: + +```{r} +str_view(words[!str_detect(words, "[aeiou]")]) +``` + +This is a useful technique whenever you're dealing with logical combinations, particularly those involving "and" or "not". +For example, imagine if you want to find all words that contain "a" and "b". +There's no "and" operator built in to regular expressions so we have to tackle it by looking for all words that contain an "a" followed by a "b", or a "b" followed by an "a": + +```{r} +str_view(words, "a.*b|b.*a") +``` + +It's simpler to combine the results of two calls to `str_detect()`: + +```{r} +words[str_detect(words, "a") & str_detect(words, "b")] +``` + +What if we wanted to see if there was a word that contains all vowels? +If we did it with patterns we'd need to generate 5! +(120) different patterns: + +```{r} +#| results: false +words[str_detect(words, "a.*e.*i.*o.*u")] +# ... +words[str_detect(words, "u.*o.*i.*e.*a")] +``` + +It's much simpler to combine five calls to `str_detect()`: + +```{r} +words[ + str_detect(words, "a") & + str_detect(words, "e") & + str_detect(words, "i") & + str_detect(words, "o") & + str_detect(words, "u") +] +``` + +In general, if you get stuck trying to create a single regexp that solves your problem, take a step back and think if you could break the problem down into smaller pieces, solving each challenge before moving onto the next one. + +### Creating a pattern with code + +What if we wanted to find all `sentences` that mention a color? +The basic idea is simple: we just combine alternation with word boundaries. + +```{r} +str_view(sentences, "\\b(red|green|blue)\\b") +``` + +But as the number of colors grows, it would quickly get tedious to construct this pattern by hand. +Wouldn't it be nice if we could store the colors in a vector? + +```{r} +rgb <- c("red", "green", "blue") +``` + +Well, we can! +We'd just need to create the pattern from the vector using `str_c()` and `str_flatten()`: + +```{r} +str_c("\\b(", str_flatten(rgb, "|"), ")\\b") +``` + +We could make this pattern more comprehensive if we had a good list of colors. +One place we could start from is the list of built-in colors that R can use for plots: + +```{r} +str_view(colors()) +``` + +But lets first eliminate the numbered variants: + +```{r} +cols <- colors() +cols <- cols[!str_detect(cols, "\\d")] +str_view(cols) +``` + +Then we can turn this into one giant pattern. +We won't show the pattern here because it's huge, but you can see it working: + +```{r} +pattern <- str_c("\\b(", str_flatten(cols, "|"), ")\\b") +str_view(sentences, pattern) +``` + +In this example, `cols` only contains numbers and letters so you don't need to worry about metacharacters. +But in general, whenever you create patterns from existing strings it's wise to run them through `str_escape()` to ensure they match literally. + +### Exercises + +1. For each of the following challenges, try solving it by using both a single regular expression, and a combination of multiple `str_detect()` calls. + + a. Find all `words` that start or end with `x`. + b. Find all `words` that start with a vowel and end with a consonant. + c. Are there any `words` that contain at least one of each different vowel? + +2. Construct patterns to find evidence for and against the rule "i before e except after c"? + +3. `colors()` contains a number of modifiers like "lightgray" and "darkblue". + How could you automatically identify these modifiers? + (Think about how you might detect and then remove the colors that are modified). + +4. Create a regular expression that finds any base R dataset. + You can get a list of these datasets via a special use of the `data()` function: `data(package = "datasets")$results[, "Item"]`. + Note that a number of old datasets are individual vectors; these contain the name of the grouping "data frame" in parentheses, so you'll need to strip those off. + +## Regular expressions in other places + +Just like in the stringr and tidyr functions, there are many other places in R where you can use regular expressions. +The following sections describe some other useful functions in the wider tidyverse and base R. + +### tidyverse + +There are three other particularly useful places where you might want to use a regular expressions + +- `matches(pattern)` will select all variables whose name matches the supplied pattern. + It's a "tidyselect" function that you can use anywhere in any tidyverse function that selects variables (e.g., `select()`, `rename_with()` and `across()`). + +- `pivot_longer()'s` `names_pattern` argument takes a vector of regular expressions, just like `separate_wider_regex()`. + It's useful when extracting data out of variable names with a complex structure + +- The `delim` argument in `separate_longer_delim()` and `separate_wider_delim()` usually matches a fixed string, but you can use `regex()` to make it match a pattern. + This is useful, for example, if you want to match a comma that is optionally followed by a space, i.e. `regex(", ?")`. + +### Base R + +`apropos(pattern)` searches all objects available from the global environment that match the given pattern. +This is useful if you can't quite remember the name of a function: + +```{r} +apropos("replace") +``` + +`list.files(path, pattern)` lists all files in `path` that match a regular expression `pattern`. +For example, you can find all the R Markdown files in the current directory with: + +```{r} +head(list.files(pattern = "\\.Rmd$")) +``` + +It's worth noting that the pattern language used by base R is very slightly different to that used by stringr. +That's because stringr is built on top of the [stringi package](https://stringi.gagolewski.com), which is in turn built on top of the [ICU engine](https://unicode-org.github.io/icu/userguide/strings/regexp.html), whereas base R functions use either the [TRE engine](https://github.com/laurikari/tre) or the [PCRE engine](https://www.pcre.org), depending on whether or not you've set `perl = TRUE`. +Fortunately, the basics of regular expressions are so well established that you'll encounter few variations when working with the patterns you'll learn in this book. +You only need to be aware of the difference when you start to rely on advanced features like complex Unicode character ranges or special features that use the `(?…)` syntax. + +## Summary + +With every punctuation character potentially overloaded with meaning, regular expressions are one of the most compact languages out there. +They're definitely confusing at first but as you train your eyes to read them and your brain to understand them, you unlock a powerful skill that you can use in R and in many other places. + +In this chapter, you've started your journey to become a regular expression master by learning the most useful stringr functions and the most important components of the regular expression language. +And there are plenty of resources to learn more. + +A good place to start is `vignette("regular-expressions", package = "stringr")`: it documents the full set of syntax supported by stringr. +Another useful reference is [https://www.regular-expressions.info/](https://www.regular-expressions.info/tutorial.html). +It's not R specific, but you can use it to learn about the most advanced features of regexes and how they work under the hood. + +It's also good to know that stringr is implemented on top of the stringi package by Marek Gagolewski. +If you're struggling to find a function that does what you need in stringr, don't be afraid to look in stringi. +You'll find stringi very easy to pick up because it follows many of the the same conventions as stringr. + +In the next chapter, we'll talk about a data structure closely related to strings: factors. +Factors are used to represent categorical data in R, i.e. data with a fixed and known set of possible values identified by a vector of strings. From 37645f2b792196bda9757776843324085c098c5a Mon Sep 17 00:00:00 2001 From: Eric Scopinho <61020302+scopinho@users.noreply.github.com> Date: Mon, 15 Jan 2024 12:34:41 -0300 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Ariana --- factors.qmd | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/factors.qmd b/factors.qmd index 1ca83bc12..63f3e33da 100644 --- a/factors.qmd +++ b/factors.qmd @@ -40,9 +40,9 @@ Imagine que você tem uma variável que registra o mês: x1 <- c("Dez", "Abr", "Jan", "Mar") ``` -Usar uma string para registrar esta variável tem dois problemas: +Usar uma *string* para registrar esta variável tem dois problemas: -1. Existem apenas doze meses possíveis e não há nada que o salve de erros de digitação: +1. Existem apenas doze meses possíveis e não há nada que proteja você contra erros de digitação: ```{r} x2 <- c("Dez", "Abr", "Jam", "Mar") @@ -119,7 +119,7 @@ df <- read_csv(csv, col_types = cols(mes = col_factor(niveis_meses))) df$mes ``` -## Questionario +## Questionário No restante deste capítulo, usaremos `dados::questionario`. É uma amostra de dados da [Pesquisa Social Geral](https://gss.norc.org), uma pesquisa de longa data nos EUA conduzida pela organização de pesquisa independente NORC da Universidade de Chicago. @@ -131,7 +131,7 @@ questionario (Lembre-se, como este conjunto de dados é fornecido por um pacote, você pode obter mais informações sobre as variáveis ​​com `?questionario`.) -Quando os fatores são armazenados em um tibble, você não consegue ver seus níveis tão facilmente. +Quando os fatores são armazenados em um *tibble*, você não consegue ver seus níveis tão facilmente. Uma maneira de visualizá-los é com `count()`: ```{r} @@ -162,7 +162,7 @@ Por exemplo, imagine que você deseja explorar o número médio de horas gastas ```{r} #| fig-alt: | -#| Im gráfico de dispersão com horas_tv no eixo-x e religião no eixo-y. +#| Um gráfico de dispersão com horas_tv no eixo-x e religião no eixo-y. #| O eixo-y parece estar ordenado de forma arbitrária, tornando difícil entender #| qualquer padrão geral. sumario_religiao <- questionario |> @@ -187,7 +187,7 @@ Podemos melhorá-lo reordenando os níveis de `religiao` usando `fct_reorder()`. ```{r} #| fig-alt: | #| O mesmo gráfico de dispersão acima, mas agora a religião é exibida em -#| ordem crescente de horas_tv. "Outra religião oriental Leste" tem o menor número de horas de TV +#| ordem crescente de horas_tv. "Outra religião oriental Leste" tem o menor número de horas de TV, #| menos de 2, e “Não sabe” tem o valor mais alto (acima de 5). ggplot(sumario_religiao, aes(x = horas_tv, y = fct_reorder(religiao, horas_tv))) + geom_point() @@ -195,7 +195,7 @@ ggplot(sumario_religiao, aes(x = horas_tv, y = fct_reorder(religiao, horas_tv))) Reordenar a religião torna muito mais fácil ver que as pessoas na categoria “Não sabe” assistem muito mais TV, e o Hinduísmo e outras religiões orientais assistem muito menos. -À medida que você começa a fazer transformações mais complicadas, recomendamos movê-las de `aes()` para uma etapa separada `mutate()`. +À medida que você começa a fazer transformações mais complicadas, recomendamos movê-las de `aes()` para uma etapa separada, usando `mutate()`. Por exemplo, você poderia reescrever o gráfico acima como: ```{r} @@ -213,8 +213,8 @@ E se criarmos um gráfico semelhante observando como a idade média varia de aco ```{r} #| fig-alt: | -#| Um gráfico de dispersão com idade no eixo-x e renda no eixo-y. Renda -#| foi reordenado em ordem de idade média, o que não faz muito +#| Um gráfico de dispersão com idade no eixo-x e renda no eixo-y. A renda +#| foi reordenada com base na idade média, o que não faz muito #| sentido. Uma seção do eixo-y vai de US$ 6.000 a 6.999, depois < US$ 1.000, #| então $8000-9999. sumario_renda <- questionario |> @@ -229,7 +229,7 @@ ggplot(sumario_renda, aes(x = idade, y = fct_reorder(renda, idade))) + ``` Aqui, reordenar arbitrariamente os níveis não é uma boa ideia! -Isso porque o `renda` já tem uma ordem de princípios com a qual não devemos mexer. +Isso porque `renda` já tem uma ordem de princípios com a qual não devemos mexer. Reserve `fct_reorder()` para fatores cujos níveis são ordenados arbitrariamente. No entanto, faz sentido colocar "Não se aplica" na frente com os outros níveis especiais. @@ -238,7 +238,7 @@ Você pode usar `fct_relevel()`. ```{r} #| fig-alt: | -#| O mesmo gráfico de dispersão, mas agora "Não se aplica", é exibido no +#| O mesmo gráfico de dispersão, mas agora "Não se aplica", é exibido na #| parte inferior do eixo-y. Geralmente há uma associação positiva #| entre renda e idade, e a faixa de renda com maior média #| idade é "Não se aplica". @@ -247,7 +247,7 @@ ggplot(sumario_renda, aes(x = idade, y = fct_relevel(renda, "Não se aplica"))) geom_point() ``` -Por que você acha que a média de idade para “Não se aplica” é tão alta? +Por que você acha que a idade média para “Não se aplica” é tão alta? Outro tipo de reordenação é útil quando você está colorindo as linhas em um gráfico. `fct_reorder2(f, x, y)` reordena o fator `f` pelos valores `y` associados aos maiores valores `x`. @@ -259,12 +259,12 @@ Isso torna o gráfico mais fácil de ler porque as cores da linha na extremidade #| fig-alt: | #| Um gráfico de linha com idade no eixo-x e proporção no eixo-y. #| Há uma linha para cada categoria de estado civil: sem resposta, -#| nunca casou, separado(a), divorciado(a), viúvo(a) e casado(a). Isso é -#| um pouco difícil de ler o gráfico porque a ordem da legenda não é +#| nunca casou, separado(a), divorciado(a), viúvo(a) e casado(a). O gráfico é +#| um pouco difícil de ler porque a ordem da legenda não é #| relacionada às linhas do gráfico. Reorganizar a legenda faz #| o gráfico ser mais fácil de ser lido porque as cores da legenda agora correspondem à #| ordem das linhas na extremidade direita do gráfico. Você pode ver alguns -#| padrões não surpreendentes: a proporção de nunca casados(as) ​​diminui comh +#| padrões não surpreendentes: a proporção de nunca casados(as) ​​diminui com a #| idade, casado(a) forma um U de cabeça para baixo e viúvo(a) começa #| baixo, mas aumenta acentuadamente após os 60 anos. por_idade <- questionario |> @@ -304,7 +304,7 @@ questionario |> 1. Existem alguns números suspeitosamente altos em `horas_tv`. A média é uma boa sumarização? -2. Para cada fator em `quesitonario` identifique se a ordem dos níveis é arbitrária ou baseada em princípios. +2. Para cada fator em `quesitonario`, identifique se a ordem dos níveis é arbitrária ou baseada em princípios. 3. Por que mover "Não se aplica" para a frente dos níveis o moveu para a parte inferior do gráfico?? @@ -313,7 +313,7 @@ questionario |> Mais poderoso do que alterar as ordens dos níveis é alterar os seus valores. Isso permite esclarecer rótulos para publicação e recolher níveis para exibições de alto nível. A ferramenta mais geral e poderosa é `fct_recode()`. -Ele permite recodificar ou alterar o valor de cada nível. +Ela permite recodificar ou alterar o valor de cada nível. Por exemplo, pegue a variável `partido` do *data frame* `questionario`: ```{r} @@ -402,9 +402,9 @@ Leia a documentação para aprender sobre `fct_lump_min()` e `fct_lump_prop()` q ### Exercícios -1. Como mudaram ao longo do tempo as proporções de pessoas que se identificam como Democratas, Republicanas e Independentes?? +1. Como mudaram ao longo do tempo as proporções de pessoas que se identificam como Democratas, Republicanas e Independentes? -2. Como você poderia agrupar a “renda” em um pequeno conjunto de categorias?? +2. Como você poderia agrupar a “renda” em um pequeno conjunto de categorias? 3. Observe que existem 9 grupos (excluindo outros) no exemplo `fct_lump` acima. Por que não 10? @@ -420,7 +420,7 @@ Você pode reconhecê-los ao imprimir porque eles usam `<` entre os níveis dos ordered(c("a", "b", "c")) ``` -Na prática, os fatores `ordenados()` se comportam de forma muito semelhante aos fatores regulares. +Na prática, os fatores ordenados (`ordered()`) se comportam de forma muito semelhante aos fatores regulares. Existem apenas dois lugares onde você pode notar um comportamento diferente: - Se você mapear um fator ordenado para colorir ou preencher no ggplot2, o padrão será `scale_color_viridis()`/`scale_fill_viridis()`, uma escala de cores que implica um ranqueamento. From b54bbeb31a663078ec3ffaac73493571932fc470 Mon Sep 17 00:00:00 2001 From: Eric Scopinho <61020302+scopinho@users.noreply.github.com> Date: Sat, 17 Feb 2024 07:08:23 -0300 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: Cesar A. Galvao --- factors.qmd | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/factors.qmd b/factors.qmd index 63f3e33da..cd8a8e003 100644 --- a/factors.qmd +++ b/factors.qmd @@ -13,7 +13,7 @@ source("_common.R") Fatores (*factors*) são usados ​​para variáveis ​​categóricas, variáveis ​​que possuem um conjunto fixo e conhecido de valores possíveis. Eles também são úteis quando você deseja exibir vetores de caracteres em ordem não alfabética. -Começaremos motivando porque os fatores são necessários para a análise de dados[^factors-1] e como você pode criá-los com a fnução `factor()`. Em seguida, apresentaremos o conjunto de dados `questionario` do pacote dados, que contém um monte de variáveis ​​​​categóricas para experimentarmos. +Começaremos motivando a necessidade de fatores para a análise de dados[^factors-1] e como você pode criá-los com a função `factor()`. Em seguida, apresentaremos o conjunto de dados `questionario` do pacote dados, que contém um monte de variáveis ​​​​categóricas para experimentarmos. Em seguida, você usará esse conjunto de dados para praticar a modificação da ordem e dos valores dos fatores, antes de terminarmos com uma discussão sobre fatores ordenados. [^factors-1]: Eles também são muito importantes para modelagem. @@ -165,14 +165,14 @@ Por exemplo, imagine que você deseja explorar o número médio de horas gastas #| Um gráfico de dispersão com horas_tv no eixo-x e religião no eixo-y. #| O eixo-y parece estar ordenado de forma arbitrária, tornando difícil entender #| qualquer padrão geral. -sumario_religiao <- questionario |> +resumo_religiao <- questionario |> group_by(religiao) |> summarize( horas_tv = mean(horas_tv, na.rm = TRUE), n = n() ) -ggplot(sumario_religiao, aes(x = horas_tv, y = religiao)) + +ggplot(resumo_religiao, aes(x = horas_tv, y = religiao)) + geom_point() ``` @@ -189,7 +189,7 @@ Podemos melhorá-lo reordenando os níveis de `religiao` usando `fct_reorder()`. #| O mesmo gráfico de dispersão acima, mas agora a religião é exibida em #| ordem crescente de horas_tv. "Outra religião oriental Leste" tem o menor número de horas de TV, #| menos de 2, e “Não sabe” tem o valor mais alto (acima de 5). -ggplot(sumario_religiao, aes(x = horas_tv, y = fct_reorder(religiao, horas_tv))) + +ggplot(resumo_religiao, aes(x = horas_tv, y = fct_reorder(religiao, horas_tv))) + geom_point() ``` @@ -201,7 +201,7 @@ Por exemplo, você poderia reescrever o gráfico acima como: ```{r} #| eval: false -sumario_religiao |> +resumo_religiao |> mutate( religiao = fct_reorder(religiao, horas_tv) ) |> @@ -215,21 +215,21 @@ E se criarmos um gráfico semelhante observando como a idade média varia de aco #| fig-alt: | #| Um gráfico de dispersão com idade no eixo-x e renda no eixo-y. A renda #| foi reordenada com base na idade média, o que não faz muito -#| sentido. Uma seção do eixo-y vai de US$ 6.000 a 6.999, depois < US$ 1.000, +#| sentido. Uma seção do eixo-y vai de US$6.000 a 6.999, depois < US$ 1.000, #| então $8000-9999. -sumario_renda <- questionario |> +resumo_renda <- questionario |> group_by(renda) |> summarize( idade = mean(idade, na.rm = TRUE), n = n() ) -ggplot(sumario_renda, aes(x = idade, y = fct_reorder(renda, idade))) + +ggplot(resumo_renda, aes(x = idade, y = fct_reorder(renda, idade))) + geom_point() ``` Aqui, reordenar arbitrariamente os níveis não é uma boa ideia! -Isso porque `renda` já tem uma ordem de princípios com a qual não devemos mexer. +Isso porque `renda` já tem uma ordem fundamentada com a qual não devemos mexer. Reserve `fct_reorder()` para fatores cujos níveis são ordenados arbitrariamente. No entanto, faz sentido colocar "Não se aplica" na frente com os outros níveis especiais. @@ -243,7 +243,7 @@ Você pode usar `fct_relevel()`. #| entre renda e idade, e a faixa de renda com maior média #| idade é "Não se aplica". -ggplot(sumario_renda, aes(x = idade, y = fct_relevel(renda, "Não se aplica"))) + +ggplot(resumo_renda, aes(x = idade, y = fct_relevel(renda, "Não se aplica"))) + geom_point() ``` @@ -290,7 +290,7 @@ Combine-a com a `fct_rev()` se desejar que eles aumentem a frequência, de modo ```{r} #| fig-alt: | -#| Um gráfico de barras de estado cicil ordenado do menos para mais comum: +#| Um gráfico de barras de estado civil ordenado do menos para mais comum: #| sem resposta (~0), separado(a) (~1,000), viúvo(a) (~2,000), divorciado(a) #| (~3,000), nuca casou (~5,000), casado(a) (~10,000). questionario |> @@ -302,18 +302,18 @@ questionario |> ### Exercícios 1. Existem alguns números suspeitosamente altos em `horas_tv`. - A média é uma boa sumarização? + A média é um bom resumo? -2. Para cada fator em `quesitonario`, identifique se a ordem dos níveis é arbitrária ou baseada em princípios. +2. Para cada fator em `questionario`, identifique se a ordem dos níveis é arbitrária ou fundamentada. -3. Por que mover "Não se aplica" para a frente dos níveis o moveu para a parte inferior do gráfico?? +3. Por que mover "Não se aplica" para a frente dos níveis o moveu para a parte inferior do gráfico? ## Modificando os níveis do fator Mais poderoso do que alterar as ordens dos níveis é alterar os seus valores. Isso permite esclarecer rótulos para publicação e recolher níveis para exibições de alto nível. A ferramenta mais geral e poderosa é `fct_recode()`. -Ela permite recodificar ou alterar o valor de cada nível. +Ela permite recodificar, ou alterar, o valor de cada nível. Por exemplo, pegue a variável `partido` do *data frame* `questionario`: ```{r} @@ -329,7 +329,7 @@ questionario |> mutate( partido = fct_recode(partido, "Indivíduo fortemente republicano" = "Fortemente republicano", - "Indivíduo não fortemente republicano" = "Não fortemente repubicano", + "Indivíduo não fortemente republicano" = "Não fortemente republicano", "Indivíduo independente com inclinação republicana" = "Independente, inclinação republicana", "Indivíduo independente com inclinação democrata" = "Independente, inclinação democrata", "Indivíduo não fortemente democrata" = "Não fortemente democrata", @@ -349,7 +349,7 @@ questionario |> mutate( partido = fct_recode(partido, "Indivíduo fortemente republicano" = "Fortemente republicano", - "Indivíduo não fortemente republicano" = "Não fortemente repubicano", + "Indivíduo não fortemente republicano" = "Não fortemente republicano", "Indivíduo independente com inclinação republicana" = "Independente, inclinação republicana", "Indivíduo independente com inclinação democrata" = "Independente, inclinação democrata", "Indivíduo não fortemente democrata" = "Não fortemente democrata", @@ -371,7 +371,7 @@ questionario |> mutate( partido = fct_collapse(partido, "other" = c("Sem resposta", "Não sabe", "Outro partido"), - "rep" = c("Fortemente republicano", "Não fortemente repubicano"), + "rep" = c("Fortemente republicano", "Não fortemente republicano"), "ind" = c("Independente, inclinação republicana", "Independente", "Independente, inclinação democrata"), "dem" = c("Não fortemente democrata", "Fortemente democrata") ) @@ -408,7 +408,7 @@ Leia a documentação para aprender sobre `fct_lump_min()` e `fct_lump_prop()` q 3. Observe que existem 9 grupos (excluindo outros) no exemplo `fct_lump` acima. Por que não 10? - (Dica: digite `?fct_lump` e encontre o padrão para o argumento `other_level` é "Other".) + (Dica: digite `?fct_lump` e note que o padrão para o argumento `other_level` é "Other".) ## Fatores ordenados {#sec-ordered-factors} @@ -431,7 +431,7 @@ Dada a utilidade discutível dessas diferenças, geralmente não recomendamos o ## Resumo Este capítulo apresentou o prático pacote forcats para trabalhar com fatores, apresentando as funções mais comumente usadas. -O pacote forcats contém uma ampla gama de outras funções auxiliares que não tivemos espaço para discutir aqui, portanto, sempre que você estiver enfrentando um desafio de análise com fatores que não encontrou antes, recomendo fortemente dar uma olhada no [índice de referência](https://forcats.tidyverse.org/reference/index.html) para ver se existe uma função pronta que pode ajudar a resolver seu problema. +O pacote forcats contém uma ampla gama de outras funções auxiliares que não tivemos espaço para discutir aqui, portanto, sempre que você estiver enfrentando um desafio de análise com fatores que não encontrou antes, recomendo fortemente dar uma olhada no [índice remissivo](https://forcats.tidyverse.org/reference/index.html) para ver se existe uma função pronta que pode ajudar a resolver seu problema. Se você quiser aprender mais sobre fatores depois de ler este capítulo, recomendamos a leitura do artigo de Amelia McNamara e Nicholas Horton, [*Wrangling categorical data in R*](https://peerj.com/preprints/3163/). Este artigo apresenta um pouco da história discutida em [*stringsAsFactors: Uma biografia não autorizada*](https://simplystatistics.org/posts/2015-07-24-stringsasfactors-an-unauthorized-biography/) e [*stringsAsFactors = \*](https://notstatschat.tumblr.com/post/124987394001/stringsasfactors-sigh) e compara as abordagens organizadas (*tidy approach*) para dados categóricos descritos neste livro com métodos do R base.