Вычитание строк суммами других строк на основе условия

#r

#r

Вопрос:

У меня есть следующий фрейм данных:

 a = data.frame("ID"=c(1,1,1,1,1,1,5,5,5,5),
                "Year"=c(2016,2017,2016,2017,2016,2017,2017,2017,2016,2017),
                "Quarter"=c(1,1,2,2,3,3,1,2,3,3),
                "Revenue" = c(100,300,200,600,500,700,200,300,800,900))


   ID Year Quarter Revenue
1   1 2016       1     100
2   1 2017       1     300
3   1 2016       2     200
4   1 2017       2     600
5   1 2016       3     500
6   1 2017       3     700
7   5 2017       1     200
8   5 2017       2     300
9   5 2016       3     800
10  5 2017       3     900
 

Я хочу вычесть доходы предыдущих кварталов за каждый квартал для каждого идентификатора. Доход за 2 квартала вычитается из дохода за 1 квартал, доход за 3 квартала вычитается из суммы 1 и 2 кварталов и т. Д.. Так что это будет означать, например, для ID = 1, year = 2016:

2016 Q1 = 100, 2016 Q2 = 100 (200-100), 2016 Q3 = 300 (500-100-100).

Есть также некоторые идентификаторы, у которых нет данных за некоторые кварталы, например ID = 5, year = 2016, у которого есть данные только за 3. квартал, поэтому можно вычесть воображаемые 0, и мы получим 2016 Q3 = 800 (800-0-0).

Желаемый результат должен выглядеть следующим образом:

    ID Year Quarter Revenue
1   1 2016       1     100
2   1 2017       1     300
3   1 2016       2     100
4   1 2017       2     300
5   1 2016       3     300
6   1 2017       3     100
7   5 2017       1     200
8   5 2017       2     100
9   5 2016       3     800
10  5 2017       3     600
 

Есть ли простой способ сделать это или мне нужно написать функцию для этого?

Ответ №1:

 library(tidyverse)

a %>%
   left_join(mutate(a, Quarter = Quarter   1),
             by= c("ID", "Year", "Quarter"))%>%
   mutate(Revenue = Revenue.x - coalesce(Revenue.y,0)) %>%
   select(-contains("."))
   ID Year Quarter Revenue
1   1 2016       1     100
2   1 2017       1     300
3   1 2016       2     100
4   1 2017       2     300
5   1 2016       3     300
6   1 2017       3     100
7   5 2017       1     200
8   5 2017       2     100
9   5 2016       3     800
10  5 2017       3     600
 

Комментарии:

1. Я попытался с помощью %>% упорядочить (идентификатор, год, квартал) %>% group_by(идентификатор, год) %>% мутировать(Доход = case_when(row_number() == 1 ~ Доход, row_number() == 2 ~ Доход [2] — Доход [1], TRUE~ Доход [3] — Доход [2] — Доход [1])) Раньше я использовал этот подход с векторным индексом, но здесь он не работает, по умолчанию не группируется по значениям упорядочивания. Просто чтобы убедиться, я даже добавил функцию упорядочивания, но она все равно не работает. Можете ли вы, пожалуйста, помочь мне понять, почему.

2. @KarthikS Во-первых, case_when в этом случае вы не можете. Во-вторых, arrange это никоим образом не помогает. Вероятно, вы думаете group_by . Наконец, что вы пытаетесь сделать? Я не понимаю, что вы делаете

3. Что я пытаюсь сделать, так это то, что если мы упорядочим фрейм данных, используя %>% arrange(ID, год, квартал), у нас будет доход за каждый год с 1 по 3 кварталы. С помощью, case_whenкогда я попытался сделать вычитание первого квартала из второго, а первого и второго кварталов из третьего на основе номеров строк. Не могли бы вы сообщить мне, почему case_when не будет работать в таких случаях.

4. @KarthikS Не удовлетворяет ли решение, которое я предоставил выше? Обратите внимание, что у вас могут быть данные для кварталов 3 и 1 без квартала 2. согласно договоренности, вы вычтете 3 и 1, но вы не должны этого делать. Просто попытайтесь понять предоставленное решение.

Ответ №2:

Вы можете использовать purrr accumulate функцию ‘s для выполнения такого рекурсивного вычисления.

 library(dplyr)
a %>%
  group_by(ID, Year) %>%
  mutate(Revenue = Revenue - cumsum(purrr::accumulate(lag(Revenue)[-1], 
                                    ~.y - .x, .init = 0)))

#      ID  Year Quarter Revenue
#   <dbl> <dbl>   <dbl>   <dbl>
# 1     1  2016       1     100
# 2     1  2017       1     300
# 3     1  2016       2     100
# 4     1  2017       2     300
# 5     1  2016       3     300
# 6     1  2017       3     100
# 7     5  2017       1     200
# 8     5  2017       2     100
# 9     5  2016       3     800
#10     5  2017       3     600
 

Ответ №3:

Вы можете сделать это в data.table .

 library(data.table)

setDT(a)

a[,Revenue:= Revenue-shift(Revenue,type = 'lag',fill = 0), by= c('ID','Year')]