Вычисление значения для уникальных записей столбца с использованием dplyr

Я не смог найти аналогичный вопрос в SO. Я совсем новичок в dplyr в целом.

Рассмотрим игрушечный фрейм данных некоторых статистических данных о продажах.

df <- data.frame(
  added = c("2020-10-05", "2020-10-30", "2020-11-04", "2020-12-10", "2020-12-14"),
  closed = c("", "2020-11-05", "2020-12-10", "", ""),
  value = c(100, 200, 300, 400, 500),
  stage = c("Quote", "Won", "Lost", "Quote", "Quote")
)

Он содержит дату сделки added, когда она была closed, сделку value и текущую stage сделки. Если сделка не выиграна или не проиграна, у нее нет closed даты. Я хочу рассчитать значение конвейера, то есть стоимость сделок, которые еще не выиграны или не проиграны, для каждой уникальной записи в столбцах added и closed (поскольку это единственные моменты, когда значение конвейера может измениться).

Я заставил его работать через смесь base R и dplyr. Сначала я создаю фрейм данных дат:

date_df <- as.data.frame(seq(as.Date("2020-10-01"),as.Date(Sys.Date()),1))
colnames(date_df) <- c("date")

Мой выбор даты начала произволен, если она предшествует первой дате в столбце added. Затем я оцениваю это выражение для каждой записи в date_df через цикл:

library(tidyverse)
pipeline <- c()
for (i in 1:nrow(date_df)) {
    pipeline <-
        df %>%
        filter(
            (added <= date_df$date[i] & closed > date_df$date[i] & closed < Sys.Date()) |
            (added <= date_df$date[i] & stage != "Won" & stage != "Lost")
        ) %>%
        summarise(pipeline = sum(value))
    date_df$pipeline[i] <- pipeline
}

Что правильно дает мне:

> date_df
         date pipeline
1  2020-10-01        0
2  2020-10-02        0
3  2020-10-03        0
4  2020-10-04        0
5  2020-10-05      100
6  2020-10-06      100
7  2020-10-07      100
8  2020-10-08      100
9  2020-10-09      100
10 2020-10-10      100
11 2020-10-11      100
12 2020-10-12      100
13 2020-10-13      100
14 2020-10-14      100
15 2020-10-15      100
16 2020-10-16      100
17 2020-10-17      100
18 2020-10-18      100
19 2020-10-19      100
20 2020-10-20      100
21 2020-10-21      100
22 2020-10-22      100
23 2020-10-23      100
24 2020-10-24      100
25 2020-10-25      100
26 2020-10-26      100
27 2020-10-27      100
28 2020-10-28      100
29 2020-10-29      100
30 2020-10-30      300
31 2020-10-31      300
32 2020-11-01      300
33 2020-11-02      300
34 2020-11-03      300
35 2020-11-04      600
36 2020-11-05      400
37 2020-11-06      400
38 2020-11-07      400
39 2020-11-08      400
40 2020-11-09      400
41 2020-11-10      400
42 2020-11-11      400
43 2020-11-12      400
44 2020-11-13      400
45 2020-11-14      400
46 2020-11-15      400
47 2020-11-16      400
48 2020-11-17      400
49 2020-11-18      400
50 2020-11-19      400
51 2020-11-20      400
52 2020-11-21      400
53 2020-11-22      400
54 2020-11-23      400
55 2020-11-24      400
56 2020-11-25      400
57 2020-11-26      400
58 2020-11-27      400
59 2020-11-28      400
60 2020-11-29      400
61 2020-11-30      400
62 2020-12-01      400
63 2020-12-02      400
64 2020-12-03      400
65 2020-12-04      400
66 2020-12-05      400
67 2020-12-06      400
68 2020-12-07      400
69 2020-12-08      400
70 2020-12-09      400
71 2020-12-10      500
72 2020-12-11      500
73 2020-12-12      500
74 2020-12-13      500
75 2020-12-14     1000
76 2020-12-15     1000
77 2020-12-16     1000
78 2020-12-17     1000
79 2020-12-18     1000
80 2020-12-19     1000
81 2020-12-20     1000
82 2020-12-21     1000

По сути, я суммирую стоимость открытых сделок на каждую дату. Однако мне кажется, что это очень неэффективный подход. Реальный набор данных достаточно обширен, и для оценки на каждую дату требуется довольно много времени.

