Curso
@drsimonj aqui com cinco truques simples que eu vivo compartilhando com quem usa R para melhorar o código!
1. Mais seguro sequenciar a partir de 1
Da próxima vez que você usar o operador de dois pontos para criar uma sequência a partir de 1, como 1:n, experimente seq().
# Sequenciar um vetor
x <- runif(10)
seq(x)
#> [1] 1 2 3 4 5 6 7 8 9 10
# Sequenciar um inteiro
seq(nrow(mtcars))
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#> [24] 24 25 26 27 28 29 30 31 32
O operador de dois pontos pode gerar resultados inesperados e causar diversos problemas sem você perceber! Veja o que acontece quando você quer sequenciar o comprimento de um vetor vazio:
# Vetor vazio
x <- c()
1:length(x)
#> [1] 1 0
seq(x)
#> integer(0)
Você também vai notar que isso evita o uso de funções como length(). Quando aplicado a um objeto com certo comprimento, seq() cria automaticamente uma sequência de 1 até o tamanho do objeto.
2. vector() no que você c()
Da próxima vez que for criar um vetor vazio com c(), tente substituir por vector("type", length).
# Um vetor numérico com 5 elementos
vector("numeric", 5)
#> [1] 0 0 0 0 0
# Um vetor de caracteres com 3 elementos
vector("character", 3)
#> [1] "" "" ""
Fazendo isso, você melhora o uso de memória e ganha velocidade! Muitas vezes você já sabe de antemão qual tipo de valor vai para o vetor e qual será o seu tamanho. Usar c() faz com que o R precise descobrir essas duas coisas de forma lenta. Dê um gás com vector()!
Um bom exemplo é dentro de um for. Muita gente escreve loops declarando um vetor vazio e o "crescendo" com c() assim:
x <- c()
for (i in seq(5)) {
x <- c(x, i)
}
#> x at step 1 : 1
#> x at step 2 : 1, 2
#> x at step 3 : 1, 2, 3
#> x at step 4 : 1, 2, 3, 4
#> x at step 5 : 1, 2, 3, 4, 5
No lugar disso, pré-defina o tipo e o tamanho com vector() e preencha por índice:
n <- 5
x <- vector("integer", n)
for (i in seq(n)) {
x[i] <- i
}
#> x at step 1 : 1, 0, 0, 0, 0
#> x at step 2 : 1, 2, 0, 0, 0
#> x at step 3 : 1, 2, 3, 0, 0
#> x at step 4 : 1, 2, 3, 4, 0
#> x at step 5 : 1, 2, 3, 4, 5
Olha uma comparação rápida de desempenho:
n <- 1e5
x_empty <- c()
system.time(for(i in seq(n)) x_empty <- c(x_empty, i))
#> user system elapsed
#> 15.238 2.327 17.650
x_zeros <- vector("integer", n)
system.time(for(i in seq(n)) x_zeros[i] <- i)
#> user system elapsed
#> 0.007 0.000 0.007
Convincente, né?
Domine suas habilidades em dados com o DataCamp
Mais de 10 milhões de pessoas aprendem Python, R, SQL e outras habilidades tecnológicas usando nossos cursos práticos elaborados por especialistas do setor.

