Uma tarefa muito comum no R
é colar textos. As funções mais importantes para isso são paste()
e sprintf()
, que vêm com o pacote base
. Nesse artigo, vamos falar dessas duas funções e de um novo pacote do tidyverse
, o glue
.
paste()
A função paste()
recebe um conjunto indeterminado de objetos como argumento através do ...
e vai colando os objetos passados elemento a elemento. Isso significa que se você passar dois vetores de tamanho n
, a função paste()
retornará um vetor de tamanho n
sendo cada posição a colagem dos dois vetores nessa posição. Por padrão, a colagem é feita com um separador de espaço simples (" "
). Exemplo:
paste(c(1, 2, 3), c(4, 5, 6))
## [1] "1 4" "2 5" "3 6"
É possível alterar o separador pelo argumento sep =
. Um atalho útil para o separador vazio (""
) é a função paste0
:
paste0(c(1, 2, 3), c(4, 5, 6))
## [1] "14" "25" "36"
Algumas vezes nosso interesse não é juntar vetores elemento a elemento, mas sim passar um vetor e colar todos seus elementos. Isso é feito com o parâmetro collapse =
:
paste(c(1, 2, 3, 4, 5, 6), collapse = '@')
## [1] "1@2@3@4@5@6"
Se você passar mais de um vetor e mandar colapsar os elementos, o paste()
vai primeiro colar e depois colapsar:
paste(c(1, 2, 3), c(4, 5, 6), collapse = '@')
## [1] "1 4@2 5@3 6"
Cuidado
Tenha muito cuidado ao passar vetores com comprimentos diferentes no paste()
! Assim como muitas funções do R, o paste()
faz reciclagem, ou seja, ele copia os elementos do menor vetor até ele ficar com o comprimento do maior vetor1. O problema é que o paste()
faz isso silenciosamente e não avisa se você inserir um vetor com comprimento que não é múltiplo dos demais. Veja que resultado bizarro:
paste(5:9, 1:3, 4:5)
## [1] "5 1 4" "6 2 5" "7 3 4" "8 1 5" "9 2 4"
Por essas e outras que dizemos que às vezes o R funciona bem demais…
sprintf()
O sprintf()
é similar ao printf
do C
. Primeiro escrevemos um texto com %s
no lugar das coisas que queremos substituir. Depois colocamos esses objetos nos outros argumentos da função, na ordem em que eles aparecem no texto.
sprintf('Aba%ste', 'ca')
## [1] "Abacate"
Quando o argumento é um vetor, a função retorna um vetor com as substituições ponto a ponto.
sprintf('Aba%ste', c('ca', 'ixas'))
## [1] "Abacate" "Abaixaste"
Se o texto contém mais de um %s
e os objetos correspondentes são vetores, o sprintf()
tenta reciclar os vetores para ficarem do mesmo tamanho. Isso só funciona quando todos os objetos têm comprimentos que são múltiplos do comprimento do maior objeto.
Isso funciona:
sprintf('Aba%s%s', c('ca'), c('xi', 'te')) # ca foi reciclado
## [1] "Abacaxi" "Abacate"
Isso não funciona:
sprintf('Aba%s%s', c('ca', 'ixaste'), c('xi', 'te', '.'))
## Error in sprintf("Aba%s%s", c("ca", "ixaste"), c("xi", "te", ".")): arguments cannot be recycled to the same length
Nem sempre queremos substituir pedaços do nosso texto por outros textos. No lugar do %s
, é possível colocar padrões para números, por exemplo. Eu uso bastante o %d
, que recebe inteiros. Uma funcionalidade legal do %d
é a possibilidade de adicionar zeros à esquerda quando um número não atinge certa quantidade de dígitos. Assim, quando ordenamos um vetor de textos que começa com números, a ordenação é a mesma da versão numérica do vetor.
Exemplo:
nums <- 1:11
sort(as.character(nums)) # ordenado pela string: 10 vem antes de 2
## [1] "1" "10" "11" "2" "3" "4" "5" "6" "7" "8" "9"
sort(sprintf('%02d', nums)) # ordenado pela string: 02 vem antes de 10
## [1] "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11"
glue
O glue
é um pacote recente. Sua primeira aparição no GitHub foi em 23/12/2016! Isso significa que é provável que algumas coisas mudem, mas isso não nos impede de aproveitar o que a ferramenta tem de bom.
A função glue()
é uma generalização do sprintf()
que permite chamar objetos do R diretamente ao invés de utilizar o %s
. Os objetos podem estar no global environment ou descritos por meio de objetos nomeados nos argumentos do glue()
. Basta inserir os objetos entre chaves {}
:
library(glue)
planeta <- 'mundo'
glue('Olá {planeta} pela {y}a vez', y = 1)
## Olá mundo pela 1a vez
Tembém é possível adicionar expressões dentro das chaves:
p <- 1.123123123
glue('{p * 100}% das pessoas adoram R.')
## 112.3123123% das pessoas adoram R.
glue('{scales::percent(p)} das pessoas adoram R.')
## 112% das pessoas adoram R.
A função glue_collapse()
é parecida com o paste()
quando collapse = ''
, mas só aceita um objeto como entrada:
x <- glue_collapse(1:10)
x
## 12345678910
all.equal(x, paste(1:10, collapse = ''))
## [1] "Attributes: < Modes: list, NULL >"
## [2] "Attributes: < Lengths: 1, 0 >"
## [3] "Attributes: < names for target but not for current >"
## [4] "Attributes: < current is not list-like >"
## [5] "target is glue, current is character"
Se quiser colar os objetos elemento a elemento e depois colapsar, faça isso explicitamente em duas operações:
glue('{letters}/{LETTERS}') %>%
glue_collapse(', ')
## a/A, b/B, c/C, d/D, e/E, f/F, g/G, h/H, i/I, j/J, k/K, l/L, m/M, n/N, o/O, p/P, q/Q, r/R, s/S, t/T, u/U, v/V, w/W, x/X, y/Y, z/Z
O glue
também tem uma função extra para trabalhar melhor com o %>%
, o glue_data()
. O primeiro argumento dessa função é uma lista ou data.frame
, e seus nomes são utilizados como variáveis para alimentar as chaves das strings. Use o .
para fazer operações com toda a base de dados:
mtcars %>%
head() %>%
glue_data('O carro {row.names(.)} rende {mpg} milhas por galão.')
## O carro Mazda RX4 rende 21 milhas por galão.
## O carro Mazda RX4 Wag rende 21 milhas por galão.
## O carro Datsun 710 rende 22.8 milhas por galão.
## O carro Hornet 4 Drive rende 21.4 milhas por galão.
## O carro Hornet Sportabout rende 18.7 milhas por galão.
## O carro Valiant rende 18.1 milhas por galão.
Resumo
- Use
paste()
para colar ou colapsar elementos usando um separador fixado. - Use
sprintf()
quando quiser colocar objetos dentro de um texto complexo. - Em todos os casos existe uma solução usando
glue
.
Atualmente sempre que tenho um problema desse tipo uso o glue
. Até o momento, não encontrei nenhum problema ou dificuldade. A vida do cientista de dados é mais feliz no tidyverse
!
É isso. Happy coding ;)
Extra:
O Guilherme Jardim Duarte fez uma ótima sugestão logo após a publicação deste artigo. No pacote stringi
existe um operador %s+%
que cola textos iterativamente, com uma sintaxe similar à linguagem python
, que permite a colagem de textos usando um simples +
. Exemplo:
library(stringi)
'a' %s+%
'ba' %s+%
'ca' %s+%
'xi'
## [1] "abacaxi"
Você pode adicionar esse operador como um atalho no RStudio, análogo ao Ctrl+Shift+M
que usamos para escrever o %>%
. Para isso, veja esse tutorial sobre RStudio Addins.