Anexo 4: Controle de Fluxo e Funções em R

Conteúdo:

  1. Funções em R
    • O que são Funções?
    • Parâmetros (Argumentos)
    • Retorno de Valores
  2. Blocos de Código if (Condicionais)
  3. Controle de Fluxo com Loops: for , while e repeat
  4. modo base: apply, lapply, tapply e sapply
  5. purr tidyverse: map, walk,etc..

O que são Funções

Funções são blocos de código que executam uma tarefa específica e podem ser reutilizados em diferentes partes do seu programa. Elas são Com elas que você pode criar para automatizar processos, tornando seu código mais organizado, eficiente e fácil de manter evitando retrabalho.

Nós interagimos com funções o tempo inteiro no R quase tudo que você faz é usar funções e especificar parâmetros.

Uma função básica em R tem os seguintes elementos:

  • nome_da_funcao: É o nome que você dá à sua função para poder chamá-la depois.

  • function(): É a palavra-chave que indica a criação de uma função.

  • (argumentos): São os valores que a função recebe para trabalhar. Eles são opcionais.

  • {corpo_da_funcao}: É o bloco de código dentro das chaves ({}) que contém as instruções a serem executadas.

  • return(): É a instrução que especifica o valor que a função deve retornar como resultado. No R, a última expressão avaliada no corpo da função é retornada automaticamente, então o return() é opcional, mas é uma boa prática para clareza.

Vamos construir uma função que transforma uma temperatura em graus Fahrenheit para graus celsius.

A fórmula para converter a temperatura é:

\[ C = \frac{(F - 32) * 5} {9} \]

Vamos chamar a nossa função conv_c2c (existe uma série de recomendações sobre como dar nome a funções). A função vai receber um parâmetro temp_f com a temperatura a ser convertida e retorna rum valor com a temperatura em Celsius.

conv_f2c <- function(temp_f) { 
# O argumento 'f' representa a temperatura em Fahrenheit.
  
  # Aplica a fórmula de conversão
  temp_c <- (temp_f - 32) * (5/9)
  
  # Retorna o valor em Celsius
  return(temp_c)
}

Escopo de Variáveis em Funções

O escopo de uma variável determina onde ela pode ser acessada. Em R, as variáveis definidas dentro de uma função têm escopo local, o que significa que elas só podem ser acessadas dentro da função. Variáveis definidas fora de uma função têm escopo global e podem ser acessadas em qualquer lugar do código.

# Variavel global
x <- 10

minha_funcao <- function() {
  # Variavel local
  y <- x/2
  print(x) # Acessa a variavel global x
  print(y)
}

minha_funcao() # Saída: 10, 5

# print(y)# Erro: y não está definido fora da função

Uma função deve receber os argumentos necessários a sua execução. As variáveis internas a função só existem dentro do espaço da função. A função retorna apenas um valor que pode ser qq tipo de dado.

Vantagens de usar funções

  • Reusabilidade: Você escreve o código uma vez e pode usá-lo várias vezes, evitando repetição de código.

  • Organização: Funções dividem seu código em partes lógicas e gerenciáveis.

  • Facilidade de manutenção: Se você precisar corrigir ou melhorar um pedaço do seu código, só precisa fazer a alteração na função, e não em todos os lugares onde o código foi repetido.

  • Legibilidade: O código se torna mais fácil de ler e entender, pois o nome da função já descreve o que ela faz.

  • Debug: Possibilidade de depurar o código inspecionando o seu funcionamento passo a passo.

Blocos de Código if (Condicionais)

Os blocos if permitem que você execute diferentes partes do código com base em uma condição. o blocos {} delimitam o bloco de código que será executado se a condição do if for verdadeira.

if (condição) {
  # Código a ser executado se a condição for verdadeira
} else {
  # Código a ser executado se a condição for falsa (opcional)
}

Analise o código abaixo

idade <- 20

