2017-01-06 6 views
2

У меня есть следующий набор данных:R: вычислить разницу во времени между конкретными событиями

df = data.frame(cbind(user_id = c(rep(1, 4), rep(2,4)), 
        complete_order = c(rep(c(1,0,0,1), 2)), 
        order_date = c('2015-01-28', '2015-01-31', '2015-02-08', '2015-02-23', '2015-01-25', '2015-01-28', '2015-02-06', '2015-02-21'))) 

library(lubridate) 
df$order_date = as_date(df$order_date) 

user_id complete_order order_date 
     1    1 2015-01-28 
     1    0 2015-01-31 
     1    0 2015-02-08 
     1    1 2015-02-23 
     2    1 2015-01-25 
     2    0 2015-01-28 
     2    0 2015-02-06 
     2    1 2015-02-21 

Я пытаюсь вычислить разницу в днях между только завершенных заказов для каждого пользователя. Желательный результат будет выглядеть следующим образом:

user_id complete_order order_date complete_order_time_diff 
<fctr>   <fctr>  <date>    <time> 
    1    1 2015-01-28    NA days 
    1    0 2015-01-31    3 days 
    1    0 2015-02-08    11 days 
    1    1 2015-02-23    26 days 
    2    1 2015-01-25    NA days 
    2    0 2015-01-28    3 days 
    2    0 2015-02-06    12 days 
    2    1 2015-02-21    27 days 

, когда я пытаюсь это решение:

library(dplyr) 

df %>% 
group_by(user_id) %>% 
mutate(complete_order_time_diff = order_date[complete_order==1]-lag(order_date[complete_order==1)) 

возвращает ошибку:

Error: incompatible size (3), expecting 4 (the group size) or 1

Любая помощь с этим будет большим, Спасибо!

+0

Похоже, что вы можете попробовать применить функцию типа 'ff = function (complete, date) date date [c (NA, cummax (complete * seq_along (complete)) [- length (complete)])] 'by_ user_id ', где« complete_order »и« order_date »передаются соответственно« complete »и« date ». –

ответ

2

Кажется, что вы ищете расстояние от каждого заказа от последнего завершенного. Имея бинарный вектор, x, c(NA, cummax(x * seq_along(x))[-length(x)]) дает индексы последнего «1», видимого перед каждым элементом. Затем вычитание каждого элемента «order_date» из «order_date» при соответствующем индексе дает желаемый результат. Например.

set.seed(1453); x = sample(0:1, 10, TRUE) 
set.seed(1821); y = sample(5, 10, TRUE) 
cbind(x, y, 
     last_x = c(NA, cummax(x * seq_along(x))[-length(x)]), 
     y_diff = y - y[c(NA, cummax(x * seq_along(x))[-length(x)])]) 
#  x y last_x y_diff 
# [1,] 1 3  NA  NA 
# [2,] 0 3  1  0 
# [3,] 1 5  1  2 
# [4,] 0 1  3  -4 
# [5,] 0 3  3  -2 
# [6,] 1 5  3  0 
# [7,] 1 1  6  -4 
# [8,] 0 3  7  2 
# [9,] 0 4  7  3 
#[10,] 1 5  7  4 

На данных, первый формат df для удобства:

df$order_date = as.Date(df$order_date) 
df$complete_order = df$complete_order == "1" # lose the 'factor' 

И, затем, либо применить вышеуказанный подход после group_by:

library(dplyr) 
df %>% group_by(user_id) %>% 
    mutate(time_diff = order_date - 
order_date[c(NA, cummax(complete_order * seq_along(complete_order))[-length(complete_order)])]) 

, или, возможно, дать попробуйте выполнить операции, которые избегают группировки (при условии, что заказ «user_id») после учета индексов, где изменяется «user_id»:

# save variables to vectors and keep a "logical" of when "id" changes 
id = df$user_id 
id_change = c(TRUE, id[-1] != id[-length(id)]) 

compl = df$complete_order 
dord = df$order_date 

# accounting for changes in "id", locate last completed order 
i = c(NA, cummax((compl | id_change) * seq_along(compl))[-length(compl)]) 
is.na(i) = id_change 

dord - dord[i] 
#Time differences in days 
#[1] NA 3 11 26 NA 3 12 27 
+0

спасибо, @alexis_laz, это определенно идет в правильном направлении. Однако, когда я тестирую ваше решение - со всеми этапами предварительной обработки данных, я получаю NA для отмененных заказов (completed_order == 0), любая идея, как обойти это? –

+0

@KasiaKulma: Вы имеете в виду пример «df» или ваши фактические данные? Если последний, вы могли бы предоставить/'dput' пример, он возвращает' NA'? И подход «dplyr», и последний возвращают 'NA'? –

+0

привет, оказалось, что группировка в моем тестовом наборе была неправильной, после исправления, решение работало как шарм, спасибо! Кроме того, спасибо за четкое объяснение того, как ваше решение работает, очень полезно! –

0

Я думаю, вы можете добавить filter функцию вместо Подменю с order_date[complete_order == 1] и убедитесь, что order_date (и другие переменные) являются правильными типы данных путем добавления stringsAsFactors = F к data.frame()):

