【问题标题】:R: calculate time difference between specific eventsR:计算特定事件之间的时间差
【发布时间】:2017-05-21 05:27:52
【问题描述】:

我有以下数据集:

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

对此的任何帮助都会很棒,谢谢!

【问题讨论】:

  • 看来您可以尝试通过“user_id”应用ff = function(complete, date) date - date[c(NA, cummax(complete * seq_along(complete))[-length(complete)])] 之类的函数,其中“complete_order”和“order_date”分别作为“complete”和“date”传递。

标签: r date dplyr lubridate


【解决方案1】:

我认为您可以添加一个filter 函数来代替order_date[complete_order == 1] 的子集,并通过将stringsAsFactors = F 添加到data.frame() 来确保order_date(和其他变量)是正确的数据类型:

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

【讨论】:

  • 嗨 Joshua,谢谢,但我需要在数据集中保留已取消的订单 (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 计算天差 bu user_id (但对于不完整的订单结果是错误的)

c2 修复了c1 与未完成订单相关的这种不一致问题。

希望这能解决问题。

我建议您使用 group_by()mutate(cumsum()) 的组合,以更好地了解拥有多个分组变量的结果。

【讨论】:

  • 谢谢,dimitris,不幸的是,这对我不起作用:正如预期结果中所建议的,我还需要计算取消订单的时间差 (completed_order == 0)
  • 赫拉斯!在此处发布之前尝试过此解决方案,此解决方案还将计算已完成订单和已取消订单之间的时间差。如果我们定义time_diff = x - y,那么x 可以是任何类型的订单,但y 必须始终是已完成的订单。希望这现在有意义
  • 看起来很有希望,在我接受答案之前让我在更复杂的数据集上测试它,谢谢!
  • 另外我认为你想要的结果的倒数第二行应该是 9 days 而不是 6 days 和最后一个 24 否则我仍然缺少一些东西。
  • 好的,我可以看到您的解决方案在大多数情况下都有效,但当一个客户的行中有多个取消订单时就不行了。我将编辑示例数据集,以便将这种情况考虑在内。再次感谢您的帮助!
【解决方案3】:

您似乎正在寻找每个订单与上一个已完成订单的距离。有一个二进制向量,xc(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

【讨论】:

  • 谢谢,@alexis_laz,它肯定是朝着正确的方向前进。但是,当我测试您的解决方案时 - 使用所有数据预处理步骤 - 我得到取消订单的 NA (completed_order == 0),知道如何解决它吗?
  • @KasiaKulma :您的意思是示例中的“df”还是您的实际数据?如果是后者,您能否提供/dput 的示例是否返回NA? “dplyr”方法和最后一种方法都返回NA?
  • 你好,原来我的测试集中的分组是错误的,改正后,解决方案很有效,谢谢!另外,感谢您对您的解决方案如何工作的清晰解释,非常有用!
  • @KasiaKulma :很高兴你发现它有帮助。祝你好运!
  • 我尝试将相同的原理应用于取消订单,但即使最后取消订单的位置计算正确,结果也不一致。你介意在这个Stack Overflow Chat(chat.stackoverflow.com/rooms/132689/…) 上聊聊吗?
猜你喜欢
  • 2012-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-02
  • 1970-01-01
  • 2013-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多