3.11 Mais sobre funções

Funções são tão comuns (provavelmente você já usou funções no Excel), que mesmo sem termos abordado o tema com detalhes, nós conseguimos utilizar várias funções nas seções anteriores:

  • a função c() foi utilizada para criar vetores;
  • a função class() foi utilizada para descobrir a classe de um objeto;
  • a família de funções is.na(), is.nan(), is.infinite() e is.null foram utilizadas para testar se um valor é NA, NaN, infinito ou NULL, respectivamente.

Diferentemente dos objetos, as funções podem receber argumentos. Argumentos são os valores que colocamos dentro dos parênteses e que as funções precisam para funcionar (calcular algum resultado). Por exemplo, a função c() precisa saber quais são os valores que formarão o vetor que ela irá criar.

c(1, 3, 5)
## [1] 1 3 5

Nesse caso, os valores 1, 3 e 5 são os argumentos da função c(). Os argumentos de uma função são sempre separados por vírgulas.

Funções no R têm personalidade. Cada uma pode funcionar de um jeito diferente das demais, mesmo quando fazem tarefas parecidas. Por exemplo, vejamos a função sum().

sum(1, 3)
## [1] 4

Como podemos ver, essa função retorna a soma de seus argumentos. Também podemos passar um vetor como argumento, e ela retornará a soma dos elementos do vetor.

sum(c(1, 3))
## [1] 4

Já a função mean(), que calcula a média de um conjunto de valores, exige que você passe valores na forma de um vetor:

# Só vai considerar o primeiro número na média
mean(1, 3)
## [1] 1

# Considera todos os valores dentro do vetor na média
mean(c(1, 3))
## [1] 2

Como cada coluna de um data frame é um vetor, podemos calcular a média de uma coluna fazendo:

# Podemos passar esse vetor para a função mean()
mean(mtcars$mpg)
## [1] 20.09062

Também podemos usar argumentos para modificar o comportamento de uma função. O que acontece se algum elemento do vetor for NA?

mean(c(1, 3, NA))
## [1] NA

Como a função não sabe o valor do terceiro elemento do vetor, ela não sabe qual é a média desses 3 elementos e, então, devolve NA. Como é muito comum termos NA nas nossas bases de dados, é muito comum tentarmos calcular a média de uma coluna que tem NA e recebermos NA como resposta.

Na grande maioria dos casos, queremos saber a média de uma coluna apesar dos NAs. Isto é, queremos retirar os NAs e então calcular a média com os valores que conhecemos. Para isso, podemos utilizar o argumento na.rm = TRUE da função mean().

mean(c(1, 3, NA), na.rm = TRUE)
## [1] 2

Esse argumento diz à função para remover os NAs antes de calcular a média. Assim, a média calculada é: (1 + 3)/2.

Claro que cada função tem os seus próprios argumentos e nem toda função terá o argumento na.rm=. Para saber quais são e como usar os argumentos de uma função, basta acessar a sua documentação:

help(mean)

Os argumentos das funções também têm nomes, que podemos ou não usar na hora de usar uma função. Veja por exemplo a função seq().

seq(from = 4, to = 10, by = 2)
## [1]  4  6  8 10

Entre outros argumentos, ela possui os argumentos from=, to= e by=. O que ela faz é criar uma sequência (vetor) de by em by que começa em from e termina em to. No exemplo, criamos uma função de 2 em 2 que começa em 4 e termina em 10.

Também poderíamos usar a mesma função sem colocar o nome dos argumentos:

seq(4, 10, 2)
## [1]  4  6  8 10

Para utilizar a função sem escrever o nome dos argumentos, você precisa colocar os valores na ordem em que os argumentos aparecem. E se você olhar a documentação da função seq(), fazendo help(seq), verá que a ordem dos argumentos é justamente from=, to= e by=.

Escrevendo o nome dos argumentos, não há problema em alterar a ordem dos argumentos:

seq(by = 2, to = 10, from = 4)
## [1]  4  6  8 10

Mas se especificar os argumentos, a ordem importa. Veja que o resultado será diferente.

