= 1:5
x <- 1:5
y
all.equal(x, y)
rm(x, y)
Definindo objetos no R
Há dois operadores para definir um objeto no R: =
e <-
. A maior parte dos usuários parece preferir o último apesar dele parecer um tanto inconveniente. Em teclados antigos, havia uma tecla específica com o símbolo <-
, mas em teclados ABNT modernos ele exige três teclas para ser escrito.
Para contornar este incômodo é comum criar um atalho no teclado para esse símbolo; o RStudio, por exemplo, tem um atalho usando a teclas Alt
e -
em conjunto. Mas ainda assim fica a questão: por que não utilizar o =
? A resposta curta é que o símbolo <-
é a melhor e mais consistente forma de definir objetos R. Na prática, contudo, há poucas diferenças entre as expressões e elas dificilmente vão fazer alguma diferença. Podemos começar com um exemplo bastante simples para entender estas diferenças.
Qual a diferença?
O código abaixo cria duas variáveis, x
e y
, cujos valores são a sequência \(1, 2, 3, 4, 5\). Até aí tudo igual. A função all.equal
certifica que os objetos são iguais e a função rm
“deleta” os objetos. Esta última vai ser conveniente para manter os exemplos organizados.
Agora considere o código abaixo. A função median
está sendo aplicada em x <- 1:5
. O que acontece desta vez? O resultado é que é criada uma variável x
com valor 1 2 3 4 5
e também é impresso a mediana deste vetor, i.e., 3
.
median(x <- 1:5)
xrm(x)
Poderíamos fazer o mesmo usando =
, certo? Errado. Aí está uma das primeiras diferenças entre estes operadores. O código abaixo calcula a mediana do vetor, mas não cria um objeto chamado x
com valor 1 2 3 4 5
. Por quê? O problema é que o operador =
tem duas finalidades distintas. Ele serve tanto para definir novos objetos, como em x = 2
, como também para definir o valor dos argumentos de uma função, como em rnorm(n = 10, mean = 5, sd = 1)
. Coincidentemente, o nome do primeiro argumento da função median
é x. Logo, o código abaixo é interpretado como: tire a mediana do vetor 1 2 3 4 5
. O mesmo acontece com outras funções (ex: mean
, var
, etc.)
median(x = 1:5)
xrm(x)
Outro exemplo em que há divergência entre os operadores é com o comando lm
. Usando <-
podemos escrever, numa única linha, um comando que define um objeto lm
(resultado de uma regressão) ao mesmo tempo em que pedimos ao R para imprimir os resultados desta regressão. O código abaixo faz justamente isto.
# Imprime os resultados da regressão e salva as info num objeto chamado 'fit'
summary(fit <- lm(mpg ~ wt, data = mtcars))
# Verifica que é, de fato, um objeto lm
class(fit)
rm(fit)
Note que isto não é possível com o operador =
. Isto acontece, novamente, porque o =
é interpretado de maneira diferente quando aparece dentro de uma função. É necessário quebrar o código em duas linhas.
# Este exemplo não funciona
summary(fit = lm(mpg ~ wt, data = mtcars))
# É preciso reescrever o código em duas linhas
= lm(mpg ~ wt, data = mtcars)
fit summary(fit)
rm(fit)
Há também algumas pequenas divergências pontuais. Os primeiros dois argumentos da função lm
são formula
e data
. Considere o código abaixo. Sem executar o código qual deve ser o resultado?
<- lm(formula <- mpg ~ wt, data <- mtcars) fit
Estamos aplicando a função lm
em dois argumentos. O primeiro deles se chama formula
e é definido como mpg ~ wt
, o segundo é chamado data
e é definido como os valores no data.frame mtcars
. Ou seja, o resultado deve ser o mesmo do exemplo acima com median(x <- 1:5)
. A função é aplicada sobre os argumentos e os objetos formula
e data
são criados.
<- lm(formula <- mpg ~ wt, data <- mtcars)
fit
fit
formula
head(data)
rm(fit, formula, data)
Note que usei os nomes dos argumentos apenas para exemplificar o caso. Pode-se colocar um nome diferente, pois não estamos “chamando” o argumento e sim especificando qual valor/objeto a função deve utilizar.
# Exemplo utilizando nomes diferentes
<- lm(a <- "mpg ~ wt", b <- mtcars)
fit
fitprint(a)
head(b)
rm(list = ls())
O mesmo não é possível com =
, por causa da duplicidade apontada acima.
= lm(a = "mpg ~ wt", b = mtcars) fit
Há ainda mais alguns exemplos estranhos, resultados da ordem que o R processa os comandos. O segundo código abaixo, por exemplo, não funciona. Isto acontece porque o <-
tem “prioridade” e é lido primeiro.
x = y <- 5)
(<- y = 5) (x
É difícil encontrar desvatagens em usar o <-
(além da dificuldade de escrevê-lo num teclado normal). Mas há pelo menos um caso em que ele pode levar a problemas. O código abaixo mostra como este operador pode ser sensível a espaços em branco. No caso, define-se o valor de x
como -2
. O primeiro teste verifica se o valor de x
é menor que \(-1\). Logo, espera-se que o código imprima "ótimo"
pois -2
é menor que -1
. Já o segundo teste faz quase o mesmo. A única diferença é um espaço em branco, mas agora ao invés de um teste, a linha de código define o valor de x como 1
e imprime "ótimo"
, pois o valor do teste (por padrão) é TRUE
.
Assim como muitos dos exemplos acima, é difícil imaginar que isto possa ser um problema real. Eventualmente, podemos apagar espaços em branco usando o ‘localizar e substituir’ e isto talvez leve a um erro como o abaixo.
# Define x como -2
<- -2
x # Se x for menor que -1 (menos um) então "ótimo"
if (x < -1) "ótimo" else "errado"
# Verifica o valor de x
x# Mesma linha com uma diferença sutil
if (x <-1 ) "ótimo" else "errado"
# Agora acabamos de mudar o valor de x (e não há aviso algum!)
x
Mais um adendo
Eu citei apenas dois operadores: =
e <-
; mas na verdade há ainda outros: <<-
, ->
e ->>
(veja help("assignOps")
). Os operadores com “flecha dupla” são comumente utilizadas dentro de funções para usos específicos. Algumas pessoas acham que o operador ->
é mais intuitivo quando usado com “pipes”. Pessoalmente, acho este tipo de código abominável.
|>
AirPassengers log() |>
window(start = c(1955, 1), end = c(1958, 12)) -> sub_air_passengers
sub_air_passengers
Além dos operadores base, o popular pacote magrittr
também possui um novo tipo de operador. Muitos conhecem o %>%
que ajuda a formar “pipes” e carregar objetos progressivamente em funções distintas. Menos conhecido, mas igualmente poderoso, o %<>%
faz algo parecido.
library(magrittr)
<- 1:5
x print(x)
%<>% mean() %<>% sin()
x print(x)
Note que o código acima é equivalente ao abaixo. A vantagem do operador %<>%
é de evitar a repetição do x <- x ...
o que pode ser conveniente quando o nome do objeto é longo. Da mesma forma, também pode ser aplicado para transformar elementos em uma lista ou colunas num data.frame
.
<- 1:5
x <- x %>% mean() %>% sin() x
Não recomendo o uso nem do ->
e nem do %<>%
.
Resumo
No geral, o operador <-
é a forma mais “segura” de se definir objetos. De fato, atualmente, este operador é considerado o mais apropriado. O livro Advanced R, do influente autor Hadley Wickham, por exemplo, recomenda que se use o operador <-
exclusivamente para definir objetos.
A inconveniência de escrevê-lo num teclado moderno é contornada, como comentado acima, por atalhos como o Alt
+ -
no RStudio. Em editores de texto como o VSCode, Sublime Text ou Notepad++ também é possível criar atalhos personalizados para o <-
. Pessoalmente, eu já me acostumei a digitar <-
manualmente e não vejo grande problema nisso. Acho que o =
tem seu valor por ser mais intutivo, especialmente para usuários que já tem algum conhecimento de programação, mas acabo usando mais o <-
. Por fim, o <-
fica bem bonito quando se usa uma fonte com ligaturas como o Fira Code.
É importante frisar que o <-
continua sendo o operador de predileção da comunidade dos usuários de R e novas funções/pacotes devem ser escritas com este sinal. Por sorte há opções bastante simples que trocam os =
para <-
corretamente como o formatR
apresentado abaixo. Veja também o addin do pacote styler
. Ou seja, é possível escrever seu código usando =
e trocá-los por <-
de maneira automática se for necessário.
library(formatR)
tidy_source(text = "x = rnorm(n = 10, mean = 2)", arrow = TRUE)
O ponto central deste post, na verdade, é mostrar como os operadores <-
e =
são muito similares na prática e que a escolha entre um ou outro acaba caindo numa questão subjetiva. Há quem acredite ser mais cômodo usar o =
não só porque ele é mais fácil de escrever, mas também porque ele é mais próximo de universal. Várias linguagens de programação comuns para a/o economista (Matlab, Python, Stata, etc.) usam o sinal de igualdade para definir objetos e parece estranho ter que usar o <-
somente para o R.
Em livros de econometria ambos os operadores são utilizados. Os livros Introduction to Econometrics with R, Applied Econometrics with R, Arbia. Spatial Econometrics, entre outros, usam o <-
. Já os livros Tsay. Financial Time Series e Shumway & Stoffer. Time Series Analysis usam =
.
No fim, use o operador que melhor