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.
Agora, execute o comando abaixo. Ele será executado perfeitamente.
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:
## Error in parse(text = input): <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.
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:
## [1] "um texto com 'aspas simples'"
## [1] "um texto com \"aspas simples\""
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:
## [1] 19
Isso não vai funcionar porque obj2
não é um número.
## 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:
## Error in parse(text = input): <text>:1:5: unexpected numeric constant
## 1: obj 1
## ^
## [1] "meutexto"
## [1] "meu texto"
## Error in parse(text = input): <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
:
## '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
:
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:
## [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 =
.
Vamos agora ver o resultado da função que executamos lá em cima.
## [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:
## 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.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: 0x55ae82f03f18>
## <environment: namespace:base>
## function (..., sep = " ", collapse = NULL, recycle0 = FALSE)
## .Internal(paste(list(...), sep, collapse, recycle0))
## <bytecode: 0x55ae717bbc98>
## <environment: namespace:base>
Referências
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/↩︎