Reshape2: несколько результатов функции агрегации?

Из того, что я прочитал, * операции литья в reshape2 потеряли свою функцию result_variable. Хэдли намекает на использование plyr для этой цели (добавление нескольких столбцов результата во входной кадр данных). Как я могу реализовать пример документации...

aqm <- melt(airquality, id=c("month", "day"), na.rm=TRUE)
cast(aqm, month ~ variable + result_variable, range)

с помощью reshape2 (dcast) и plyr (ddply)?

Ответ 1

Этот вопрос имеет несколько ответов из-за гибкости пакетов 'reshape2' и 'plyr'. Я покажу один из самых простых примеров, чтобы понять здесь:

library(reshape2)
library(plyr)

aqm <- melt(airquality, id=c("Month", "Day"), na.rm=TRUE)
aqm_ply <- ddply(aqm, .(Month, variable), summarize, min=min(value), max=max(value))
aqm_melt <- melt(aqm_ply, id=c("Month", "variable"), variable.name="variable2")
dcast(aqm_melt, Month ~ variable + variable2)

#   Month Ozone_min Ozone_max Solar.R_min Solar.R_max Wind_min Wind_max Temp_min  Temp_max
# 1     5         1       115           8         334      5.7     20.1       56        81
# 2     6        12        71          31         332      1.7     20.7       65        93
# 3     7         7       135           7         314      4.1     14.9       73        92
# 4     8         9       168          24         273      2.3     15.5       72        97
# 5     9         7        96          14         259      2.8     16.6       63        93

Шаг 1. Позвольте разбить его на шаги. Во-первых, оставьте определение "aqm" самостоятельно и работайте с расплавленными данными. Это облегчит понимание примера.

aqm <- melt(airquality, id=c("Month", "Day"), na.rm=TRUE)

#     Month Day variable value
# 1       5   1    Ozone  41.0
# 2       5   2    Ozone  36.0
# 3       5   3    Ozone  12.0
# 4       5   4    Ozone  18.0
# ...
# 612     9  30     Temp  68.0

Шаг 2. Теперь мы хотим заменить столбец "значение" столбцами "min" и "max". Мы можем выполнить это с помощью функции "ddply" из пакета "plyr". Для этого мы используем функцию "ddply" (кадр данных как входной сигнал, кадр данных в качестве выходного сигнала, следовательно, "dd" -слойный). Сначала указываем данные.

