¿Cuál es la mejor manera de aprovechar los argumentos de la función?

Esto se siente como una operación muy simple - computar el medio por grupo de un marco de datos y combinarlo a otro marco de datos preformato - mi UDF hace esto y eso no es realmente la parte con la que estoy luchando.

Lo que quiero es que mi función se meta a través de una serie (lista, vector, etc.) de argumentos.

Quiero ser rápidamente capaz de construir una lista (o vector - No estoy establecido en el uso de listas) de variables y pasarla a la función como argumento para que construya un marco de datos con todas las variables que lo alimento en esa lista. Mi base de datos real tiene variables 50+ y quiero hacer todo tipo de nuevos marcos de datos con diferentes combinaciones de las variables. Una lista podría tener 5 variables, la otra podría tener 25. Pero estoy abierto a la idea de que tengo algo conceptualmente equivocado y que debería estar usando un bucle, purr, mapa, aplicar, algún otro paquete, etc. o cambiar la forma en que mi función está escrita? ¿Qué estoy perdiendo?

library(tidyverse)

data_sample <- data.frame(
  Name = c("Dalton Campbell", "Dalton Campbell", "Dalton Campbell", "Andre Walker", "Andre Walker", "Andre Walker"),
  Defense_Grade = c(88, 86, 92, 94, 97, 95),
  Tackle_Grade = c(66, 69, 72, 74, 76, 78),
  Coverage_Grade = c(44, 43, 44, 76, 73, 78)
)

#Here I set up the dataframe which the function will bind to 
data_sample_averages <-  data_sample %>% 
  group_by(Name) %>% 
  dplyr::summarise(Defense_Grade_Average = mean(Defense_Grade))
#> `summarise()` ungrouping output (override with `.groups` argument)


#Function which computes average of variable (the only argument) and merges it back to data_sample_averages
get_avg2 <- function(v_name) {
  
  avg <- "_Average"      
  
  data_1 <-  data_sample %>% 
    dplyr::group_by(Name) %>% 
    dplyr::summarise("{{ v_name }}_{avg}" := mean({{ v_name }}, na.rm = TRUE))
  
  data_sample_averages <- merge(data_sample_averages, data_1, by = "Name")
  
  return(data_sample_averages)

}

#This works - it computers the average of Tackle_Grade and binds it to data_sample_averages
#However my real dataframe has 50+ columns and I don't want to copy and paste this line 50 times, changing the argument every time.
data_sample_averages <- get_avg2(Tackle_Grade)
#> `summarise()` ungrouping output (override with `.groups` argument)

#shows you the averages
print(data_sample_averages)
#>              Name Defense_Grade_Average Tackle_Grade__Average
#> 1    Andre Walker              95.33333                    76
#> 2 Dalton Campbell              88.66667                    69


#Neither of these work - this is where I'm stuck
#I want my function to iterate through a list of arguments which are essentially just character #strings in order for the UDF to work 
variable_list <- list("Defense_Grade", "Tackle_Grade", "Coverage Grade")

data_sample_averages <- lapply(variable_list, get_avg2)
#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA

#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)

data_sample_averages <- purrr::map(variable_list, get_avg2)
#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)

Pregunta hecha hace 3 años, 5 meses, 0 días - Por logicluminary


