1

给定由以下定义的数据框:

set.seed(1)
date <- sample(seq(as.Date('2016/01/01'), as.Date('2016/12/31'), by="day"), 12)
vals <- data.frame(x = rep(1:3, 4), date = date, cost = rnorm(12, 100))
vals
#    x       date      cost
# 1  1 2016-04-07 100.48743
# 2  2 2016-05-15 100.73832
# 3  3 2016-07-27 100.57578
# 4  1 2016-11-25  99.69461
# 5  2 2016-03-14 101.51178
# 6  3 2016-11-20 100.38984
# 7  1 2016-12-06  99.37876
# 8  2 2016-08-25  97.78530
# 9  3 2016-08-13 101.12493
# 10 1 2016-01-23  99.95507
# 11 2 2016-12-27  99.98381
# 12 3 2016-03-03 100.94384

我想添加一个新列,其中第 i 行的新值所有成本值的总和,其中:

  • 日期小于或等于第 i日期且大于第 i日期减去 90 天
  • x 值等于第 i 行的 x 值。(在此示例中,x 和 date 的组合是唯一的,但通常它们可能不是。)

我可以通过两种不同的方式做到这一点:

tmp <- vals %>% group_by(date, x) %>% 
summarise(total = sum(vals$cost[vals$date <= date[1] & vals$date > (date[1] - 90) & vals$x == x[1]]))
vals %>% left_join(tmp)

vals %>% rowwise() %>% 
mutate(total = sum(vals$cost[vals$date <= date[1] & vals$date > (date[1] - 90) & vals$x == x]))

两者在我的大数据上都相当慢,大概是因为所有的子集。我正在将数据框传回计算中,这对我来说有点像黑客攻击。

有没有办法在 内“正确”地做到这一点dplyr?我的意思是,无需传入数据框并进行缓慢的子集设置。

或者如果没有,是否至少有一种更有效的方法来做到这一点?

4

2 回答 2

1

喜欢vals %>% arrange(x, date) %>% group_by(x) %>% mutate(new = cumsum(cost))

解决每天多条记录的问题。我猜你必须先做一个每天的计算?

vals %>% 
  arrange(x, date) %>%
  group_by(x, date) %>%
    mutate(cost = cumsum(cost)) %>%
  ungroup() %>%
  group_by(x) %>%
    mutate(new = cumsum(cost))
于 2017-02-13T20:46:27.123 回答
1

基本上,(按日期排序时)您总是计算sum(cost[index_start : index_end])位置index_startindex_end滑过行。这可以使用成本的累积总和更有效地完成:sum(cost[index_start : index_end]) = cumsum(cost[index_end]) - cumsum(cost[index_start - 1]). 对于您的数据框,代码一种可能的实现如下。

# arrange by date so all relevant cost come after each other
vals <- arrange(vals, x, date)
group_by(vals, x) %>% 
  mutate(
    cumsum_cost = cumsum(cost),
    index_start = map_dbl(
      date,
      function(cur_date, date) {
        min(which(cur_date - days(90) <= date))
      },
      date = date),
    cumsum_cost_90_days_ago = map_dbl(
      index_start,
      function(index_start, cumsum_cost) {
        if (index_start - 1 <= 0) {
          return(0)
        } else {
          cumsum_cost[index_start - 1]
        }
      },
      cumsum_cost = cumsum_cost),
    cost_90_days = cumsum_cost - cumsum_cost_90_days_ago
  )

如果人们在获取方面更聪明index_start(例如通过使用数据帧按 排序的知识date),则可以进一步加快这一速度。索引的一种简单方法是滚动连接,例如 in data.table

于 2017-02-15T07:56:54.880 回答