Условное агрегирование строк с использованием «или» в data.table - PullRequest
2 голосов
/ 20 марта 2020

У меня достаточно большая (3 миллиона строк) таблица данных, содержащая счета и платежи от многих клиентов, где у каждого клиента есть ряд (уникальных) документов, каждый из которых, в свою очередь, имеет дату создания и дату, когда документ оплачивается. Если дата платежа еще не определена, в столбце payment_date указан NA. Данные выглядят примерно так:

dt = data.table(
  customer_id = c(rep(1,4), rep(2,4)),
  document_id = c(1:8),
  creation_date = as.Date(c("2005-03-01", "2005-03-03", "2005-03-10", "2005-03-25", "2006-03-01", "2006-03-04", "2006-03-10", "2006-03-12"), "%Y-%m-%d"),
  payment_date = as.Date(c("2005-03-05", "2005-03-07", NA, "2005-03-28", "2006-03-05", NA, "2006-03-15", "2006-03-16"), "%Y-%m-%d"),
  open_docs_10 = c(0,1,2,1,0,1,2,3),
  percentage_open_10 = c(0.0,0.20,0.70,1.0,0.0,0.3,1.0,1.0)
)

Для каждого документа (т. Е. Для строки) я sh рассчитываю (в идеале) две функции:

1) Open_docs_10, то есть количество неоплаченных или «открытых» документов, которые customer_id текущего документа имели в определенном временном окне (скажем, 10 дней) до даты создания document_id. «Открыть» означает, что значение payment_date равно NA, попадает после или попадает во временной интервал, в то время как creation_date находится внутри или до временного интервала.

2) Percentage_open_10, который представляет собой процентное число дней в Время windows у клиента были открытые документы. Количество документов на самом деле не имеет значения; На рисунке показано что-то вроде: «4 из 10 предыдущих дней, когда у этого клиента были открытые платежи, когда был создан этот новый документ».

For 1), я попробовал что-то вроде:


open_docs_10 = dt[,c("customer_id", "document_id", "creation_date", "payment_date")] %>% 
  .[, open_docs_10 := .[.(customer_id = customer_id, upper = creation_date, lower = creation_date - days(10)), 
                       on = .(customer_id, payment_date >= lower, creation_date > lower), uniqueN(document_id), by=.EACHI
                       ]$V1
    ]

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

payment_date >= lower OR upper >= creation_date >= lower

Кажется, я не могу использовать и / или операторы внутри " на "пункт. Но как мне этого добиться, используя data.table?

Для 2), я понятия не имею, как решить эту проблему.

Я не связан использованием data.table в реальном смысле; может быть, я пытаюсь решить мою проблему трудным способом, когда другой пакет R дал бы намного более умный способ обработки вещей? Любая помощь будет принята с благодарностью!

1 Ответ

1 голос
/ 20 марта 2020

Я думаю, что вы не всегда включаете или исключаете даты окончания в свои расчеты percentage_open_10. Если мы включаем даты окончания, вы можете использовать следующее:

ndays <- 10L
setnafill(dt, fill=as.IDate("9999-12-31"), cols="payment_date")

dt[, cd10 := creation_date - ndays + 1L]

dt[, c("open_docs_10", "percentage_open_10") := 
    .SD[.SD, on=.(customer_id, creation_date<=creation_date, payment_date>=cd10), 
        allow.cartesian=TRUE, by=.EACHI, {
        ix <- x.document_id != i.document_id
        p <- 0
        if (any(ix)) {
            lastd <- min(c(i.creation_date, max(x.payment_date[ix]))) 
            firstd <- if (any(ix)) max(c(i.cd10, min(x.creation_date[ix]))) 
            p <- (lastd - firstd + 1) / 10
        }
        .(.N - 1L, p)
    }][, (1L:3L) := NULL]
]

output:

   customer_id document_id creation_date payment_date       cd10 open_docs_10 percentage_open_10
1:           1           1    2005-03-01   2005-03-05 2005-02-20            0                0.0
2:           1           2    2005-03-03   2005-03-07 2005-02-22            1                0.3
3:           1           3    2005-03-10   9999-12-31 2005-03-01            2                0.7
4:           1           4    2005-03-25   2005-03-28 2005-03-16            1                1.0
5:           2           5    2006-03-01   2006-03-05 2006-02-20            0                0.0
6:           2           6    2006-03-04   9999-12-31 2006-02-23            1                0.4
7:           2           7    2006-03-10   2006-03-15 2006-03-01            2                1.0
8:           2           8    2006-03-12   2006-03-16 2006-03-03            3                1.0

Однако, с 3 миллионами строк, я не надеюсь, что это может быть выполнено в несколько секунд.

...