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()eis.nullforam utilizadas para testar se um valor éNA,NaN, infinito ouNULL, 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 5Nesse 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] 4Como 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] 4Já 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] 2Como 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.09062També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] NAComo 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] 2Esse 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 10Entre 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 10Para 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 10Mas se especificar os argumentos, a ordem importa. Veja que o resultado será diferente.
seq(2, 10, 4)
## [1] 2 6 10A 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çãoxey: argumentos da funçãosoma <- x + y: operação que a função executasoma: 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] 4O 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] 6Agora, 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 + 10b.
function <- 10c.
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.