Использование dplyr для суммирования и сохранения одинакового имени переменной

Я обнаружил, что data.table и dplyr имеют разные результаты при попытке сделать то же самое. Я хотел бы использовать синтаксис dplyr, но рассчитать его так, как это делает data.table. Вариант использования: я хочу добавить промежуточные итоги в таблицу. Для этого мне нужно выполнить агрегацию для каждой переменной, но затем сохранить те же имена переменных (в преобразованной версии). Data.table позволяет мне выполнять некоторую агрегацию для переменной и сохранять то же имя. Затем выполните еще одну агрегацию с той же переменной. Он будет продолжать использовать нетрансформированную версию. Dplyr, однако, будет использовать преобразованную версию.

В итоговой документации сказано:

# Note that with data frames, newly created summaries immediately
# overwrite existing variables
mtcars %>%
  group_by(cyl) %>%
  summarise(disp = mean(disp), sd = sd(disp))

Это в основном проблема, с которой я сталкиваюсь, но мне интересно, есть ли хороший обходной путь. Одна вещь, которую я обнаружил, состояла в том, чтобы просто назвать преобразованную переменную как-нибудь еще, а затем переименовать ее в конце, но это не очень приятно для меня. Если есть хороший способ сделать промежуточные итоги, это было бы хорошо знать. Я осмотрел этот сайт и не увидел обсуждаемой ситуации. Любая помощь будет принята с благодарностью!

Здесь я привел простой пример, один раз с результатами data.table и один раз с dplyr. Я хочу взять эту простую таблицу и добавить итоговую строку, которая является средневзвешенным значением интересующего столбца (Итого).

library(data.table)
library(dplyr)

dt <- data.table(Group = LETTERS[1:5],
                 Count = c(1000, 1500, 1200, 2000, 5000),
                 Total = c(50, 300, 600, 400, 1000))
dt[, Count_Dist := Count/sum(Count)]
dt[, .(Count_Dist = sum(Count_Dist), Weighted_Total = sum(Count_Dist*Total))]

dt <- rbind(dt[, .(Group, Count_Dist, Total)],
      dt[, .(Group = "All", Count_Dist = sum(Count_Dist), Total = sum(Count_Dist*Total))])
setnames(dt, "Total", "Weighted_Avg_Total")

dt

df <- data.frame(Group = LETTERS[1:5],
                 Count = c(1000, 1500, 1200, 2000, 5000),
                 Total = c(50, 300, 600, 400, 1000))

df %>%
  mutate(Count_Dist = Count/sum(Count)) %>%
  summarize(Count_Dist = sum(Count_Dist),
            Weighted_Total = sum(Count_Dist*Total))

df %>% 
  mutate(Count_Dist = Count/sum(Count)) %>%
  select(Group, Count_Dist, Total) %>% 
  rbind(df %>%
          mutate(Count_Dist = Count/sum(Count)) %>%
          summarize(Group = "All",
                    Count_Dist = sum(Count_Dist),
                    Total = sum(Count_Dist*Total))) %>% 
  rename(Weighted_Avg_Total = Total)

Еще раз спасибо за любую помощь!

2 ответа

Решение

Возможное решение - пропустить mutate шаги и использование transmute для первого mutate/select-шаг и непосредственно рассчитать нужные переменные из исходных переменных без создания промежуточной переменной для второй mutate-ступенно:

df %>% 
  transmute(Group, Count_Dist = Count/sum(Count), Weighted_Avg_Total = Total) %>% 
  bind_rows(df %>%
              summarize(Group = "All",
                        Count_Dist = sum(Count/sum(Count)),
                        Weighted_Avg_Total = sum((Count/sum(Count))*Total)))

который дает:

  Group Count_Dist Weighted_Avg_Total
1     A 0.09345794            50.0000
2     B 0.14018692           300.0000
3     C 0.11214953           600.0000
4     D 0.18691589           400.0000
5     E 0.46728972          1000.0000
6   All 1.00000000           656.0748

Другое возможное решение - изменить порядок, в котором новые переменные вычисляются в dplyr а затем использовать select чтобы вернуть порядок столбцов в то, что вы изначально хотели:

df %>% 
  mutate(Count_Dist = Count/sum(Count)) %>%
  select(Group, Count_Dist, Weighted_Avg_Total = Total) %>% 
  bind_rows(df %>%
              mutate(Count_Dist = Count/sum(Count)) %>%
              summarize(Group = "All",
                        Weighted_Avg_Total = sum(Count_Dist*Total),
                        Count_Dist = sum(Count_Dist)) %>% 
              select(Group, Count_Dist, Weighted_Avg_Total))

Если вы хотите включить Count-колонка, вы могли бы сделать (на основе моего комментария снизу):

df %>% 
  transmute(Group = Group, Count_Dist = Count/sum(Count), Weighted_Avg_Total = Total, Count) %>% 
  bind_rows(df %>%
              summarize(Group = "All",
                        Count_Dist = sum(Count/sum(Count)),
                        Weighted_Avg_Total = sum((Count/sum(Count))*Total),
                        Count = sum(Count)))

Одной из альтернатив может быть использование mutate дважды, чтобы рассчитать даже Weighted_Total и использовать sum из этого столбца в summarize,

df %>%
  mutate(Count_Dist = Count/sum(Count)) %>%
  mutate(Weighted_Total = Count_Dist*Total) %>%
  summarize(Count_Dist = sum(Count_Dist),
            Weighted_Total = sum(Weighted_Total))
Result:
  Count_Dist Weighted_Total
1          1     656.074766

А также:

    df %>% 
      mutate(Count_Dist = Count/sum(Count)) %>%
      select(Group, Count_Dist, Total) %>% 
      rbind(df %>%
              mutate(Count_Dist = Count/sum(Count)) %>%
              mutate(Weighted_Total = Count_Dist*Total) %>%
              summarize(Group = "All",
                        Count_Dist = sum(Count_Dist),
                        Total = sum(Weighted_Total))) %>% 
      rename(Weighted_Avg_Total = Total)

Result:

      Group   Count_Dist Weighted_Avg_Total
    1     A 0.0934579439          50.000000
    2     B 0.1401869159         300.000000
    3     C 0.1121495327         600.000000
    4     D 0.1869158879         400.000000
    5     E 0.4672897196        1000.000000
    6   All 1.0000000000         656.074766
Другие вопросы по тегам