Funções

Definição

  • Funções são blocos de código que podem ser chamados para realizar uma tarefa específica

  • Elas facilitam a organização do código, promovem a reutilização e tornam o código mais organizados.

  • As funções permitem-lhe reduzir a duplicação de código, automatizando uma tarefa generalizada a ser aplicada recursivamente.

  • Dica: você deve considerar escrever uma função sempre que copiar e colar um bloco de código mais de duas vezes

  • Para definir uma função em R, você usa a palavra-chave function seguida pelos parâmetros/argumentos da função e o corpo da função

name <- function(arguments) {
  body
  return(objeto que guarda o resultado final)
}
  • Funções são objetos, assim como vetores são objetos.

  • De forma geral, as funções podem ser divididas em três partes:

    • O formals(), a lista de argumentos que controlam como você chama a função.

    • O body(), o código dentro da função.

    • O environment(), a estrutura de dados que determina como a função encontra os valores associados aos nomes.

  • Embora os formals() e o body() sejam especificados explicitamente quando você cria uma função, o environment() é especificado implicitamente, com base em onde você definiu a função.

  • O environment() da função sempre existe, mas só é impresso quando a função não está definida no ambiente global.

  • f(x) = 2x

f_x = function(x){
  f_x = 2*x # função
  return(f_x)
}

x = c(1,2,3,4)
(y = f_x(x))
[1] 2 4 6 8
args(f_x)
function (x) 
NULL
plot(x,y)

formals(f_x)
$x
body(f_x)
{
    f_x = 2 * x
    return(f_x)
}
environment(f_x)
<environment: R_GlobalEnv>
attr(f_x, "srcref")
function(x){
  f_x = 2*x # função
  return(f_x)
}
  • Média de um vetor numérico
media = function(x){
  media_x = sum(x)/length(x)
  return(media_x)
}
y = sample(1:100,20,replace=T)
media(y)
[1] 51.25
mean(y)
[1] 51.25
  • Função para fazer uma correspondência entre o número e o dia da semana
dia_semana <- function(numero_dia) {
  dia <- switch(
    numero_dia,
    "1" = "Domingo",
    "2" = "Segunda-feira",
    "3" = "Terça-feira",
    "4" = "Quarta-feira",
    "5" = "Quinta-feira",
    "6" = "Sexta-feira",
    "7" = "Sábado"
  )
  return(cat("O dia correspondente ao número", numero_dia, "é", dia, "\n"))
}
dia_semana(2)
O dia correspondente ao número 2 é Segunda-feira 

Argumentos Faltantes

  • A função missing em R é usada para verificar se um argumento foi fornecido a uma função.

  • Isso é particularmente útil quando você deseja definir um comportamento padrão ou tomar decisões com base na presença ou ausência de argumentos.

# Função para somar dois números com valores padrão
soma_com_default <- function(a, b) {
  if (missing(a)) {
    a <- 0
  }
  
  if (missing(b)) {
    b <- 0
  }
  
  return(a + b)
}

# Testando a função com diferentes combinações de argumentos
print(soma_com_default(3, 5))   # Ambos os argumentos fornecidos: 8
[1] 8
print(soma_com_default(3))      # Apenas 'a' fornecido: 3
[1] 3
print(soma_com_default(b = 5))  # Apenas 'b' fornecido: 5
[1] 5
print(soma_com_default())       # Nenhum argumento fornecido: 0
[1] 0
  • Observe como o argumento size é definido na função sample
sample
function (x, size, replace = FALSE, prob = NULL) 
{
    if (length(x) == 1L && is.numeric(x) && is.finite(x) && x >= 
        1) {
        if (missing(size)) 
            size <- x
        sample.int(x, size, replace, prob)
    }
    else {
        if (missing(size)) 
            size <- length(x)
        x[sample.int(length(x), size, replace, prob)]
    }
}
<bytecode: 0x00000294a067def0>
<environment: namespace:base>

Argumentos padrão

  • A função pode ser construída com argumentos pré-definidos
media = function(x = c(1,2,3,4)){
  return( sum(x)/length(x)) 
}
media()
[1] 2.5

