R 中跨列(不是行!)的滚动减法

Rolling subtraction across columns (not rows!) in R

我四处寻找类似的问题,但我发现的所有问题都想做滚动减法 by rows

我想做的是在我的数据帧上进行滚动减法。具体来说,我想按顺序(从左到右)减去每一列,同时保持当前减去的累积,作为下一个序列中要减去的“总”列。

我找到了一种对此进行硬编码的方法,但显然它看起来很丑陋,如果列数与创建的 dfs 数有任何不同,代码就会中断。

假设我们有一个每年每个年龄的人口数据框,总数是每年的行总和:

df <- data.frame(Age <- c(1:40), 
                 Total <- rep(500,40), 
                 Y1990 <- rep(100,40), 
                 Y1991 <- rep(100,40),
                 Y1992 <- rep(100,40))

我想要的结果是通过下面的代码实现的:

df1 <- df$Total  #or df[2]
df2 <- df1 - df[3]
df3 <- df2 - df[4]
...
dfx <- df(x-1) - df[x+1]

#and then we join them together like so:
final_df <- cbind(df$Age, df1, df2, df3,..., dfx)

#final_df should be the Age column, the Total column (500), df2 should be 400 (500-100 = 400), df3 should be 300, etc. etc.)

我摆弄了循环但无法完全使 first/last 迭代工作(x+1/x-1 部分一直给我一个下标超出范围的错误)。我什至尝试在循环中使用“break”或“next”,但我不太理解。我有大约 70 年的数据,将来可能会更多,所以我需要更新我的代码以使其面向未来,以免有数百行“dfx”代码。

我想知道是否有人可以提供一个超级简单的循环或函数来解决这个问题。也许 data.table 解决方案是最简单的,尽管我很难使用 data.table 语法。如果您可以在整个迭代过程中保留变量名称(虽然不是必需的),则可以获得加分。我只希望我的代码漂亮而健壮!干杯,谢谢。

我想这就是你想要的。不需要40个相同的行,5个就够了:

df <- data.frame(Age = c(1:5), Total = rep(500, 5), Y1990 = rep(100, 5), Y1991 = rep(100, 5), Y1992 = rep(100, 5))

final_df <- data.frame(df[, 1:2], df$Total - t(apply(df[, 3:5], 1, cumsum)))
colnames(final_df)[-(1:2)] <- c("df2", "df3", "df4")
final_df
#   Age Total df2 df3 df4
# 1   1   500 400 300 200
# 2   2   500 400 300 200
# 3   3   500 400 300 200
# 4   4   500 400 300 200
# 5   5   500 400 300 200

这是 data.table 的解决方案:

library(data.table)
df <- data.frame(Age = c(1:5), Total = rep(500, 5), Y1990 = rep(100, 5), Y1991 = rep(100, 5), Y1992 = rep(100, 5))
setDT(df)
final_df <- cbind(df[, .(Age = Age)], 
                  df[, Reduce(`-`, .SD, init = Total, accumulate = TRUE), 
                     .SDcols = Y1990:Y1992])
final_df
  Age  V1  V2  V3  V4
1:   1 500 400 300 200
2:   2 500 400 300 200
3:   3 500 400 300 200
4:   4 500 400 300 200
5:   5 500 400 300 200

解决此问题的各种方法:

cbind(df[1], matrixStats::rowCumsums(as.matrix(df[-1])))
  Age   1   2   3   4
1   1 500 600 700 800
2   2 500 600 700 800
3   3 500 600 700 800
4   4 500 600 700 800
5   5 500 600 700 800


cbind(df[1], list2DF(Reduce('-', df[-1], accumulate = TRUE)))

  Age Var.2 Var.3 Var.4 Var.5
1   1   500   400   300   200
2   2   500   400   300   200
3   3   500   400   300   200
4   4   500   400   300   200
5   5   500   400   300   200