if (idade >= 18) {
  print("Você é maior de idade.")
  print("você já pode dirigir")  
} else {
  print("Você é menor de idade.")
}

As chaves {} são essenciais quando você tem mais de uma instrução a ser executada dentro do bloco if. Sem as chaves, apenas a primeira instrução após o if é considerada parte do bloco, o que pode levar a erros. Mesmo com uma única instrução, usar chaves melhora a legibilidade do código. As condições dentro do IF podem ser complexas usando os demais operadores lógicos.

O controle de fluxo por laços

As estruturas for, while e repeat são as principais ferramentas em R para criar laços de repetição (ou loops), que são usados para executar um bloco de código várias vezes.

O for for é ideal quando você sabe exatamente quantas vezes a repetição precisa ocorrer. Ele itera sobre uma sequência de itens, como um vetor, uma lista ou uma coluna de um data frame, executando o código para cada item.

for (item in sequencia) {
  # Código a ser executado
}

por exemplo:

numeros <- c(1, 3, 5, 7, 9 ) 

for (n in numeros) {
  quadrado <- n^2
  print(paste("O quadrado de", n, "é", quadrado))
}

o for é laço mais comum e seguro, pois evita loops infinitos. É a melhor escolha para a maioria das tarefas de iteração. em sua forma geral temos:

 while (condicao) {
  # Código a ser executado
}
contador <- 5

while (contador > 0) {
  print(contador)
  contador <- contador - 1  # É crucial alterar a variável para evitar um loop infinito
}
print("Contador chegou a ZERO!")

O while útil quando você não sabe o número exato de repetições, mas sabe qual condição deve ser satisfeita para que o loop pare.

o repeat executa um bloco de código indefinidamente até que seja explicitamente interrompido. Você deve usar uma instrução break dentro do laço para parar a execução, baseada em uma condição. Se não houver break, o loop será infinito.

repeat {
  # Código a ser executado
  if (condicao_de_parada) {
    break
  }
}
numeros <- c(3, 7, 9, 12, 5, 15)
i <- 1

repeat {
  numero_atual <- numeros[i]
  
  if (numero_atual > 10) {
    print(paste("O primeiro número maior que 10 é", numero_atual))
    break  # Sai do loop
  }
  
  i <- i + 1
}

O repeat útil para situações onde a condição de saída pode estar no meio do bloco de código, ou quando você precisa garantir que o laço execute pelo menos uma vez. No entanto, é mais propenso a erros (como loops infinitos) e geralmente menos usado que o for e o while.

Em resumo:

for while repeat
Interações conhecida condicional indefinida (break)
condição parada sequencia definida condição é FALSA comando break
uso tipico iteragir em uma sequencia repetir até condição simulações, eventos do sistema

Familia apply

No R BASE, as funções apply, lapply e sapply são ferramentas poderosas para aplicar uma função a uma estrutura de dados (como um vetor, matriz, lista ou data frame) sem a necessidade de escrever um loop explícito. Elas são fundamentais para programação funcional e para tornar o código mais rápido conciso e eficiente.

1. apply()

  • Finalidade: Aplica uma função a cada linha ou coluna de uma matriz ou array.
  • Estrutura: apply(X, MARGIN, FUN, ...).
    • X: A matriz, array ou data frame a ser processado.
    • MARGIN: Especifica se a função deve ser aplicada a:
      • 1: Linhas.
      • 2: Colunas.
    • FUN: A função a ser aplicada.
    • ...: Argumentos adicionais a serem passados para a função FUN.
  • Retorno: Depende da função FUN e da estrutura de dados X. Pode ser um vetor, matriz, lista ou outra estrutura de dados.
  • Exemplo:
# Criando uma matriz
matriz <- matrix(1:9, nrow = 3, ncol = 3)
matriz

# Aplicando a função 'mean' a cada linha
apply(matriz, 1, mean)


# Aplicando a função 'sum' a cada coluna
apply(matriz, 2, sum)

