6.4 Criando ou modificando funções

Funções são objetos que contêm um script que usa os argumentos (também objetos) para executar alguma coisa. A expressão function(){} é utilizada para criar funções. Criar funções é útil pois podem executar algo várias vezes (podendo ser de forma diferente) sem precisar reescrever o código todas às vezes. Isso nos auxilia em práticas rotineiras como, por exemplo, na manipulação de um conjunto de dados de localização geográfica de espécimes botânicos. Podemos gerar um mapa personalizado de distribuição geográfica de cada espécie criando uma função para plotar um mapa, e depois utilizamos a aplicamos a função sobre a categoria, neste caso, a variável contendo o nome da espécie. É muito útil também poder modificar uma função criada por outra pessoa, seja uma função de um determinado pacote ou uma função que você encontrou em uma página qualquer navegando pela internet.

É muito simples construir uma função. Há um bloco que deve ser sempre repetido:

function(meu_argumento1, meu_argumento2, ...) {
  
  # AQUI FICAM AS AÇÕES DE SUA FUNÇÃO, COMO POR EXEMPLO
  
  return("resultado da função") #
  
} 

É costume sempre utilizar a função return() como último elemento da função para que algum objeto seja retornado ao usuário.

?return # veja o help

6.4.1 Exemplo I

Vamos fazer a nossa versão da função mean(), que tira a média aritmética dos valores de um vetor.

# Vamos fazer essa função na unha:
amedia <- function(x) {
  # x será um vetor de comprimento >=1 e todos os valores devem ser numéricos, senão precisamos avisar.
  # essa funçao ira retornar o valor do 'am' que definimos como nulo inicialmente
  am <- NULL
  # condicao 1
  c1 <- is.vector(x)
  # condicao 2
  xx <- as.numeric(x) # convertemos em numérico
  xx <- xx[!is.na(xx)] # tira o que não é número (o que não foi convertido ou está em branco)
  c2 <- length(xx) == length(x) # os comprimentos são iguais? e tem algum valor?
  if (c1 & length(xx) > 0) { # se for um vetor e houver algum valor numérico, pode calcular a média
    am <- sum(xx) / length(xx)
    if (!c2) { # se c1 for falso
      print(paste(length(x) - length(xx), " valores do vetor não são numéricos e foram excluídos")) # avisa
    }
  } else {
    print("O objeto não é um vetor ou não há valores numéricos") # avisa
  }
  return(am)
}

Vamos agora utilizar a função:

v1 <- c(1, 2, 3, 4, 5, 6)
amedia(v1)
## [1] 3.5
v1 <- c(3, 3, 3, 3, 3, 3, 3)
amedia(v1)
## [1] 3
v1 <- c(3, 3, 3, 3, 3, 3, "A")
amedia(v1)
## Warning in amedia(v1): NAs introduced by coercion
## [1] "1  valores do vetor não são numéricos e foram excluídos"
## [1] 3
v1 <- LETTERS
amedia(v1)
## Warning in amedia(v1): NAs introduced by coercion
## [1] "O objeto não é um vetor ou não há valores numéricos"
## NULL

6.4.2 Exemplo II

Os loops por meio da expressão for(){} e a condicional if são muito úteis dentro de funções. Sua função pode, por exemplo, ser construida para que um argumento possa assumir diferentes valores e, a depender do valor, executar uma coisa diferente. Como exemplo, vamos criar uma função que contem o script do exemplo de if:

# CRIA uma funcao com os seguintes argumentos:
# vet = um vetor de valores
# busca.valor = um valor para busca em vet
# nrun = numero de vezes da iteracao
minhafuncao <- function(vet, busca.valor, nrun) {
  # cria um loop do número de vezes (note o argumento nrun abaixo)
  for (v in 1:nrun) {
    # pega um valor aleatorio
    # amostra o indice aleatoriamente
    idx <- sample(1:length(vet), 1)
    valor <- vet[idx]
    # se o valor amostrado for igual ao valor procurado para
    if (valor == busca.valor) {
      # se o valor selecionado aleatoriamente for 10, ou seja se a expressão valor==10 for TRUE execute:
      # imprima isso
      # print(paste("A primeira vez que o valor 10 foi selecionado aleatoriamente foi quando o objeto v assumiu o valor de",v))
      # interrompa (quebre) o loop
      break # note este argumento
    } else {
      # caso contrario, valor!=10, imprime o valor selecionado e continua o loop
      # print(paste("O valor selecionado foi ",valor,"no indice",v))
    }
  }
  # o valor do objeto v será o último valor assumido na execucao do for(){}, se tiver encontrado será menor que nrun, caso contrario será ==nrun
  # se encontrou retorna o indice do valor (que é o último valor assumido por idx)
  # caso o último valor seja == ao valor buscado, ou que o ultimo v é menor que o especificado em nrun, retorna o indice do valor no vetor vet
  if (v < nrun | valor == busca.valor) {
    print(paste("Encontrei o valor", busca.valor, "no indice, ", idx, "do vetor indicado"))
    return(idx)
  } else {
    # caso contrário returna NA
    print(paste("Não encontrei o valor", busca.valor, "no vetor indicado"))
    return(NA)
  }
}

Vamos utilizar a função recém-criada:

umvetor <- sample(10:100, 60, replace = T)
minhafuncao(vet = umvetor, busca.valor = 22, nrun = 1000)
## [1] "Não encontrei o valor 22 no vetor indicado"
## [1] NA
retornou.isso <- minhafuncao(vet = umvetor, busca.valor = 22, nrun = 1000)
## [1] "Não encontrei o valor 22 no vetor indicado"
# e usar de novo com outros valores
umvetor <- sample(200:300, 50, replace = T)
sort(table(umvetor), decreasing = T)[1:10]
255 210 219 228 260 261 264 269 201 202
3 2 2 2 2 2 2 2 1 1
minhafuncao(vet = umvetor, busca.valor = 250, nrun = 10000)
## [1] "Encontrei o valor 250 no indice,  36 do vetor indicado"
## [1] 36
retornou.isso <- minhafuncao(vet = umvetor, busca.valor = 250, nrun = 10000)
## [1] "Encontrei o valor 250 no indice,  36 do vetor indicado"