Funções primitivas

  • Há uma exceção à regra de que uma função possui três componentes. Funções primitivas, como sum()[, chamam o código C diretamente.
sum
function (..., na.rm = FALSE)  .Primitive("sum")
`[`
.Primitive("[")
  • Essas funções existem principalmente em C, não em R, então seus formals()body()environment()são todos NULL:

dot,dot,dot

  • Na definição de uma função, você pode usar três pontos (...) para indicar que a função aceita argumentos adicionais.
  • Esses argumentos adicionais não precisam ser explicitamente listados na definição da função
  • Esses argumentos adicionais geralmente são passados para outras funções dentro do corpo da função.
  • No exemplo abaixo, ... permite a passagem de argumentos adicionais, como na.rm para remover valores ausentes ao calcular a média.
media_ponderada <- function(valores,pesos,...) {
  # Calcula a média ponderada
  resultado <- sum(valores * pesos,...) / sum(pesos,...)
  return(resultado)
}
media_ponderada(c(1,2,NA),c(1,2,3))
[1] NA
media_ponderada(c(1,2,NA),c(1,2,3),na.rm=T)
[1] 0.8333333

on.exit

  • A função on.exit é usada para especificar código que deve ser executado quando a execução de uma função é concluída

  • Frequentemente usada para garantir que determinadas ações sejam realizadas antes que a função termine, independentemente de como ele termina

  • No exemplo a seguir vamos trocar de diretório

names_wd_atual <- "colocar caminho" # diretório atual
wd_atual <- setwd(names_wd_atual)

criar_grafico <- function() {
  x11() # abrindo nova janela para a figura
  new_wd <- paste0(wd_atual,"/Figuras/figura_exemplo_on_exit.jpg")
  jpeg(new_wd) # salvando figura no novo diretório
  plot(1:10, main = "Meu Gráfico", ylab = "Valores")
  # Configura on.exit para fechar o dispositivo gráfico ao sair
  on.exit({
    dev.off()
    cat("Fechando o dispositivo gráfico\n")
    setwd(wd_atual) # voltando para o diretório atual
    cat("voltando para o diretório atual\n")
  }, add = TRUE)
}

# Chama a função
criar_grafico()

Formas de Funções

  • Embora tudo o que acontece em R seja resultado de uma função, nem todos as funções são formuladas da mesma forma.

  • Existem quatro formas de funções:

    • Prefix Functions

    • Infix Functions

    • Replacement Functions

    • Special functios

Prefix Functions

  • São as funções padrão ou regulares, identificadas por um nome seguido de argumentos entre parênteses.
media_ponderada <- function(x,peso=c(1,1,1),...)return(sum(peso*x,...)/sum(peso,...))


media_ponderada(c(1,2,3)) # o nome da função vem antes de seus argumento
[1] 2

Infix Functions

  • O nome da função fica entre seus argumentos

  • As formas infixas são usadas para muitos operadores matemáticos e para funções definidas pelo usuário que começam e terminam com %

[1] -1
[1] 5
[1] 6
[1] 0.6666667
x <- c(1,2,3,4,5)
6 %in% x # o número 6 pertence ao conjuto x?
[1] FALSE
x <- c(1,2,3,4,5)
2 %in% x # o número 2 pertence ao conjuto x?
[1] TRUE
mat1 = matrix(c(1,2,3,4),2,2)
mat2 = matrix(c(1,2,3,4),2,2)
mat1 %*% mat2 # multiplicação de matriz
     [,1] [,2]
[1,]    7   15
[2,]   10   22
  • Definindo nova função infix
`%+%` <- function(a, b) paste(a, b,sep="-")
"new " %+% "string"
[1] "new -string"
  • A biblioteca magrittr adiciona o operador pipe, que permite usar a saída de uma função como entrada de outra função
require(magrittr) # pacote para usar %>%
x <- c(1,2,3,4)
desvio_padrao = sqrt( var(x) )
desvio_padrao = var(x) %>% sqrt()
desvio_padrao = var(x) |> sqrt() # pacote base
  • Os colchetes transformam o pipe no que é quase uma função anônima
1:5 %>% {(. + 1) / 2} # 1:5 são utilizados no lugar do ponto
[1] 1.0 1.5 2.0 2.5 3.0

Replacement Functions

  • Estas funções são frequentemente usadas para atribuir valores a variáveis ou modificar objetos existentes.
'function_name<-' <- function(x, additional arguments, value) 
{ 
function body 
} 
  • Nomear elementos do vetor com a função names
x = c(1,2,3,4)
names(x) <- paste0("n",1:length(x))
x
n1 n2 n3 n4 
 1  2  3  4 
  • Mudar o segundo elemento do vetor
"replace_second<-" = function(x,value){
  x[2] <- value
  x
}
x = c(1,2,3,4)
replace_second(x) <- 0L
x
[1] 1 0 3 4
'modify<-' <- function(x, position, value) {
  x[position] <- value
  x
}
x = 1:5
modify(x, 1) <- 2 # value=2
x
[1] 2 2 3 4 5

Special Functions

  • Funções especiais muitas vezes desempenham papéis específicos, como manipulação condicional, filtragem de dados e manipulação de strings.

  • Ferramentas de controle de fluxo

    • if (cond) true (`if`(cond, true))

    • if (cond) true else false (`if`(cond, true, false))

    • for(var in seq) action (`for`(var, seq, action))

    • while(cond) action (`while`(cond, action))

    • repeat expr (`repeat`(expr))

    • next (`next`())

    • break (`break`())

Condições

  • O sistema de condições fornece um conjunto de ferramentas que permitem ao autor de uma função indicar que algo incomum está acontecendo

  • O autor da função sinaliza condições com funções como stop() (para erros), warning() ( para avisos) e message() (para mensagens)

  • Compreender o sistema de condições é importante porque muitas vezes você precisará desempenhar as duas funções: sinalizar as condições das funções que você cria e lidar com as condições sinalizadas pelas funções que você chama.

Erro (Stop Condition):

  • A função stop() é usada para sinalizar um erro. Quando um erro é sinalizado, a execução do código é interrompida e uma mensagem de erro é exibida.
calcular_media <- function(x) {
  if (length(x) == 0) {
    stop("Erro: O vetor está vazio.")
  }
  if (is.numeric(x) == F) {
    stop("Erro: vetor deve ser do tipo numérico")
  }
  return(mean(x))
}
calcular_media(1:4)
[1] 2.5
calcular_media <- function(x) {
  if (length(x) == 0) {
    stop("Erro: O vetor está vazio.")
  }
  if (is.numeric(x) == F) {
    stop("Erro: vetor deve ser do tipo numérico")
  }
  return(mean(x))
}
calcular_media(c(1,2,3,"l"))
# Error in calcular_media(c(1, 2, 3, "l")) : 
#  Erro: vetor deve ser do tipo numérico

Aviso (Warning Condition):

  • A função warning() é usada para sinalizar um aviso. Um aviso não interrompe a execução do código, mas alerta o usuário sobre algo que pode ser problemático.
calcular_raiz_quadrada <- function(x) {
  if (any(x < 0)) {
    warning("Alguns valores são negativos. A raiz quadrada de números negativos será tratada como NaN.")
  }
  return(sqrt(x))
}

# Exemplo de uso
resultado <- calcular_raiz_quadrada(c(4, -9, 16))
print(resultado)
[1]   2 NaN   4

Mensagem Informativa (Message Condition):

  • A função message() é usada para sinalizar mensagens informativas. Essas mensagens são úteis para fornecer informações adicionais durante a execução do código.
calcular_cubo <- function(x) {
  message("Calculando o cubo dos valores.")
  return(x^3)
}

# Exemplo de uso
resultado <- calcular_cubo(c(2, 3, 4))
print(resultado)
[1]  8 27 64

Ignorando as condições

  • Ao lidar com condições em R, você pode usar as funções try(), suppressWarnings(), e suppressMessages() para controlar o tratamento de erros, avisos e mensagens.

try()

  • A função try() é usada para avaliar expressões e manipular erros sem interromper a execução do código. Ela retorna um objeto que você pode verificar usando inherits() para determinar se ocorreu um erro.
x = c()
mean(x)
[1] NA
resultado <- try(mean(), silent = TRUE)
resultado
[1] "Error in mean.default() : argumento \"x\" ausente, sem padrão\n"
attr(,"class")
[1] "try-error"
attr(,"condition")
<simpleError in mean.default(): argumento "x" ausente, sem padrão>
if (inherits(resultado, "try-error")) { # verifica se o objeto resultado é da classe "try-error
    cat("Erro: Ocorreu um problema ao calcular a média.\n")}
Erro: Ocorreu um problema ao calcular a média.

suppressWarnings() para Suprimir Avisos:

  • A função suppressWarnings() é usada para executar uma expressão e suprimir os avisos que ela pode gerar.
x = c()
mean(x)
Warning in mean.default(x): argumento não é numérico nem lógico: retornando NA
[1] NA
suppressWarnings(mean(x))
[1] NA

suppressMessages() para Suprimir Mensagens:

  • A função suppressMessages() é usada para executar uma expressão e suprimir as mensagens informativas que ela pode gerar.
calcular_cubo <- function(x) {
  message("Calculando o cubo dos valores.")
  return(x^3)
}

# Exemplo de uso
resultado <- suppressMessages(calcular_cubo(c(2, 3, 4)))
resultado
[1]  8 27 64

Invocando funções

  • Normalmente você chama uma função colocando seus argumentos, entre parênteses, após seu nome: mean(1:10, na.rm = TRUE)

  • Porém, em algumas situações você tem uma lista de argumentos que deseja passar para uma função.

  • A função do.call em R é usada para chamar uma função e passar argumentos para ela na forma de uma lista

Exemplo 1

# Lista de argumentos
args <- list(1, 2, 3, 4, 5)

# Usando do.call para chamar a função sum
resultado <- do.call(sum, args)
print(resultado)  # Isso imprimirá 15
[1] 15

Exemplo 2

  • Você pode usar do.call com funções que têm parâmetros nomeados
# Definindo uma função que cria uma sequência
criar_sequencia <- function(inicio, fim, passo = 1) {
  return(seq(from = inicio, to = fim, by = passo))
}

# Lista de argumentos com parâmetros nomeados
args <- list(inicio = 1, fim = 10, passo = 2)

# Usando do.call para chamar a função criar_sequencia
sequencia <- do.call(criar_sequencia, args)
print(sequencia) 
[1] 1 3 5 7 9

Exemplo 3

  • Um uso comum de do.call é combinar listas de data frames:
# Criando dois data frames
df1 <- data.frame(a = 1:3, b = 4:6)
df2 <- data.frame(a = 7:9, b = 10:12)

# Lista de data frames
dfs <- list(df1, df2)

# Usando do.call com rbind para combinar os data frames
df_combinado <- do.call(rbind, dfs)
print(df_combinado)
  a  b
1 1  4
2 2  5
3 3  6
4 7 10
5 8 11
6 9 12
# Criando dois data frames
df1 <- data.frame(a = 1:3, b = 4:6)
df2 <- data.frame(c = 7:9, d = 10:12)

# Lista de data frames
dfs <- list(df1, df2)

# Usando do.call com rbind para combinar os data frames
df_combinado <- do.call(cbind, dfs)
print(df_combinado)
  a b c  d
1 1 4 7 10
2 2 5 8 11
3 3 6 9 12

Exemplo 4

  • Selecionar funções baseada em condições
vectors <- list(c(1, 2, 3), c(4, 5, 6))
op = "SOMA"

if (op == "SOMA") {
  result <- do.call("+", vectors)
} else if(op != "SOMA") 
  {
  result <- do.call("*", vectors)
}

result
[1] 5 7 9

Exemplo 5

  • Usando a função paste
strings_list <- list("Hello", "world!", "How", "are", "you?")
strings_list
[[1]]
[1] "Hello"

[[2]]
[1] "world!"

[[3]]
[1] "How"

[[4]]
[1] "are"

[[5]]
[1] "you?"
# Combine strings in the list into a single sentence
do.call(paste, c(strings_list,sep=" "))
[1] "Hello world! How are you?"

Função Anônima

  • Embora quase sempre você crie uma função e depois a vincule a um nome, a etapa de vinculação não é obrigatória.

  • Se você optar por não dar um nome a uma função, obterá uma função anônima .

y = list(c(rnorm(100)))

do.call(what = function(x) var(x)|> sqrt(),args = y)
[1] 0.9239861
# Criando um vetor de números
numeros <- c(1, 5, 8, 3, 7, 2)

# Usando Filter com uma função anônima
filtrados <- Filter(function(x) x^2 > 4, numeros)

print(filtrados)
[1] 5 8 3 7
integrate(function(x) exp(-x), 0, Inf)
1 with absolute error < 5.7e-05
integrate(function(x) dnorm(x), -3, 3)
0.9973002 with absolute error < 9.3e-07

Vantagens das Funções Anônimas:

  • Conveniente para Operações Simples: Funções anônimas são úteis para operações simples que não precisam ser reutilizadas em outros lugares do código.

  • Reduz Poluição do Espaço de Nomes: Como não possuem um nome explícito, elas não poluem o espaço de nomes com funções que são usadas apenas uma vez.

Lista de funções

  • Funções em uma lista
funs <- list(
  half = function(x) x / 2,
  mult = function(x) x * 2
)
funs$mult(10)
[1] 20

Atividade

  1. Escreva uma função que, dado um número de 1 a 12, imprima o nome do mês do ano correspondente.

  2. O coeficiente de variação (cv) é uma forma de medir a variabilidade dos dados. Crie uma função que calcule o coeficiente de variação de um vetor numérico. Crie uma condição para que se o vetor não for do tipo numeric, não faça o cálculo

  3. Crie uma função para criar um objeto de uma classe chamada “Livro” que representa um livro com atributos como autor e ano de publicação.

  4. Considere a seguinte função: f(x) = (x - 3)^2 + 5 se 0=<x<10 e f(x) = 54 se x>=10. Escreva uma função considerando: erro para entrada do tipo não numérico e erro para entrada de valores negativos.

  5. Considere os argumentos x e y. Use o conceito de função infix para saber se x é divisível por Y.

  6. Crie um exemplo usando a função try. Se for erro, atribua NaN ao objeto da class try-error e imprima mensagem de erro


Referências