Меня действительно интересует только выражение, которое будет оцениваться для каждой уникальной записи в added и closed, как описано выше. Существуют ли какие-либо элегантные решения с использованием dplyr, которые достигают этого (предпочтительно) без создания нового объекта? Идея состоит в том, чтобы позже построить его и отправить в блестящее приложение, поэтому сокращение времени вычислений может иметь решающее значение.

Заранее спасибо!

РЕДАКТИРОВАТЬ

Чтобы показать пример для двух дат 2020-11-04 и 2020-11-05:

На 2020-11-04 три сделки (запись 1-3 в данных) не относятся ни к Lost, ни к Won, поэтому значение конвейера на эту дату равно 600.

На 2020-11-05 одна сделка теперь Lost (вторая запись в данных), а стоимость пайплайна падает до 400.

Таким образом, при закрытии сделки стоимость конвейера уменьшается, а при добавлении сделки стоимость конвейера увеличивается.

Значения для всех дат добавляются к исходному вопросу.

Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
0
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не понимаю, почему вы используете closed < Sys.Date() есть ли у вас будущие закрытые сделки, если это так, этот код будет учитывать это:

df %>%
 mutate(across(closed:added, lubridate::ymd), cl = closed < Sys.Date()) %>%
  pivot_longer(c(closed, added), values_to = "date") %>%
   filter(!is.na(date)) %>%
    group_by(date) %>%
     summarise(pipeline=sum((stage= = "Quote" | cl) *value)) %>%
      mutate(pipeline=cumsum(pipeline)) 
# A tibble: 7 x 2
  date       summed
  <date>      <dbl>
1 2020-10-05    100
2 2020-11-05    300
3 2020-10-30    500
4 2020-12-10   1200
5 2020-11-04   1500
6 2020-12-10   2200
7 2020-12-14   2700

Это не было ясно, но будущие закрытые сделки действительно возможны, поэтому closed < Sys.Date(). Представленный здесь результат неверен. Например, значение для 2020-11-04 должно быть 600, а для 2020-11-05 должно быть 400.

thesixmax 21.12.2020 10:00

Пожалуйста, также смотрите мое редактирование с разъяснениями в исходном сообщении.

thesixmax 21.12.2020 10:12
Ответ принят как подходящий

Такой подход также будет полезен

result <- dff %>% mutate(id = row_number(),
                         added = as.Date(added),
                         closed = as.Date(closed)) %>%
  pivot_longer(cols = c("added", "closed"), names_to = "activity", values_to = "dates") %>%
  mutate(activity = factor(activity, levels = c("added", "closed"), ordered = T)) %>%
  arrange(dates, activity) %>%
  mutate(val = cumsum(value*case_when(activity == "added" ~ 1,
                                      activity == "closed" ~ -1,
                                      TRUE ~ 0))) %>%
  group_by(dates) %>% summarise(val = min(val))

# A tibble: 7 x 2
  dates        val
  <date>     <dbl>
1 2020-10-05   100
2 2020-10-30   300
3 2020-11-04   600
4 2020-11-05   400
5 2020-12-10   500
6 2020-12-14  1000
7 NA             0

Dput(dff) используется

> dput(dff)
structure(list(added = structure(c(18540, 18565, 18570, 18606, 
18610), class = "Date"), closed = structure(c(NA, 18571, 18606, 
NA, NA), class = "Date"), value = c(100, 200, 300, 400, 500), 
    stage = c("Quote", "Won", "Lost", "Quote", "Quote")), row.names = c(NA, 
-5L), class = "data.frame")

Если вы хотите отобразить результаты, используйте complete и fill как

plot <- result %>% filter(!is.na(dates))  %>%
  complete(dates = seq.Date(min(dates), max(dates), by = "day")) %>%
  fill(val) %>%
  ggplot() +
  geom_line(aes(x = dates, y = val))

plot

Очень аккуратный! Как мне получить двойное значение для 2020-12-10 ? Конечным результатом будет 500, но ваше решение содержит две записи для этой даты. Кроме того, я был бы признателен, если бы вы добавили mutate(dates = as.Date(dates)) %>% после pivot_longer, чтобы можно было использовать исходный df.

thesixmax 21.12.2020 11:07

Это выглядит хорошо! Для полноты картины я бы удалил select(dates, val) %>%, так как это лишнее, и заменил бы summarise(val = last(val)) на summarise(val = min(val)) для облегчения интерпретации. Отмечено как решение.

thesixmax 21.12.2020 11:27

Другие вопросы по теме