ddply(aqm,

И затем мы укажем переменные, которые хотим использовать для группировки наших данных, "Месяц" и "переменная". Мы используем функцию ., чтобы ссылаться на эти переменные напрямую, а не ссылаться на значения, которые они содержат.

ddply(aqm, .(Month, variable),

Теперь нам нужно выбрать агрегирующую функцию. Здесь мы выбираем функцию summarize, потому что у нас есть столбцы ( "День" и "значение" ), которые мы не хотим включать в наши окончательные данные. Функция summarize удалит все исходные, негрупповые столбцы.

ddply(aqm, .(Month, variable), summarize,

Наконец, мы укажем расчет для каждой группы. Мы можем ссылаться на столбцы исходного фрейма данных ( "aqm" ), даже если они не будут содержаться в нашем конечном кадре данных. Вот как это выглядит:

aqm_ply <- ddply(aqm, .(Month, variable), summarize, min=min(value), max=max(value))

#    Month variable  min   max
# 1      5    Ozone  1.0 115.0
# 2      5  Solar.R  8.0 334.0
# 3      5     Wind  5.7  20.1
# 4      5     Temp 56.0  81.0
# 5      6    Ozone 12.0  71.0
# 6      6  Solar.R 31.0 332.0
# 7      6     Wind  1.7  20.7
# 8      6     Temp 65.0  93.0
# 9      7    Ozone  7.0 135.0
# 10     7  Solar.R  7.0 314.0
# 11     7     Wind  4.1  14.9
# 12     7     Temp 73.0  92.0
# 13     8    Ozone  9.0 168.0
# 14     8  Solar.R 24.0 273.0
# 15     8     Wind  2.3  15.5
# 16     8     Temp 72.0  97.0
# 17     9    Ozone  7.0  96.0
# 18     9  Solar.R 14.0 259.0
# 19     9     Wind  2.8  16.6
# 20     9     Temp 63.0  93.0

Шаг 3. Мы видим, что данные значительно уменьшены, так как функция ddply агрегировала строки. Теперь нам нужно снова расплавить данные, чтобы мы могли получить вторую переменную для окончательного кадра данных. Обратите внимание, что нам нужно указать новый аргумент variable.name, поэтому у нас нет двух столбцов с именем "variable".

aqm_melt <- melt(aqm_ply, id=c("Month", "variable"), variable.name="variable2")

    #    Month variable variable2 value
# 1      5    Ozone       min   1.0
# 2      5  Solar.R       min   8.0
# 3      5     Wind       min   5.7
# 4      5     Temp       min  56.0
# 5      6    Ozone       min  12.0
# ...
# 37     9    Ozone       max  96.0
# 38     9  Solar.R       max 259.0
# 39     9     Wind       max  16.6
# 40     9     Temp       max  93.0

Шаг 4. И мы можем, наконец, обернуть все это, введя наши данные в окончательную форму.

dcast(aqm_melt, Month ~ variable + variable2)

#   Month Ozone_min Ozone_max Solar.R_min Solar.R_max Wind_min Wind_max Temp_min  Temp_max
# 1     5         1       115           8         334      5.7     20.1       56        81
# 2     6        12        71          31         332      1.7     20.7       65        93
# 3     7         7       135           7         314      4.1     14.9       73        92
# 4     8         9       168          24         273      2.3     15.5       72        97
# 5     9         7        96          14         259      2.8     16.6       63        93

Надеюсь, этот пример даст вам достаточно понимания, чтобы вы начали. Имейте в виду, что новая версия пакета plyr, оптимизированная для фреймов, активно разрабатывается под названием "dplyr", поэтому вы можете захотеть быть готовым преобразовать свой код в новый пакет после того, как он станет полностью полноценным.

Ответ 2

Я думаю, что другие ответы должны были быть рассмотрены с точки зрения использования "plyr" или "dplyr" (и я бы посоветовал вам продолжить поиск в этом направлении).

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

dcastMult <- function(data, formula, value.var = "value", 
                   funs = list("min" = min, "max" = max)) {
  require(reshape2)
  if (is.null(names(funs)) | any(names(funs) == "")) stop("funs must be named")
  Form <- formula(formula)
  LHS <- as.character(Form[[2]])
  if (length(LHS) > 1) LHS <- LHS[-1]
  temp <- lapply(seq_along(funs), function(Z) {
    T1 <- dcast(data, Form, value.var = value.var, 
                fun.aggregate=match.fun(funs[[Z]]), fill = 0)
    Names <- !names(T1) %in% LHS
    names(T1)[Names] <- paste(names(T1)[Names], names(funs)[[Z]], sep = "_")
    T1
  })
  Reduce(function(x, y) merge(x, y), temp)
}

Похоже, что это путаница, но результат заключается в том, что вы можете придерживаться того же синтаксиса, с которым вы знакомы, при использовании нескольких функций агрегации. "Имена" для аргумента funs используются как суффиксы в результирующих именах. Анонимные функции могут быть указаны как ожидалось, например maxSq = function(x) max(x)^2.

dcastMult(aqm, month ~ variable, value.var="value",
       funs = list("min" = min, "max" = max))
#   month ozone_min solar.r_min wind_min temp_min ozone_max solar.r_max wind_max temp_max
# 1     5         1           8      5.7       56       115         334     20.1       81
# 2     6        12          31      1.7       65        71         332     20.7       93
# 3     7         7           7      4.1       73       135         314     14.9       92
# 4     8         9          24      2.3       72       168         273     15.5       97
# 5     9         7          14      2.8       63        96         259     16.6       93

Ответ 3

Вот решение dplyr, использующее удивительную функцию %>%. Он также использует базовую функцию reshape, которая часто недоиспользуется (IMHO). Код является самоочевидным.

library(dplyr)
airquality %>%
  melt(c('Month', 'Day')) %>%
  group_by(Month, variable) %>%
  summarise(min = min(value, na.rm = T), max = max(value, na.rm = T)) %>%
  reshape(timevar = 'variable', idvar = 'Month', direction = 'wide') %>%
  arrange(Month)

Ответ 4

С недавний коммит в версия разработки data.table v1.9.5, мы можем использовать несколько столбцов value.var одновременно (а также использовать несколько функций агрегации в fun.aggregate). Подробнее см. ?dcast, а также раздел примеров.

Здесь мы можем использовать dcast:

require(data.table) # v1.9.5+
dt = as.data.table(airquality)
valvars = c("Ozone", "Solar.R", "Wind", "Temp")
dcast(dt, Month ~ ., fun=list(min, max), na.rm=TRUE, value.var=valvars)
#    Month ._min_Ozone ._min_Solar.R ._min_Wind ._min_Temp ._max_Ozone ._max_Solar.R ._max_Wind
# 1:     5           1             8        5.7         56         115           334       20.1
# 2:     6          12            31        1.7         65          71           332       20.7
# 3:     7           7             7        4.1         73         135           314       14.9
# 4:     8           9            24        2.3         72         168           273       15.5
# 5:     9           7            14        2.8         63          96           259       16.6
#    ._max_Temp
# 1:         81
# 2:         93
# 3:         92
# 4:         97
# 5:         93

Вы можете смело игнорировать предупреждения.