2. lapply()

  • Finalidade: Aplica uma função a cada elemento de uma lista.
  • Estrutura: lapply(X, FUN, ...)
    • X: A lista a ser processada.
    • FUN: A função a ser aplicada.
    • ...: Argumentos adicionais a serem passados para a função FUN.
  • Retorno: Sempre retorna uma lista, mesmo que a função FUN retorne valores que poderiam ser combinados em um vetor ou matriz.
  • Exemplo:
# Criando uma lista
minha_lista <- list(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9))

# Aplicando a função 'length' a cada elemento da lista
comprimentos <- lapply(minha_lista, length)
print(comprimentos)

3. sapply()

  • Finalidade: Semelhante a lapply(), mas tenta simplificar a saída. Aplica uma função a cada elemento de uma lista e tenta simplificar a saída para um vetor ou matriz, se possível.
  • Estrutura: sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
    • X: A lista a ser processada.
    • FUN: A função a ser aplicada.
    • ...: Argumentos adicionais a serem passados para a função FUN.
    • simplify: Um argumento lógico (TRUE por padrão). Se TRUE, tenta simplificar a saída. Se FALSE, o comportamento é idêntico a lapply.
    • USE.NAMES: Um argumento lógico (TRUE por padrão). Se TRUE, os nomes dos elementos da lista de entrada são preservados na lista de saída (se possível).
  • Retorno: Pode retornar um vetor, matriz, lista ou outra estrutura de dados, dependendo do resultado da função FUN.
  • Exemplo:
# Criando uma lista
minha_lista <- list(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9))

# Aplicando a função 'length' a cada elemento da lista
comprimentos <- sapply(minha_lista, length)
print(comprimentos)

Principais Diferenças e Quando Usar:

  • apply(): Ideal para trabalhar com matrizes e arrays, permitindo aplicar funções a linhas ou colunas.
  • lapply(): Use quando você precisa aplicar uma função a cada elemento de uma lista e sempre precisa de uma lista como resultado. É uma boa escolha quando você não tem certeza sobre o tipo de saída da função que está aplicando.
  • sapply(): Use quando você quer aplicar uma função a uma lista e obter uma saída simplificada (vetor ou matriz) sempre que possível. Se a função FUN retornar resultados de tamanhos diferentes, sapply ainda retornará uma lista. É um atalho conveniente para usar lapply e, em seguida, tentar simplificar o resultado.

Em resumo:

Característica apply() lapply() sapply()
Estrutura de Dados Matrizes, arrays Listas Listas
Retorno Depende da função e da estrutura de dados Sempre uma lista Tenta simplificar para vetor/matriz
Foco Aplicar a linhas/colunas Aplicar a cada elemento (lista) Aplicar e simplificar

Dominar essas funções é fundamental para escrever código R mais eficiente e elegante. Experimente com diferentes funções e estruturas de dados para entender completamente como elas funcionam.