3. Aposente o which()
Da próxima vez que for usar which(), tente evitar! Muita gente usa which() para obter índices a partir de uma condição booleana e depois seleciona valores nesses índices. Não precisa.
Pegando elementos do vetor maiores que 5:
x <- 3:7
# Usando which (desnecessário)
x[which(x > 5)]
#> [1] 6 7
# Sem which
x[x > 5]
#> [1] 6 7
Ou contando quantos valores são maiores que 5:
# Usando which
length(which(x > 5))
#> [1] 2
# Sem which
sum(x > 5)
#> [1] 2
Por que deixar de lado o which()? Ele geralmente é desnecessário, e vetores booleanos já resolvem.
Por exemplo, o R permite selecionar elementos marcados como TRUE em um vetor booleano:
condition <- x > 5
condition
#> [1] FALSE FALSE FALSE TRUE TRUE
x[condition]
#> [1] 6 7
Além disso, combinados com sum() ou mean(), vetores booleanos servem para obter a contagem ou a proporção de valores que atendem a uma condição:
sum(condition)
#> [1] 2
mean(condition)
#> [1] 0.4
which() informa os índices dos valores TRUE:
which(condition)
#> [1] 4 5
E embora o resultado não esteja errado, não é necessário. Por exemplo, vejo com frequência gente combinando which() e length() para testar se algum ou todos os valores são TRUE. Em vez disso, use any() ou all():
x <- c(1, 2, 12)
# Usando `which()` e `length()` para testar se existe algum valor > 10
if (length(which(x > 10)) > 0)
print("At least one value is greater than 10")
#> [1] "At least one value is greater than 10"
# Envolvendo o vetor booleano com `any()`
if (any(x > 10))
print("At least one value is greater than 10")
#> [1] "At least one value is greater than 10"
# Usando `which()` e `length()` para testar se todos os valores são positivos
if (length(which(x > 0)) == length(x))
print("All values are positive")
#> [1] "All values are positive"
# Envolvendo o vetor booleano com `all()`
if (all(x > 0))
print("All values are positive")
#> [1] "All values are positive"
Ah, e ainda economiza um tempinho...
x <- runif(1e8)
system.time(x[which(x > .5)])
#> user system elapsed
#> 1.156 0.522 1.686
system.time(x[x > .5])
#> user system elapsed
#> 1.071 0.442 1.662
4. Dê um factor nesse factor!
Já removeu valores de um factor e ficou preso com níveis antigos que não existem mais? Vejo de tudo quanto é gambiarra para lidar com isso. A solução mais simples muitas vezes é só envolver de novo com factor().
Este exemplo cria um factor com quatro níveis ("a", "b", "c" e "d"):
# Um factor com quatro níveis
x <- factor(c("a", "b", "c", "d"))
x
#> [1] a b c d
#> Levels: a b c d
plot(x)

Se você remove todos os casos de um nível ("d"), o nível continua registrado no factor:
# Remover todos os valores de um nível
x <- x[x != "d"]
# Mas o nível ainda está aqui!
x
#> [1] a b c
#> Levels: a b c d
plot(x)

Um jeito super simples de remover é usar factor() de novo:
x <- factor(x)
x
#> [1] a b c
#> Levels: a b c
plot(x)

Geralmente é uma boa solução para um problema que irrita muita gente. Poupe dor de cabeça e dê um factor nesse factor!
5. Primeiro vem o $, depois vem a performance
Da próxima vez que quiser extrair valores de uma coluna de data.frame em que as linhas atendem a uma condição, especifique a coluna com $ antes das linhas com [.
Digamos que você quer a potência (hp) dos carros com 4 cilindros (cyl) no conjunto mtcars. Você pode escrever de qualquer um destes jeitos:
# linhas primeiro, coluna depois — não é o ideal
mtcars[mtcars$cyl == 4, ]$hp
#> [1] 93 62 95 66 52 65 97 66 91 113 109
# coluna primeiro, linhas depois — bem melhor
mtcars$hp[mtcars$cyl == 4]
#> [1] 93 62 95 66 52 65 97 66 91 113 109
A dica é usar a segunda abordagem.
Mas por quê?
Primeiro motivo: se livrar daquela vírgula chata! Quando você especifica as linhas antes da coluna, precisa lembrar da vírgula: mtcars[mtcars$cyl == 4,]$hp. Ao indicar a coluna primeiro, você passa a referenciar um vetor — e não precisa da vírgula!
Segundo motivo: velocidade! Vamos testar em um data frame maior:
# Simulando um data frame...
n <- 1e7
d <- data.frame(
a = seq(n),
b = runif(n)
)
# linhas primeiro, coluna depois — não é o ideal
system.time(d[d$b > .5, ]$a)
#> user system elapsed
#> 0.497 0.126 0.629
# coluna primeiro, linhas depois — bem melhor
system.time(d$a[d$b > .5])
#> user system elapsed
#> 0.089 0.017 0.107
Vale a pena, né?
Ainda assim, se você quiser lapidar suas habilidades de ninja de data frame em R, recomendo aprender dplyr. Você pode ver um bom overview no site do dplyr ou aprender de verdade com cursos online como o Data Manipulation in R with dplyr da DataCamp.
Encerramento
Obrigado por ler e espero que isso tenha sido útil para você.
Para novidades dos posts recentes, siga @drsimonj no Twitter ou me envie um e-mail em drsimonjackson@gmail.com.
Se quiser o código que gerou este post, confira o repositório blogR no GitHub.
Dê uma olhada no nosso Guia de introdução ao Tidyverse: tutorial.

