Как я могу мутировать, используя динамически сгенерированный список функций

У меня есть фрейм данных, который инкапсулирует ряд статистических данных для экзамена, отслеживаемых за разные годы и группы. Я хотел бы создать функцию, которая добавляет новые столбцы, показывающие изменение этой статистики для каждой группы из динамически поставляемого списка базовых лет.

Вот пример вывода, который я хотел бы.

grades <- data.frame(
  Group = c(rep("A", 4), rep("B", 4)),
  Year  = rep(seq(2015, 2018), 2),
  Mean  = c(seq(100, 130, 10), seq(200, 260, 20)),
  PassR = c(seq(0.5, 0.53, 0.01), seq(0.6, 0.66, 0.02))
)

grades |> group_by(Group) |> calculateDifferences(c(2015, 2016))

# A tibble: 8 × 8
# Groups:   Group [2]
  Group  Year  Mean PassR Mean_Diff2015 Mean_Diff2016 PassR_Diff2015 PassR_Diff2016
  <chr> <int> <dbl> <dbl>         <dbl>         <dbl>          <dbl>          <dbl>
1 A      2015   100  0.5              0           -10         0             -0.0100
2 A      2016   110  0.51            10             0         0.0100         0     
3 A      2017   120  0.52            20            10         0.0200         0.0100
4 A      2018   130  0.53            30            20         0.0300         0.0200
5 B      2015   200  0.6              0           -20         0             -0.0200
6 B      2016   220  0.62            20             0         0.0200         0     
7 B      2017   240  0.64            40            20         0.0400         0.0200
8 B      2018   260  0.66            60            40         0.0600         0.0400

Моя лучшая попытка — это следующая функция, но она сталкивается с проблемами области видимости столбца «Год» в списке.

# Calculate differences from the given year for both mean and pass rate
calculateDifferences <- function(data, diffYears) {
  mutate(data,
    across(
      any_of(c("Mean", "PassR")), 
      #list(Diff2015 = function(col) col - col[Year == 2015],
      #     Diff2016 = function(col) col - col[Year == 2016]),
      map(as.list(diffYears), function(year) { function(col) col - col[Year == year] }) |>
        set_names(str_c("Diff", diffYears)),
      .names = "{.col}_{.fn}"
    )
  )
}

Запуск этого кода жалуется, что не может найти объект Year. Я пытался ввести некоторый NSE, чтобы задержать оценку переменной, но ни !!substitute("Year"), ни !!quo("Year") не дают желаемого результата: он просто выдает ошибку dplyr::mutate_incompatible_size <named_list>. Попытка заменить его на .data[["Year"]] жалуется, что это не в контексте маскировки данных.

Если я жестко запрограммирую годы (как в закомментированном разделе функции), это работает правильно и дает желаемый результат, но не может адаптироваться к динамически предоставляемому списку лет.

Могу попробовать отдельно вытащить колонку Год с помощью data[["Year"]]. Это хорошо работает, если данные не сгруппированы, но не работает, если данные сгруппированы.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Использование cur_data() для доступа к данным текущей группы:

library(dplyr)
library(purrr)
library(stringr)

calculateDifferences <- function(data, diffYears) {
  mutate(data,
         across(
           any_of(c("Mean", "PassR")), 
           map(as.list(diffYears), function(year) { function(col) col - col[cur_data()$Year == year] }) |>
             set_names(str_c("Diff", diffYears)),
           .names = "{.col}_{.fn}"
         )
  )
}
grades <- data.frame(
  Group = c(rep("A", 4), rep("B", 4)),
  Year  = rep(seq(2015, 2018), 2),
  Mean  = c(seq(100, 130, 10), seq(200, 260, 20)),
  PassR = c(seq(0.5, 0.53, 0.01), seq(0.6, 0.66, 0.02))
)

grades |> group_by(Group) |> calculateDifferences(c(2015, 2016))
# A tibble: 8 × 8
# Groups:   Group [2]
  Group  Year  Mean PassR Mean_Diff2015 Mean_Diff2016 PassR_Diff2015 PassR_Diff2016
  <chr> <int> <dbl> <dbl>         <dbl>         <dbl>          <dbl>          <dbl>
1 A      2015   100  0.5              0           -10         0             -0.0100
2 A      2016   110  0.51            10             0         0.0100         0
3 A      2017   120  0.52            20            10         0.0200         0.0100
4 A      2018   130  0.53            30            20         0.0300         0.0200
5 B      2015   200  0.6              0           -20         0             -0.0200
6 B      2016   220  0.62            20             0         0.0200         0
7 B      2017   240  0.64            40            20         0.0400         0.0200
8 B      2018   260  0.66            60            40         0.0600         0.0400

Мне непонятно, почему он может найти cur_data()$Year, но .data[["Year"]] или просто Year.

Отличный! В вашем коде есть pick(Year)$Year вместо cur_data()$Year, но это действительно работает. Я оставлю это открытым на несколько дней, чтобы посмотреть, есть ли другие подходы.

Stephen Morgan 12.04.2023 02:58

Ой - да, либо сработает. cur_data() недавно устарел в пользу pick(), но я думаю, что есть много случаев (например, этот), где cur_data() понятнее.

zephryl 12.04.2023 03:58
Ответ принят как подходящий

Вот альтернативный подход, основанный на возврате таблички внутри и последующей распаковке с использованием аргумента .unpack. Я изменил функцию, чтобы переменные можно было передавать в качестве аргумента, а не жестко запрограммировать (что также позволяет при желании использовать функции tidyselect), а также группировку.

library(purrr)
library(dplyr)

calculateDifferences <- function(data, vars, diffYears, group = Group) {
  data |>
  mutate(
    across({{  vars  }}, ~
             map(diffYears, \(year) 
                 tibble("Diff{year}" := .x - .x[Year == year])
                 ) |>
             list_cbind(),
           .unpack = TRUE),
    .by = {{  group  }}
  )
}

grades |>
  calculateDifferences(c(Mean, PassR), c(2015, 2016)) 

  Group Year Mean PassR Mean_Diff2015 Mean_Diff2016 PassR_Diff2015 PassR_Diff2016
1     A 2015  100  0.50             0           -10           0.00          -0.01
2     A 2016  110  0.51            10             0           0.01           0.00
3     A 2017  120  0.52            20            10           0.02           0.01
4     A 2018  130  0.53            30            20           0.03           0.02
5     B 2015  200  0.60             0           -20           0.00          -0.02
6     B 2016  220  0.62            20             0           0.02           0.00
7     B 2017  240  0.64            40            20           0.04           0.02
8     B 2018  260  0.66            60            40           0.06           0.04

Я выбрал это решение, потому что оно кажется более надежным: здесь pick/cur_group не нужен, и голый год работает.

Stephen Morgan 15.04.2023 10:22

Другие вопросы по теме