在 R 中跨列(不是行!)滚动减法
Posted
技术标签:
【中文标题】在 R 中跨列(不是行!)滚动减法【英文标题】:Rolling subtraction across columns (not rows!) in R 【发布时间】:2022-01-13 23:05:33 【问题描述】:我四处寻找类似的问题,但我发现的所有问题都想按行进行滚动减法。
我想做的是在我的数据框列中滚动减法。具体来说,我想按顺序(从左到右)减去每一列,同时保持当前减去的累积值,作为下一个序列中要减去的“总”列。
我找到了一种对其进行硬编码的方法,但显然它看起来很难看,如果列数与创建的 dfs 数有任何不同,代码就会中断。
假设我们有一个每年每个年龄的人口数据框,总数为每年的 rowSums:
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.)
我摆弄着循环,但不能完全让第一次/最后一次迭代工作(x+1/x-1 部分一直给我一个错误,即下标超出范围)。我什至尝试在循环中使用“break”或“next”,但我无法完全掌握它。我有大约 70 年的数据,未来可能更多,所以我需要更新我的代码以使其适应未来,以免有数百行“dfx”代码。
我想知道是否有人可以提供一个超级简单的循环或函数来解决这个问题。也许 data.table 解决方案是最简单的,尽管我很难使用 data.table 语法。如果您可以在整个迭代过程中保留变量名称(尽管不是必需的),则可以加分。我只希望我的代码既漂亮又健壮!干杯,谢谢。
【问题讨论】:
【参考方案1】:我想这就是你想要的。不需要 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
【讨论】:
很棒,简单,可解释,使用基础 R,并且可推广到 x 数量的列。非常感谢! 刚刚稍微编辑了您的代码以具有final_df <- data.frame(df[, 1:2], df$Total - t(apply(df[, 3:ncol(df)], 1, cumsum)))
,将 3:5 替换为 3:ncol(df),所以现在它应该可以工作无限年!很容易理解,谢谢!!
感谢编辑。【参考方案2】:
这是一个带有 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
【讨论】:
看起来很棒!谢谢你。我喜欢 .SDcols 功能。我开始更频繁地使用 Reduce,它是一个很棒的工具。像魔术一样工作。现在只需要更好地使用 data.table 语法。干杯!【参考方案3】:解决此问题的各种方法:
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
【讨论】:
感谢 Onyambu,非常优雅。您的解决方案类似于 Brian 的解决方案。只是好奇那个 list2DF() 函数是什么?它的工作方式类似于 cbind() 吗?它需要一个列表作为输入吗?我对此很感兴趣。干杯!以上是关于在 R 中跨列(不是行!)滚动减法的主要内容,如果未能解决你的问题,请参考以下文章