1.7 Dicas de erros comuns de sintaxe da linguagem R

Abrir e esquecer de fechar parênteses, colchetes e aspas é um dos erros mais comuns no início da caminhada rumo ao aprendizado de qualquer linguagem de programação, e é necessário muita paciência1 por parte do usuário para aprender a lidar com a frustração dos sucessivos erros que aparecem no caminho. Mesmo usuários mais experientes têm que aprender a lidar com os erros, que são comuns, o que levou o programador Patrick Burns a publicar um curioso livro sob o título The R Inferno (Burns 2012). Uma ferramenta útil para evitar tais problemas é o uso de uma IDE como o RStudio. Há opções na configuração que autorizam o software a checar seu código e informá-lo de possíveis erros, além de proverem ao usuário uma ferramenta de limpeza do código. Sugerimos fortemente a instalação de uma IDE para usar o R. Segue então uma brevíssima lista de erros super comuns que iniciantes enfrentam no aprendizado da linguagem R.

1.7.1 Parênteses

Lembre-se sempre de, ao abrir um parêntese, fechá-lo. Caso contrário, a ação não se completará e seu console ficará travado com um sinal de + aguardando que seu código seja completo. Neste caso, ficar completo significa fechar o parêntese. Tente executar o código abaixo e verifique que o sinal de + ficará estagnado na tela de seu console. Para sair desta tela, clique no console e depois tecle Esc. Você verá que o sinal de > voltará a aparecer no console.