seq(2, 10, 4)
## [1]  2  6 10

A seguir, apresentamos algumas funções nativas do R úteis para trabalhar com data frames :

  • head() - Mostra as primeiras 6 linhas.
  • tail() - Mostra as últimas 6 linhas.
  • dim() - Número de linhas e de colunas.
  • names() - Os nomes das colunas (variáveis).
  • str() - Estrutura do data frame. Mostra, entre outras coisas, as classes de cada coluna.
  • cbind() - Acopla duas tabelas lado a lado.
  • rbind() - Empilha duas tabelas.

Além de usar funções já prontas, você pode criar a sua própria função. A sintaxe é a seguinte:

nome_da_funcao <- function(argumento_1, argumento_2) {
  
  # Código que a função irá executar
  
}

Repare que function é um nome reservado no R, isto é, você não pode criar um objeto com esse nome.

Um exemplo: vamos criar uma função que soma dois números.

minha_soma <- function(x, y) {
  soma <- x + y
  
  soma  # resultado retornado
}

Essa função tem os seguintes componentes:

  • minha_soma: nome da função
  • x e y: argumentos da função
  • soma <- x + y: operação que a função executa
  • soma: valor retornado pela função

Após rodarmos o código de criar a função, podemos utilizá-la como qualquer outra função do R.

minha_soma(2, 2)
## [1] 4

O objeto soma só existe dentro da função, isto é, além de ele não ser colocado no seu environment, ele só existirá na memória (RAM) enquanto o R estiver executando a função. Depois disso, ele será apagado. O mesmo vale para os argumentos x e y.

O valor retornado pela função representa o resultado que receberemos ao utilizá-la. Por padrão, a função retornará sempre a última linha de código que existir dentro dela. No nosso exemplo, a função retorna o valor contido no objeto soma, pois é isso que fazemos na última linha de código da função.

Repare que se atribuirmos o resultado a um objeto, ele não será mostrado no console:

resultado <- minha_soma(3, 3)

# Para ver o resultado, rodamos o objeto `resultado`
resultado
## [1] 6

Agora, o que acontece se a última linha da função não devolver um objeto? Veja:

minha_nova_soma <- function(x, y) {
  soma <- x + y
}

A função minha_nova_soma() apenas cria o objeto soma, sem retorná-lo como na função minha_soma(). Se utilizarmos essa nova função, nenhum valor é devolvido no console:

minha_nova_soma(1, 1)

No entanto, a última linha da função agora é a atribuição soma <- x + y e esse será o “resultado retornado”. Assim, podemos visualizar o resultado da função fazendo:

resultado <- minha_nova_soma(1, 1)

resultado
## [1] 2

É como se, por trás das cortinas, o R estivesse fazendo resultado <- soma <- x + y, mas apenas o objeto resultado continua existindo, já que os objetos soma, xe y são descartados após a função ser executada.

Claro que, na prática, é sempre bom criarmos funções que retornem na tela os seus resultados, para evitar esse passo a mais se quisermos apenas ver o resultado no console. Assim, a função minha_soma() costuma ser preferível com relação à função minha_nova_soma().

Exercícios

1. Qual dos códigos abaixo devolverá um erro se for avaliado?

  • a. 3 * 5 + 10

  • b. function <- 10

  • c. mean(1, 10)

  • d. (soma <- sum(1, 1))

2. Crie uma função que receba um número e retorne o quadrado deste número.

3. Crie uma função que receba 2 números e devolva a raiz quadrada da soma desses números.

4. Crie uma função que receba dois valores (numéricos) e devolva o maior deles.

5. Use a função runif() para criar uma função que retorne um número aleatório inteiro entre 0 e 10 (0 e 10 inclusive). Caso você não conheça a função runif(), rode help(runif) para ler a sua documentação.

6. Rode help(sample) para descobrir o que a função sample() faz. Em seguida

  • a. use-a para escrever uma função que devolva uma linha aleatória de um data frame;

  • b. generalize a função para retornar um número qualquer de linhas, que poderá ser escolhido por quem for usá-la.


Curso-R