【问题标题】:Cumulative sum of certain numbers in a dataframe ordered by date按日期排序的数据框中某些数字的累积和
【发布时间】:2017-01-14 17:26:46
【问题描述】:

我有一个包含日期和值的数据框,并且想要一个仅用于正数和一个仅用于负数的 cumsum。日期有时多次具有相同的日期,然后缺少几天(无值 = 无行)

首先我只是测试了一个累积和。这些是累积的,但不是按日期顺序排列的:

df$cumsum <- cumsum(df$values) 
# or
df$cumsum  <- ave(df$values, FUN=cumsum)
# Should cumulate by date but did not in right order
df$cumsum   <- cumsum(df[order(df$date, df$values), "values"])

终于找到了一个解决方案,它按照我的意愿完成了第一步(不是我想在数据框中做的,而是完成了工作):

dt <- data.table(df)
dt[order(date), cumsum := cumsum(values)]

很好,但是每次尝试过滤大于 0 的值都没有成功。最后我对数据进行了子集化并得到了结果,但这并不是我想要的。

dt.pos <- data.table(subset(df, values> 0))
dt.pos[order(date), cumsum := cumsum(values)]

dt.neg <- data.table(subset(df, values < 0))
dt.neg[order(date), cumsum := cumsum(values)]

我正在寻找与 Python 等价物一样简单的东西(带有有序数据框):

df["cumsum_pos"] = df["values"][df["values"] > 0].cumsum()
df["cumsum_neg"] = df["values"][df["values"] < 0].cumsum()

/编辑

df <- data.frame(date = as.Date(c("2016-12-08", "2016-12-07", "2016-12-05", "2017-01-05", 
                                  "2017-01-10", "2017-01-11", "2017-01-11")),
                 values = c(10, -10, 5, 5, -7, 8, 8))

# just the cumsum
# expected output = c(5, -5, 5, 10, 3, 11, 19)

df$cumsum <- cumsum(df$values)
# output = c(10, 0, 5, 10, 3, 11, 19)

df$cumsum  <- ave(df$values, FUN=cumsum)
# output = c(10, 0, 5, 10, 3, 11, 19)

df$cumsum <- cumsum(df[order(df$date, df$values), "values"])
# output = c(5, -5, 5, 10, 3, 11, 19) correct in this example
# doesn't work with dates in a different order 2016-12-31, 2016-12-30, ... 2015-12-31, 2015-12-30

# Now for just the positives
# expected output = c(10, 0, 5, 15, 15, 23, 31)
df$cumsum.pos[df$values > 0] <- cumsum(df[order(df$date, df$values), "values"][df$values > 0])
# output = c(5, NA, 15, 20, NA, 28, 36)

# And then the same with just the negatives

/编辑

nicolas 的评论没有产生正确的输出

df<-df[order(df$date),]
# values = c(5, -10, 10, 5, -7, 8, 8)
# expected output = c(5, 5, 15, 20, 20, 28, 36)
df$cumsum<-ave(df$values,df$values>0,FUN=cumsum)
# output = c(5, -10, 15, 20, -17, 28, 36)

【问题讨论】:

  • 请展示一个可重现的小例子和预期的输出
  • 首先订购您的data.frame:df&lt;-df[order(df$date),],然后使用ave:df$cumsum&lt;-ave(df$value,df$value&gt;0,FUN=cumsum)
  • 不幸的是,这并没有给我我想要的东西,因为它也增加了负面影响。虽然我认为这是朝着正确方向迈出的一步。

标签: r dataframe cumsum


【解决方案1】:

你可以用这个。

library(data.table)
df <- as.data.table(df)

# Order by date
df <- df[order(date)]

# Perform the cumsum for positives and negatives separately
df[, expected := cumsum(values), by = sign(values)]

# Just for the negatives, get the previous positive value
df[, expected := ifelse(values > 0, expected, c(0, expected[-.N]))]

print(df)

         date values expected
1: 2016-12-05      5        5
2: 2016-12-07    -10        5
3: 2016-12-08     10       15
4: 2017-01-05      5       20
5: 2017-01-10     -7       20
6: 2017-01-11      8       28
7: 2017-01-11      8       36

注意,如果有多个连续的负值,则必须重复操作。例如,如果您的数据框是这个:

df <- data.frame(date = as.Date(c("2016-12-08", "2016-12-07", "2016-12-05", "2017-01-05","2017-01-10", "2017-01-10", "2017-01-11", "2017-01-11")), 
values = c(10, -10, 5, 5, -7, -15, 8, 8))

上述代码的一次执行将产生以下输出:

         date values expected
1: 2016-12-05      5        5
2: 2016-12-07    -10        5
3: 2016-12-08     10       15
4: 2017-01-05      5       20
5: 2017-01-10     -7       20
6: 2017-01-10    -15      -17
7: 2017-01-11      8       28
8: 2017-01-11      8       36

值 -17 是错误的。为了避免这个问题,您可以重复这个过程,直到没有任何负值留下。所以完整的代码是:

df <- df[order(date)]
df[, expected := cumsum(values), by = sign(values)]

# If there are negative values, repeat the process
while(length(which(df$expected < 0))){
  df[, expected := ifelse(values > 0, expected, c(0, expected[-.N]))]
}

print(df)
         date values expected
1: 2016-12-05      5        5
2: 2016-12-07    -10        5
3: 2016-12-08     10       15
4: 2017-01-05      5       20
5: 2017-01-10     -7       20
6: 2017-01-10    -15       20
7: 2017-01-11      8       28
8: 2017-01-11      8       36

【讨论】:

  • 感谢您的努力。但这只是我已经给出解决方案的第一部分。我只需要正值的 cumsum。
  • @sezi80 抱歉,我没听懂这个问题。我已经更新了答案,它分别计算了正面和负面的累积和。这是您的预期输出吗?
  • 好的,我最初的帖子似乎描述性不够。我想要一个只有正数的 cumsum,所以 geom_line() 会直接上升。无需对数据进行子集化。 expected output = c(5, 5, 15, 20, 20, 28, 36)
  • 不客气。使用expected[-.N],我得到整列expected(参见df$expected[-.N]),这样我就可以在序列前面加上0。结果就像将整列向下“移动”一个位置。你可以用df[, expected := ifelse(values &gt; 0, expected, shift(expected))]得到同样的效果
猜你喜欢
  • 2021-06-09
  • 2021-07-30
  • 2020-09-13
  • 2019-10-30
  • 1970-01-01
  • 2022-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多