erro <- c(1, 2, 3

Agora, execute o comando abaixo. Ele será executado perfeitamente.

erro <- c(1, 2, 3)

1.7.2 Vírgulas

Ao concatenar elementos ou ao adicionar valores em argumentos de funções, é necessário lembrar sempre de colocar as vírgulas em seus devidos lugares. Caso contrário … mais um erro! Tentem executar o código abaixo:

numeros <- c(1, 5, 6 7, 8)
## Error: <text>:1:22: unexpected numeric constant
## 1: numeros <- c(1, 5, 6 7
##                          ^

O R dará o aviso Error: unexpected numeric constant in "numeros <- c(1,5,6 7" e encerrá a operação. Executem agora o comando abaixo. Será executado sem problemas.

numeros <- c(1, 5, 6, 7, 8)

1.7.3 Aspas

Textos são especificados dentro de aspas duplas "" ou aspas simples ''; tanto faz qual você usa, o importante é sempre que abrir aspas, fechar as aspas com o mesmo tipo. Se seu texto tem acentos, use aspas duplas para delimitá-lo. Aspas simples podem entrar num texto definido por aspas duplas, e vice-versa. Rode isso no seu console:

objum <- "um texto com 'aspas simples'"
objum
## [1] "um texto com 'aspas simples'"
obj2 <- 'um texto com "aspas simples"'
obj2
## [1] "um texto com \"aspas simples\""
# note que foi adicionada uma barra invertida, porque \" na sintaxe do Rsignifica aspas para confundir com as aspas que você usa para indicar textos.

1.7.4 Números

Números são sempre especificados sem aspas; se você colocar qualquer número entre aspas, ele será interpretado como texto:

obj1 <- 18
obj1 + 1 # essa fórmula irá funcionar porque obj1 é um número
## [1] 19
obj2 <- "18"

Isso não vai funcionar porque obj2 não é um número.

obj2 + 1
## Error in obj2 + 1: non-numeric argument to binary operator

Caso você insista em rodar, receberá a seguinte mensagem: Error in obj2 + 1 : non-numeric argument to binary operator.

1.7.5 Nomes de objetos

Nomes de objetos não podem ter espaço em branco e aspas são ignoradas:

obj 1 = "meutexto" #nao vai funcionar
## Error: <text>:1:5: unexpected numeric constant
## 1: obj 1
##         ^
obj1 <- "meutexto" # vai funcionar
obj1
## [1] "meutexto"
"obj1" <- "meu texto" # vai criar objeto obj1, ignorando as aspas
obj1
## [1] "meu texto"
obj"1" = "meu texto" #nao vai funcionar
## Error: <text>:1:4: unexpected string constant
## 1: obj"1"
##        ^

1.7.6 Atribuição de objetos

A atribuição de valores aos objetos pode ser feita com dois operadores equivalentes, = ou <- (este pode ser utilizado no sentido inverso também ->):

obj1 <- "meu texto"

# ou pode escrever assim
obj1 <- "meu texto" # atribui

# ou assim
"meu texto" -> obj1 # atribui
obj1
## [1] "meu texto"

1.7.7 Busque entender as partes

Se você tiver dificuldade no entendimento de um script do R que está tentando rodar, separe os termos das linhas e expressões para entender o que cada parte está fazendo. Também pode alterar os valores dos argumentos para entender o funcionamento de uma função, ou simplesmente digitar a função sem os parênteses para ver o script que ela contem. Por exemplo, vejamos a expressão abaixo, que faz uso de um conjunto de dados chamado iris, que vem disponível com o R.

obj1 <- paste(levels(iris$Species), tapply(iris$Sepal.Length, INDEX = iris$Species, mean, na.rm = TRUE), sep = " sépala média = ")
obj1
## [1] "setosa sépala média = 5.006"     "versicolor sépala média = 5.936"
## [3] "virginica sépala média = 6.588"

À primeira vista, a expressão parece complicada. Vamos separar as partes e entender pedaço por pedaço. Primeiro, vamos tentar entender quem é iris. Vamos checar a estrutura de iris:

str(iris) #veja a estrutura
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

Agora, vamos obter um sumário estatístico de iris:

summary(iris) #veja o que é iris
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 NA
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 NA
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500 NA

Note que iris$Species contem 50 valores para três nomes e portanto eles estao sendo intepretados como categoria, isto é, é um fator. Vamos ver os níveis desse fator com a expressão abaixo, contida na função executada anteriormente e que desejamos destrinchar:

levels(iris$Species) #categorias especies do objeto factor iris$Species
## [1] "setosa"     "versicolor" "virginica"

Partamos para o segundo elemento dessa função, que é a expressão colocada abaixo:

#o segundo elemento é o resultado de:
tapply(iris$Sepal.Length, INDEX = iris$Species, mean, na.rm = TRUE)
##     setosa versicolor  virginica 
##      5.006      5.936      6.588

Essa expressão basicamente calcula a média dos 50 comprimentos de sépala para cada espécie. Por fim, temos o último elemento da expressão que é o argumento sep pertencente à função paste(). Este argumento é responsável por informar à função paste() qual separador nós vamos utilizar para separar os elementos contidos antes do argumento sep, isto é, separar o resultado de levels(iris$Species) da expressão tapply(iris$Sepal.Length,INDEX=iris$Species,mean,na.rm=TRUE). Nesse caso, vamos separar esses dois elementos com o texto sépala média =.

paste(, sep ='sépala média = ')

Vamos agora ver o resultado da função que executamos lá em cima.

#veja o resultado
obj1
## [1] "setosa sépala média = 5.006"     "versicolor sépala média = 5.936"
## [3] "virginica sépala média = 6.588"

Lembre-se sempre de checar também o código de qualquer função, para poder entender como ela funciona. Vejamos os casos das funções paste() e tapply(), utilizadas neste exercício. Para checar o código de uma função, geralmente basta executa o nome da função sem parênteses. Vejamos:

tapply
## function (X, INDEX, FUN = NULL, ..., default = NA, simplify = TRUE) 
## {
##     FUN <- if (!is.null(FUN)) 
##         match.fun(FUN)
##     if (inherits(INDEX, "formula")) {
##         if (is.data.frame(X)) 
##             INDEX <- .formula2varlist(INDEX, X)
##         else stop("'X' must be a data frame when 'INDEX' is a formula")
##     }
##     if (!is.list(INDEX)) 
##         INDEX <- list(INDEX)
##     INDEX <- lapply(INDEX, as.factor)
##     nI <- length(INDEX)
##     if (!nI) 
##         stop("'INDEX' is of length zero")
##     if (!is.object(X) && !all(lengths(INDEX) == length(X))) 
##         stop("arguments must have same length")
##     namelist <- lapply(INDEX, levels)
##     extent <- lengths(namelist, use.names = FALSE)
##     cumextent <- cumprod(extent)
##     if (cumextent[nI] > .Machine$integer.max) 
##         stop("total number of levels >= 2^31")
##     storage.mode(cumextent) <- "integer"
##     ngroup <- cumextent[nI]
##     group <- as.integer(INDEX[[1L]])
##     if (nI > 1L) 
##         for (i in 2L:nI) group <- group + cumextent[i - 1L] * 
##             (as.integer(INDEX[[i]]) - 1L)
##     if (is.null(FUN)) 
##         return(group)
##     levels(group) <- as.character(seq_len(ngroup))
##     class(group) <- "factor"
##     ans <- split(X, group)
##     names(ans) <- NULL
##     index <- as.logical(lengths(ans))
##     ans <- lapply(X = ans[index], FUN = FUN, ...)
##     ansmat <- array(if (simplify && all(lengths(ans) == 1L)) {
##         ans <- unlist(ans, recursive = FALSE, use.names = FALSE)
##         if (!is.null(ans) && is.na(default) && is.atomic(ans)) 
##             vector(typeof(ans))
##         else default
##     }
##     else vector("list", prod(extent)), dim = extent, dimnames = namelist)
##     if (length(ans)) {
##         ansmat[index] <- ans
##     }
##     ansmat
## }
## <bytecode: 0x5587b70e7670>
## <environment: namespace:base>
paste
## function (..., sep = " ", collapse = NULL, recycle0 = FALSE) 
## .Internal(paste(list(...), sep, collapse, recycle0))
## <bytecode: 0x5587a4ef5730>
## <environment: namespace:base>

Referências

Burns, Patrick. 2012. The R Inferno. Lulu.com. https://www.burns-stat.com/documents/books/the-r-inferno/.

  1. Lembrem-se sempre de que vocês não são os únicos a ter de lidar com a frustração durante o aprendizado de uma linguagem de programação. É importante estarem conscientes dessa verdade. Vejam esta postagem: https://www.codingame.com/blog/dealing-with-programming-frustration-the-right-way/↩︎