3 Respuestas:

  • Dijiste que no estabas usando listas, así que estoy usando vectores.

    Mi solución se basa en una función en la última versión de dplyr: across() función.

    library(tidyverse)
    
    data_sample <- data.frame(
      Name = c("Dalton Campbell", "Dalton Campbell", "Dalton Campbell", "Andre Walker", "Andre Walker", "Andre Walker"),
      Defense_Grade = c(88, 86, 92, 94, 97, 95),
      Tackle_Grade = c(66, 69, 72, 74, 76, 78),
      Coverage_Grade = c(44, 43, 44, 76, 73, 78)
    )
    
    # The function
    compute_avg <- function(.data, names){
        
        names_quo <- enquos(names)  
      
        .data %>%
        group_by(Name) %>%
        summarise(
          across(
            .cols = !!!names_quo,
            .fns = ~ mean(.x, na.rm = TRUE),
            .names = "{.col}_Average"
          )
        )
    }
    
    compute_avg(.data = data_sample, names = c(Defense_Grade, Tackle_Grade))
    
    # A tibble: 2 x 3
      Name            Defense_Grade_Average Tackle_Grade_Average
                                                 
    1 Andre Walker                     95.3                   76
    2 Dalton Campbell                  88.7                   69
    

    Respondida el Dec 17, 2020 a las 12:38 - por bytebarricade

    Votos positivos: 0 | Votos negativos: 0

  • El problema que estás enfrentando es que en tu lista de variables, estás incluyendo un espacio en una de las variables ("Coverage Grade"), por lo que el nombre de la columna no coincide con el nombre real en tu dataframe ("Coverage_Grade").

    Además, en tu función get_avg2, estás usando las llaves dobles {{ }} para referenciar las variables. Sin embargo, al iterar a través de una lista de nombres de variables, no puedes usar las llaves dobles de manera directa.

    Para solucionar estos problemas, aquí te dejo una versión corregida del código:

    R
    library(tidyverse)
    
    data_sample <- data.frame(
      Name = c("Dalton Campbell", "Dalton Campbell", "Dalton Campbell", "Andre Walker", "Andre Walker", "Andre Walker"),
      Defense_Grade = c(88, 86, 92, 94, 97, 95),
      Tackle_Grade = c(66, 69, 72, 74, 76, 78),
      Coverage_Grade = c(44, 43, 44, 76, 73, 78)
    )
    
    data_sample_averages <- data_sample %>% 
      group_by(Name) %>% 
      summarise(Defense_Grade_Average = mean(Defense_Grade))
    
    get_avg2 <- function(v_name) {
    
      avg <- "_Average"      
    
      data_1 <-  data_sample %>% 
        group_by(Name) %>% 
        summarise(!!paste0(v_name, avg) := mean(!!sym(v_name), na.rm = TRUE))
    
      data_sample_averages <<- merge(data_sample_averages, data_1, by = "Name")
    
      return(data_sample_averages)
    }
    
    variable_list <- c("Defense_Grade", "Tackle_Grade", "Coverage_Grade")
    
    

    Usando lapply

    data_sample_averages <- lapply(variable_list, get_avg2)

    Usando purrr::map

    data_sample_averages <- purrr::map(variable_list, get_avg2) print(data_sample_averages)

    En este código, he corregido el nombre de la variable en la lista variable_list y he modificado la forma en que se referencia la variable en la función get_avg2 utilizando !!sym(v_name) en lugar de {{ v_name }}.

    Con estos cambios, deberías poder iterar a través de la lista de variables y calcular las medias para cada una de ellas de manera exitosa.

    Respondida el Dec 18, 2020 a las 12:38 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Problema:

    Quieres pasar una lista de nombres de variables a una función que calcula la media de cada variable en un marco de datos y añade los resultados a otro marco de datos. Sin embargo, tu enfoque actual no funciona.

    Solución 1: Usar dplyr::across()

    dplyr::across() te permite aplicar una función a varias columnas en un marco de datos. Puedes utilizarlo para calcular la media de todas las variables en la lista:

    
    variable_list <- list("Defense_Grade", "Tackle_Grade", "Coverage Grade")
    
    data_sample_averages <- data_sample %>% 
      group_by(Name) %>% 
      dplyr::summarise(across(variable_list, ~mean(., na.rm = TRUE)))
    

    Solución 2: Usar purrr::map2()

    purrr::map2() te permite aplicar dos funciones a dos vectores y combinar los resultados. Puedes utilizarlo para calcular la media de cada variable y unir los resultados al marco de datos promedio:

    
    data_sample_averages <- purrr::map2(variable_list, data_sample, ~{
      avg <- "_Average"      
    
      data_1 <-  data_sample %>% 
        dplyr::group_by(Name) %>% 
        dplyr::summarise("{{ .y }}_{avg}" := mean({{ .y }}, na.rm = TRUE))
    
      merge(data_sample_averages, data_1, by = "Name")
    })
    

    Solución 3: Cambiar la función get_avg2()

    Puedes modificar tu función get_avg2() para que acepte un vector de nombres de variables y calcule la media de todas ellas:

    
    get_avg2 <- function(v_names) {
    
      avg <- "_Average"      
    
      data_1 <-  data_sample %>% 
        dplyr::group_by(Name) %>% 
        dplyr::summarise(across(v_names, ~mean(., na.rm = TRUE)))
    
      data_sample_averages <- merge(data_sample_averages, data_1, by = "Name")
    
      return(data_sample_averages)
    
    }
    

    Uso:

    Una vez que hayas implementado una de estas soluciones, puedes utilizarla para calcular las medias y unirlas al marco de datos promedio:

    
    data_sample_averages <- get_avg2(variable_list)
    

    Respondida el Dec 18, 2020 a las 12:45 - por Gemini

    Votos positivos: 0 | Votos negativos: 0