df = data.frame(cbind(user_id = c(rep(1, 4), rep(2,4)), 
         complete_order = c(rep(c(1,1,0,1), 2)), 
         order_date = c('2015-01-28', '2015-01-31', '2015-02-08', '2015-02-23', '2015-01-25', '2015-01-28', '2015-02-06', '2015-02-21')), 
       stringsAsFactors = F) 

df$order_date <- lubridate::ymd(df$order_date) 

df %>% 
    group_by(user_id) %>% 
    filter(complete_order == 1) %>% 
    mutate(complete_order_time_diff = order_date - lag(order_date)) 

Это возвращает время до следующего полного порядка (и NA если не один):

user_id complete_order order_date complete_order_time_diff 
    <chr>   <chr>  <date>     <time> 
1  1    1 2015-01-28     NA days 
2  1    1 2015-01-31     3 days 
3  1    1 2015-02-23     23 days 
4  2    1 2015-01-25     NA days 
5  2    1 2015-01-28     3 days 
6  2    1 2015-02-21     24 days 
+0

Привет, Джошуа, спасибо, но мне нужно сохранить отмененные заказы (completed_orders == 0) в наборе данных и рассчитать разницу во времени для них, а также –

2

попробовать этот

library(dplyr) 

df %>% group_by(user_id, complete_order) %>% 
    mutate(c1 = order_date - lag(order_date)) %>% 
    group_by(user_id) %>% mutate(c2 = order_date - lag(order_date)) %>% ungroup %>% 
    mutate(complete_order_time_diff = ifelse(complete_order==0, c2, c1)) %>% 
    select(-c(c1, c2)) 

Обновление

для нескольких отмененных заказов

df %>% mutate(c3=cumsum(complete_order != "0")) %>% group_by(user_id, complete_order) %>% 
    mutate(c1 = order_date - lag(order_date)) %>% 
    group_by(user_id) %>% mutate(c2 = order_date - lag(order_date)) %>% 
    mutate(c2=as.numeric(c2)) %>% group_by(user_id, c3) %>% 
    mutate(c2=cumsum(ifelse(complete_order==1, 0, c2))) %>% ungroup %>% 
    mutate(complete_order_time_diff = ifelse(complete_order==0, c2, c1)) %>% 
    select(-c(c1, c2, c3)) 

логик

c3 является id каждый раз, когда есть заказ (т.е. complete_order not 0) для увеличения на 1.

c1 вычисляет разницу в день бушелей user_id (но для не полных заказов результата неправильно)

c2 исправляет эту несогласованность c1 относительно Некомплектные заказы.

надеюсь, что это очистит вещи.

Я предлагаю вам работать с комбинациями group_by() и mutate(cumsum()), чтобы лучше понять результаты, имеющие более одной сгруппированной переменной.

+0

спасибо, dimitris, к сожалению, это не работает для меня: как предложено в желаемом результате , Мне нужно рассчитать разницу во времени для отмененных заказов (completed_order == 0), а также –

+0

это проще, см. Мой обновленный ответ –

+0

helas! попробовал это перед публикацией здесь, и это решение также рассчитает разницу во времени между выполненными заказами и отмененными заказами. Если мы определяем 'time_diff = x - y', то' x' может быть любым типом порядка, но 'y' должен быть завершенным порядком, всегда. Надеюсь, теперь это имеет смысл. –