Создание нового среднего столбца в цикле с помощью настраиваемой функции

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

Я могу воспроизвести только результат решения, но только для однофакторной переменной A.

   df <- data.frame(
      target = c(1, 4, 8, 9, 2, 1, 3, 5, 7, 1),
      A = c("A", "Z", "N", "A", "Z"),
      B = c("B", "Q", "G", "B", "T"),
      C = c("C", "Y", "C", "P", "Y")
    )

grouped_mean <- function(data, summary_var, ...) {
  summary_var <- enquo(summary_var)

  data %>%
    # Selects only factor data types and a target column
    select(which(map_chr(., class) == "factor"), !!summary_var) %>%
    group_by(...) %>%
    # Over here I am not able to change column name, so that it yields Mean_A, Mean_B and Mean_C
    mutate(mean = mean(!!summary_var)) %>%
    ungroup()
}

grouped_mean(data = df, 
             group_var = A, 
             summary_var = target)

Я попробовал зациклить:

map_df(df, grouped_mean(data = df, summary_var = target))

Но я получаю такую ​​ошибку:

Ошибка: не удается преобразовать объект tbl_df/tbl/data.frame в функцию

Вопросы и ответы:

  1. Я не уверен, как создать функцию, которая динамически изменяет имя в функции изменения, с имени mean на mean_A, mean_B и mean_c
  2. Я пробовал функцию map_df, чтобы зациклить каждый элемент df, но безуспешно. Идея состоит в том, чтобы создать новые столбцы, которые являются средствами целевой функции.

person Loncar    schedule 04.03.2020    source источник


Ответы (1)


Вот немного необычное решение, но оно должно сработать для вас (при условии, что вы согласны с указанием target в качестве столбца, для которого вы хотите получить среднее значение). Это просто использует mutate_if() и использует подмножество с tapply(), чтобы получить ваши средства.

Затем он использует rename_at() для изменения имен, чтобы они соответствовали желаемому результату. Если вы хотите, чтобы он был в нижнем регистре, вы можете заключить gsub() в tolower()

df %>%
  mutate_if(is.factor, list(Mean = ~tapply(df$target, ., mean)[.])) %>%
  rename_at(vars(ends_with("Mean")), ~gsub("(.*?)_(.*)", "\\2_\\1", .))

   target A B C Mean_A Mean_B Mean_C
1       1 A B C    4.5    4.5   3.75
2       4 Z Q Y    2.5    3.5   2.50
3       8 N G C    6.5    6.5   3.75
4       9 A B P    4.5    4.5   8.00
5       2 Z T Y    2.5    1.5   2.50
6       1 A B C    4.5    4.5   3.75
7       3 Z Q Y    2.5    3.5   2.50
8       5 N G C    6.5    6.5   3.75
9       7 A B P    4.5    4.5   8.00
10      1 Z T Y    2.5    1.5   2.50
person Andrew    schedule 04.03.2020
comment
Спасибо, Эндрю. Очень интересное решение! Точка в функции tapply, обозначающая индекс, что именно делает индекс в этом случае? Кроме того, в конце концов, что это делает: [.]? - person Loncar; 04.03.2020
comment
Это заполнитель для столбца в анонимной функции (обозначен ~ в синтаксисе tidyverse). Итак, он перебирает столбцы, которые удовлетворяют условию is.factor. Скобки разделяют средства из tapply() по имени. Будет немного понятнее, если вы запустите tapply(df$target, df$A, mean), а затем tapply(df$target, df$A, mean)[df$A]. Думайте о . как о function(.) mean(.) с lapply(). Вы также можете использовать .x, чтобы быть более явным. Это поможет прояснить ситуацию? Если нет, я могу попытаться найти ресурсы, объясняющие это более подробно. - person Andrew; 04.03.2020
comment
Хорошо объяснено. Спасибо за решение, и если вы найдете интересные ресурсы, поделитесь, пожалуйста. - person Loncar; 04.03.2020
comment
Я посмотрю и дам вам знать - я знаю, что они называются лямбда-функциями, но я думаю, что это не зависит от языка. Там может быть что-то конкретное, связанное с tidyverse. Отправлю, если найду! - person Andrew; 04.03.2020