3.11 Funções

Enquanto objetos são nomes que guardam valores, funções no R são nomes que guardam um código de R. A ideia é muito simples: sempre que você rodar uma função, o código que ela guarda será executado e um resultado nos será devolvido.

Funções são tão comuns e intuitivas (provavelmente você já usou funções no Excel), que mesmo sem definir o que elas são, nós já utilizamos 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.

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().

Como você deve ter percebido, 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.

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:

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

Também podemos usar argumentos para modificar o comportamento de uma função. O que acontece se algum elemento do vetor for 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().

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:

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().

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:

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:

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

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:

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.

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.

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:

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

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:

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:

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

  1. 3 * 5 + 10
  2. function <- 10
  3. mean(1, 10)
  4. (soma <- sum(1, 1))

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

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

4. Rode help(sample) para descobrir o que a função sample() faz. Em seguida, use-a para escrever uma função que devolva uma linha aleatória de um data frame.


Curso-R