Definindo objetos no R: = ou <-?

Aprenda as diferenças entre os operadores de atribuição em R e quando usar cada um. Entenda objetos, vetores, listas e data.frames.
R
data-science
conceitos-basicos
Published

July 15, 2025

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?

Criando objetos dentro de funções

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.

x = 1:5
y <- 1:5

all.equal(x, y)
[1] TRUE
rm(x, y)

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)
[1] 3
x
[1] 1 2 3 4 5
rm(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)
[1] 3
x
Error: object 'x' not found
rm(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))

Call:
lm(formula = mpg ~ wt, data = mtcars)

Residuals:
   Min     1Q Median     3Q    Max 
-4.543 -2.365 -0.125  1.410  6.873 

Coefficients:
            Estimate Std. Error t value             Pr(>|t|)    
(Intercept)   37.285      1.878   19.86 < 0.0000000000000002 ***
wt            -5.344      0.559   -9.56        0.00000000013 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 3.05 on 30 degrees of freedom
Multiple R-squared:  0.753, Adjusted R-squared:  0.745 
F-statistic: 91.4 on 1 and 30 DF,  p-value: 0.000000000129
# Verifica que é, de fato, um objeto lm
class(fit)
[1] "lm"
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))
Error in summary.lm(fit = lm(mpg ~ wt, data = mtcars)): argument "object" is missing, with no default
# É preciso reescrever o código em duas linhas
fit = lm(mpg ~ wt, data = mtcars)
summary(fit)

Call:
lm(formula = mpg ~ wt, data = mtcars)

Residuals:
   Min     1Q Median     3Q    Max 
-4.543 -2.365 -0.125  1.410  6.873 

Coefficients:
            Estimate Std. Error t value             Pr(>|t|)    
(Intercept)   37.285      1.878   19.86 < 0.0000000000000002 ***
wt            -5.344      0.559   -9.56        0.00000000013 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 3.05 on 30 degrees of freedom
Multiple R-squared:  0.753, Adjusted R-squared:  0.745 
F-statistic: 91.4 on 1 and 30 DF,  p-value: 0.000000000129
rm(fit)

Complicando as coisas

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?

fit <- lm(formula <- mpg ~ wt, data <- mtcars)

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.

fit <- lm(formula <- mpg ~ wt, data <- mtcars)
fit

Call:
lm(formula = formula <- mpg ~ wt, data = data <- mtcars)

Coefficients:
(Intercept)           wt  
      37.29        -5.34  
formula
mpg ~ wt
head(data)
                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
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
fit <- lm(a <- "mpg ~ wt", b <- mtcars)
fit

Call:
lm(formula = a <- "mpg ~ wt", data = b <- mtcars)

Coefficients:
(Intercept)           wt  
      37.29        -5.34  
print(a)
[1] "mpg ~ wt"
head(b)
                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
rm(fit, a, b)

O mesmo não é possível com =, por causa da duplicidade apontada acima.

fit = lm(a = "mpg ~ wt", b = mtcars)
Error in terms.formula(formula, data = data): argument is not a valid model

Há ainda mais alguns exemplos estranhos, resultados da ordem que o R processa os comandos. No R, é possível definir múltiplos objetos numa mesma linha da seguinte maneira

a <- b <- c <- 5
a = b = c = 5

Agora considere os exemplos abaixo. O segundo código não funciona. Isto acontece porque o <- tem “prioridade” e é lido primeiro.

(x = y <- 5)
[1] 5
(x <- y = 5)
Error in x <- y = 5: could not find function "<-<-"

Qual operador usar?

De maneira geral, usar <- é sempre melhor do que =.

É 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.

# Define x como -2
x <- -2
# Se x for menor que -1 (menos um) então "ótimo"
if (x < -1) "ótimo" else "errado"
[1] "ótimo"
# Verifica o valor de x
x
[1] -2
# Mesma linha com uma diferença sutil
if (x <-1 ) "ótimo" else "errado"
[1] "ótimo"
# Agora acabamos de mudar o valor de x (e não há aviso algum!)
x
[1] 1

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 similar.

Outros operadores

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.

É possível usar a flecha “ao contrário”, isto é, -> como em 5 -> a. Algumas pessoas acham que o operador -> é mais intuitivo quando usado com “pipes”.

AirPassengers |>
  log() |>
  window(start = c(1955, 1), end = c(1958, 12)) -> sub_air_passengers

sub_air_passengers
       Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct   Nov   Dec
1955 5.489 5.451 5.587 5.595 5.598 5.753 5.897 5.849 5.743 5.613 5.468 5.628
1956 5.649 5.624 5.759 5.746 5.762 5.924 6.023 6.004 5.872 5.724 5.602 5.724
1957 5.753 5.707 5.875 5.852 5.872 6.045 6.142 6.146 6.001 5.849 5.720 5.817
1958 5.829 5.762 5.892 5.852 5.894 6.075 6.196 6.225 6.001 5.883 5.737 5.820

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. Considere o exemplo abaixo.

library(magrittr)
x <- 1:5
x %<>% mean() %<>% sin()
print(x)
[1] 0.1411

Note que o código acima é equivalente ao abaixo.

x <- 1:5
x <- mean(x)
x <- sin(x)

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.

Apesar destas potenciais vantagens, não se recomenda o uso destes outros operadores. Por serem muito menos conhecidos, seu uso torna o código mais difícil de entender.

Conclusão

O operador <- é o mais correto

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 Positron, VSCode ou Sublime Text também é possível criar atalhos personalizados para o <-. Por fim, o <- fica bem bonito quando se usa uma fonte com ligaturas como o Fira Code.

O operador = tem seu valor por ser mais intutivo, especialmente para usuários que já tem algum conhecimento de programação. Em projetos que envolvam outros colaboradores, o = pode se provar o mais intuitivo.

É 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.

Diferenças de cultura

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. Curiosamente, livros de econometria (microeconometria, econometria espacial) costumam usar o <-. Livros como: Christoph Hanck, Martin Arnold, Alexander Gerber, and Martin Schmelzer * Hanck, Arnold, Gerber, Schmelzer. Introduction to Econometrics with R * Applied Econometrics with R * Arbia. Spatial Econometrics

Já livros de séries de tempo costumam usar o =. Exemplos como:

Pacotes e extensões

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)
x <- rnorm(n = 10, mean = 2)

Próximos passos na série “Fundamentos do R”

Agora que você entende como definir objetos em R, o próximo passo é aprender sobre pipes - uma ferramenta fundamental que vai transformar a forma como você escreve código R.

Outros recursos recomendados

Para aprofundar seus conhecimentos sobre objetos e atribuição em R: