вычислительные различия между группами: альтернатива распространению для нескольких вычислений - PullRequest
0 голосов
/ 10 января 2019

Мне обычно нужно вычислять различия между группами, вложенными в некоторый интервал и / или дополнительную группировку. Для вычисления одной переменной это легко сделать с помощью spread и mutate. Вот воспроизводимый пример с набором данных ChickWeight; не отвлекайтесь на сами расчеты (это всего лишь игрушечный пример), мой вопрос о том, как обращаться с набором данных, структурированным как датафрейм ChickSum, созданный ниже.

# reproducible dataset
data(ChickWeight)
ChickSum = ChickWeight %>% 
  filter(Time == max(Time) | Time == min(Time)) %>%
  group_by(Diet, Time) %>% 
  summarize(mean.weight = mean(weight)) %>%
  ungroup()

Вот как я могу рассчитать изменение среднего веса цыплят между первым и последним разом, разделенным по рациону:

# Compute change in mean weight between first and last time
ChickSum %>%
  spread(Time, mean.weight) %>%
  mutate(weight.change = `21` - `0`)

Однако, это не так хорошо работает с несколькими переменными:

ChickSum2 = ChickWeight %>% 
  filter(Time == max(Time) | Time == min(Time)) %>%
  group_by(Diet, Time) %>% 
  # now also compute variable "count"
  summarize(count = n(), mean.weight = mean(weight)) %>%
  ungroup()

Я не могу распространяться на Time и на оба count и mean.weight; Мое текущее решение состоит в том, чтобы сделать две spread - mutate операции - один раз для count и снова для mean.weight ---, а затем join результатов.

ChickCountChange = ChickSum2 %>%
  select(-mean.weight) %>%
  spread(Time, count) %>%
  mutate(count.change = `21` - `0`)
ChickWeightChange = ChickSum2 %>%
  select(-count) %>%
  spread(Time, mean.weight) %>%
  mutate(weight.change = `21` - `0`)

full_join(
  select(ChickWeightChange, Diet, weight.change), 
  select(ChickCountChange, Diet, count.change), 
  by = "Diet")

Есть ли другой подход к этим типам вычислений? Я пытался придумать стратегию, которая сочетает group_by и purrr::pmap, чтобы избежать spread, но при этом сохранить преимущества описанного выше подхода (например, аргумент spread fill для выбора способа обработки пропущенных групповых комбинаций), но я не понял этого. Я открыт для предложений или альтернативных структур данных / способов мышления о проблеме.

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Итак, я нашел потенциальное / частичное решение в процессе написания воспроизводимого примера. По сути, мы используем gather для группировки по самим переменным:

ChickSum2 %>% 
  gather(variable, value, count, mean.weight) %>% 
  spread(Time, value) %>% mutate(Change = `21` - `0`) %>% 
  select(Diet, variable, Change) %>% 
  spread(variable, Change)

Это работает, только если выполняются следующие два условия:

  1. Все переменные одного типа (например, mean.weight и count являются числовыми).
  2. Расчет разности одинаков для всех переменных (например, я хочу вычислить last - first для всех переменных).

Я полагаю, что второе условие можно ослабить, например, используя case_when.

0 голосов
/ 10 января 2019

Вы можете попробовать перегруппировать, а затем использовать lag() для расчета различий. Работает для вашего игрушечного примера, но может быть лучше увидеть некоторые из ваших реальных наборов данных:

ChickWeight %>% 
  filter(Time == max(Time) | Time == min(Time)) %>%
  group_by(Diet, Time) %>% 
  # now also compute variable "count"
  summarize(count = n(), mean.weight = mean(weight)) %>%
  ungroup() %>% 
  group_by(Diet) %>% 
  mutate(count.change = count - lag(count), 
         weight.change = mean.weight - lag(mean.weight)) %>% 
  filter(Time == max(Time))

Результат:

  Diet   Time count mean.weight count.change weight.change
  <fct> <dbl> <int>       <dbl>        <int>         <dbl>
1 1        21    16        178.           -4          136.
2 2        21    10        215.            0          174 
3 3        21    10        270.            0          230.
4 4        21     9        239.           -1          198.
...