Pacote `purr’do tidyverse

As funções map e walk do pacote purrr em R são alternativas modernas e expressivas às funções lapply, sapply e walk do R base. O purrr faz parte do ecossistema tidyverse, que enfatiza a clareza, a consistência e a legibilidade do código. Elas oferecem recursos adicionais e uma sintaxe mais intuitiva para trabalhar com listas e vetores.

1. map()

  • Finalidade: Aplica uma função a cada elemento de um vetor, lista ou tibble. É a alternativa mais direta para lapply e sapply.

  • Estrutura: map(x, f, ..., .x = NULL, .y = NULL, by = NULL, .keep = NULL, .call = NULL)

    • x: O vetor, lista ou tibble a ser processado.
    • f: A função a ser aplicada.
    • ...: Argumentos adicionais a serem passados para a função f.
    • .x, .y: Nomes padrão dos argumentos que recebem o vetor/lista original e o índice (opcional).
    • by: Permite aplicar a função a subconjuntos dos dados.
    • .keep: Especifica quais nomes devem ser mantidos na lista de saída.
    • .call: Permite usar uma expressão como a função a ser aplicada (menos comum).
  • Variações: map_lgl(), map_int(), map_dbl(), map_chr(), map_logl(), map_int(), map_dbl(), map_chr(). Essas variações forçam o tipo de saída da função f para lógico, inteiro, numérico (double) e caracter, respectivamente. Isso ajuda a evitar ambiguidades e garante que a saída seja do tipo esperado.

  • Retorno: Sempre retorna uma lista, a menos que você use uma das variantes que forçam um tipo específico (como map_dbl, que retorna um vetor numérico).

  • Exemplo:

library(purrr)

# Criando uma lista
minha_lista <- list(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9))

# Aplicando a função 'length' a cada elemento da lista
comprimentos <- map(minha_lista, length)
print(comprimentos)

# Forçando o tipo de saída para numérico
comprimentos_num <- map_dbl(minha_lista, length)
print(comprimentos_num)

2. walk()

  • Finalidade: Aplica uma função a cada elemento de um vetor, lista ou tibble e não retorna nada. É projetada para efeitos colaterais, como imprimir resultados, escrever em arquivos ou atualizar gráficos. É uma alternativa à função for (embora geralmente mais legível).

  • Estrutura: walk(x, f, ..., .x = NULL, .y = NULL)

    • x: O vetor, lista ou tibble a ser processado.
    • f: A função a ser aplicada.
    • ...: Argumentos adicionais a serem passados para a função f.
    • .x, .y: Nomes padrão dos argumentos que recebem o vetor/lista original e o índice (opcional).
  • Variações: walk_list(), walk2(), walk_along(). walk_list() retorna uma lista dos resultados da função aplicada (útil em alguns casos), enquanto walk2() aplica a função a dois vetores/listas simultaneamente. walk_along() itera sobre os elementos de um vetor ou lista, em vez de sobre os nomes.

  • Retorno: NULL. A função é utilizada apenas pelos seus efeitos colaterais.

  • Exemplo:

library(purrr)

# Criando uma lista
minha_lista <- list(c(1, 2, 3, 5, 10), c(9, 5, 7, 4), c(11, 8))

# Imprimindo o comprimento de cada elemento da lista
walk(minha_lista, function(x) length(x)))

Principais Diferenças e Vantagens de purrr:

  • Clareza e Consistência: O purrr segue uma filosofia de design consistente, tornando o código mais fácil de entender e manter.
  • Nomes de Argumentos Padrão: Os nomes de argumentos .x e .y tornam mais claro qual argumento recebe o elemento atual e qual recebe o índice (se você precisar dele).
  • Forçamento de Tipos de Dados: As variantes map_lgl(), map_int(), map_dbl(), map_chr() e também o map_db que forçam o tipo de dados da saída, evitando ambiguidades e facilitando o debugging.
  • Efeitos Colaterais Explícitos: walk() é projetada especificamente para efeitos colaterais, o que torna o código mais legível e fácil de entender.
  • Integração com tidyverse: O purrr se integra perfeitamente com outras funções do tidyverse, facilitando a criação de pipelines de dados elegantes e eficientes.
  • Mais Expressivo: Em muitos casos, o purrr oferece uma sintaxe mais concisa e expressiva do que as funções do R base.

Quando Usar:

  • map(): Substitui lapply e sapply quando você precisa aplicar uma função a uma lista ou vetor e precisa de uma lista como resultado ou quer forçar o tipo de saída.
  • walk(): Use em vez de loops for quando você precisa executar uma ação (efeito colateral) para cada elemento de uma lista ou vetor.
  • Em geral: Se você estiver trabalhando com o tidyverse, usar purrr é altamente recomendado para consistência e clareza do código.

O purrr é uma adição valiosa ao seu arsenal de ferramentas em R, especialmente se você valoriza a clareza, a consistência e a legibilidade do código.

R Programming for Data Science Advanced R 2nd edition