Artigo escrito com a colaboração de Larisse Annie Saldanha
O bang bang operator (!!) no R é um operador de quase-citação (quasi-quotation) dentro do tidy evaluation, permitindo avaliar seletivamente partes de uma expressão citada. Isso é especialmente útil ao escrever funções que combinam código fixo com argumentos dinâmicos, garantindo flexibilidade na manipulação de dados em pacotes como o dplyr. Neste artigo, exploramos como esse operador funciona, sua relação com os conceitos de citação (quote(), expr()) e não citação (!!, !!!), e como utilizá-lo para tornar seu código mais eficiente.
No tidy evaluation, todas as funções de citação são, na verdade, funções de quase-citação, porque também oferecem suporte à citação. Onde a quotation é o ato de capturar uma expressão não avaliada, unquotation é a capacidade de avaliar seletivamente partes de uma expressão citada.
Juntos, isso é chamado quase-quotação. A quase-quotação facilita a criação de funções que combinam o código escrito pelo autor da função com o código escrito pelo usuário da função. Isso ajuda a resolver uma grande variedade de problemas desafiadores.
Um argumento citado é capturado pela função e é processado de algum jeito personalizado. Se você não tiver certeza se um argumento é citado ou avaliado, tente executar o código fora da função. Se não funcionar ou fizer algo diferente, esse argumento precisa ser citado.
Unquoting
Unquoting é a habilidade de avaliar seletivamente partes de uma expressão citada de outra forma. Use bang bang `!!` para citar um único argumento numa dada função. Por exemplo:
```{r}
x <- expr(-1)
expr(f(!!x,y)) ```
<!--  -->
A função `quote()` é equivalente à função `expr()`. Ela simplesmente retorna o argumento. Veja:
```{r echo=TRUE}
quote(x+y) ```
Assim como chamamos objetos, `!!` também funciona para símbolos e constantes:
```{r echo=TRUE}
a <- sym("y")
b <- 1
expr(f(!!a, !!b)) ```
Formalmente, `quo()` e `expr()` são funções de quase-citação, bang bang `!!` é um operador de citação, e `!!!` é um operador de divisão de citação. Estes termos tem um rico histórico em linguagens Lisp, e em linguagens modernas como Julia e Racket.
O operador `!!!` toma uma lista de expressões e insere ela no local do operador. Por exemplo:
```{r echo=TRUE}
xs <- exprs(1,a,-b)
expr(f(!!!xs,y)) ```
```{r echo=TRUE}
ys <- set_names(xs, c("a","b","c"))
expr(f(!!!ys,d=4)) ```
Exemplo 1
Os exemplos que serão mostrados, serão do banco de dados `iris` do R.
```{r echo=TRUE}
head(iris)
```
Suponha que todas as plantas deste banco de dados pertencem à novas espécias, **pallida** e queremos atualizar os valores da coluna que correspondem às espécies. Usamos então `mutate()`:
```{r echo=TRUE}
mutate(iris, Species = "Pallida") %>% head() ```
Por definição, o `dplyr` cita o nome e avalia o valor. Por isso, podemos facilmente trocar o lado do valor do par por um objeto e, novamente, ele é avaliado e obtemos um resultado idêntico.
```{r echo=TRUE}
targetValue = "Pallida"
mutate(iris, Species = targetValue) %>% head() ```
Quando tentamos especificar o valor por um nome, não funciona. Ele somente cria uma coluna.
```{r echo=TRUE}
targetColumn = "Species"
mutate(iris, targetColumn = "Pallida") %>% head() ```
Uso do Operador bang bang `!!`
Como dito anteriormente, o propósito deste operador é citar o argumento. Então, se antes citarmos o nome e avaliar o valor, a citação do nome resolveria nosso problema? Infelizmente não completamente. Mas com o uso do operador `:= (definição)` resolvemos completamente o problema.
```{r echo=TRUE}
targetColumn = "Species"
mutate(iris, !!targetColumn := "Pallida") %>% head() ```
E mais:
```{r echo=TRUE}
targetColumn = "Species"
mutate(iris, !!targetColumn := targetValue) %>% head() ```
Uso em Outras Funções do `dplyr`
```{r echo=TRUE}
a <-"Species"
filter(iris, !!a == "versicolor") %>% head() ```
Não funciona, já que `a` é um símbolo e queremos que ele seja avaliado em `Species`. Para funcionar, criamos um símbolo. Precisamos transformar o caracter string `”Species”` em símbolo.
```{r echo=TRUE}
a <-"Species"
filter(iris, !!rlang :: sym(a) == "versicolor") %>% head() ```
Exemplo 2
Banco de Dados `Starwars` do R, onde:
- Mass: Peso (kg)
- Height: Altura (cm)
- BMI: Body Mass Index
```{r echo= TRUE}
head(starwars)
starwars <- mutate(starwars, height = height / 100)
transmute(starwars, bmi = mass / height^2) ```
```{r eval=FALSE, include=FALSE}
x <- "height"
transmute(starwars, bmi = mass / (!!x)^2) ```
```{r echo=TRUE}
x <- sym("height")
transmute(starwars, bmi = mass / (!!x)^2) ```
```{r echo=TRUE}
starwars %>%
group_by(species) %>%
summarise(avg = mean(height)) ```
```{r}
mean_by <- function(data, var, group) {
data %>%
group_by(group) %>%
summarise(avg = mean(var))
}
# mean_by(starwars, "species", "height")
#> Erro: Column `group` is unknown
```
Logo, os argumentos `group` e `var` precisam ser citados:
```{r} mean_by <- function(data, var, group) {
var <- sym(var)
group <- sym(group)
data %>%
group_by(!! group) %>%
summarise(avg = mean(!! var))
}
mean_by(starwars,"height","species") ```
Uso Típico do operador
`calcula_percentual_linha <- function(data, linha, coluna, indicador){` \
`var1 <- sym(linha)` \
`var2 <- sym(coluna)` \
`indicador <- sym(indicador)` \
\
`tab <- data %>% ` \
`group_by(!!var1,!!var2) %>%` \
`summarise(Total=sum(!!indicador,na.rm = T)) %>%` \
`mutate(` \
`%` `= (Total/sum(Total))` \
`) %>% ` \
`dplyr::select(-Total) %>%` \
`arrange(!!var2,!!var1) %>%` \
`pivot_wider(names_from = DescLinha, values_from = `%`, values_fill = list(`%` = 0))` \
\
`tab` \
\
`}`
Curtiu nosso conteúdo? Você encontra muito mais no canal do Statplace no YouTube.
Siga-nos nas redes sociais! Estamos no Facebook, Instagram e LinkedIn.

