根据前几行中的值快速计算单元格中的值的方法
Fast way to calculate values in cells based on values in previous rows
我希望有一种快速的方法来处理行计算,其中单元格的值取决于不同列的前几行中的值,更喜欢矢量化而不是循环遍历各个行( 的跟进)。
假设我有以下数据集 dt
和一个 constant
(加载的库是 data.table
、dplyr
和 purrr
)
dt <- structure(list(var1 = c(-92186.7470607738, -19163.5035325072,
-18178.8396858014, -9844.67882723287, -16494.7802822178, -17088.0576319257
), var2 = c(-3.12, NA, NA, NA, NA, NA), var3 = c(1, NA, NA, NA,
NA, NA)), class = c("data.table", "data.frame"), row.names = c(NA,
-6L))
constant <- 608383
print(dt)
var1 var2 var3
1: -92186.747 -3.12 1
2: -19163.504 NA NA
3: -18178.840 NA NA
4: -9844.679 NA NA
5: -16494.780 NA NA
6: -17088.058 NA NA
的快速矢量化等价物
for(i in 2:nrow(dt)){
prev <- dt[(i-1),]
dt[i, var2 := prev$var2 - var1/constant]
}
会是
dt %>%
mutate(var2 = accumulate(var1[-1], .init = var2[1], ~ .x - .y /constant))
但是如果我想在计算中包含更多列怎么办?在此示例中 var3
,但在实际数据集中有 >10 列。我希望解决方案能够考虑到这一点。循环示例(所需输出):
for(i in 2:nrow(dt)){
prev <- dt[(i-1),]
dt[i, var2 := prev$var2 + prev$var3 - var1/constant]
dt[i, var3 := prev$var1 + 0.1 * var2/constant]
}
print(dt)
var1 var2 var3
1: -92186.747 -3.120000e+00 1.00
2: -19163.504 -2.088501e+00 -92186.75
3: -18178.840 -9.218881e+04 -19163.52
4: -9844.679 -1.113523e+05 -18178.86
5: -16494.780 -1.295311e+05 -9844.70
6: -17088.058 -1.393758e+05 -16494.80
虽然 很棒,但是由于我们不能在 baseR 中有两个输入向量 Reduce()
所以我使用了这个技巧-
- 在
Reduce()
内的 data.frame()
中生成了 var1
的新值
- 如果你想使用
var1
的当前值,请使用 .y
- 如果要使用以前的值,请改用
.x$var1
。
- 使用公式,我需要使用任何变量的当前生成值。
- 其余的我想很清楚了。
accumulate = TRUE
很明显,因为您需要所有中间值。
- 因为这里输出的是一个列表,即
rbind
using do.call
在基础 R 中你可以做到
do.call(rbind, Reduce(function(.x, .y) {data.frame(var1 = .y,
var2 = .x$var2 + .x$var3 -.y/constant,
var3 = .x$var1 + 0.1 * (.x$var2 + .x$var3 -.y/constant)/constant)},
dt$var1[-1],
init = data.frame(var1 = dt$var1[1], var2 = -3.12, var3 = 1),
accumulate = TRUE))
var1 var2 var3
1 -92186.747 -3.120000e+00 1.00
2 -19163.504 -2.088501e+00 -92186.75
3 -18178.840 -9.218881e+04 -19163.52
4 -9844.679 -1.113523e+05 -18178.86
5 -16494.780 -1.295311e+05 -9844.70
6 -17088.058 -1.393758e+05 -16494.80
可以在tidyverse/purrr中模拟如下
library(purrr)
accumulate(dt$var1[-1], .init = data.frame(var1 = dt$var1[1], var2 = -3.12, var3 = 1),
~ data.frame(var1 = .y,
var2 = .x$var2 + .x$var3 -(.y/constant),
var3 = .x$var1 + 0.1 * (.x$var2 + .x$var3 -(.y/constant))/constant)) %>% map_df(~.x)
var1 var2 var3
1 -92186.747 -3.120000e+00 1.00
2 -19163.504 -2.088501e+00 -92186.75
3 -18178.840 -9.218881e+04 -19163.52
4 -9844.679 -1.113523e+05 -18178.86
5 -16494.780 -1.295311e+05 -9844.70
6 -17088.058 -1.393758e+05 -16494.80
这是您可以使用的另一种基于 R 的解决方案:
do.call(rbind, Reduce(function(x, y) {
data.frame(var1 = dt$var1[y],
var2 = x[["var2"]] + x[["var3"]] - (dt$var1[y] / constant),
var3 = dt$var1[y - 1] + 0.1 * ((x[["var2"]] + x[["var3"]] - (dt$var1[y] / constant)) / constant))
}, init = data.frame(var1 = dt$var1[1], var2 = -3.12, var3 = 1), 2:nrow(dt), accumulate = TRUE))
var1 var2 var3
1 -92186.747 -3.120000e+00 1.00
2 -19163.504 -2.088501e+00 -92186.75
3 -18178.840 -9.218881e+04 -19163.52
4 -9844.679 -1.113523e+05 -18178.86
5 -16494.780 -1.295311e+05 -9844.70
6 -17088.058 -1.393758e+05 -16494.80
我认为您可以使用以下解决方案。以下是有关其工作原理的一些说明:
- 在这个问题中,我们需要填充 2 个长度为 6 的向量,其中两个已经通过
.init
参数指定,与上一个问题相反,我们正在填充两个变量,因此我们需要创建一个 tibble
并从那里开始
- 我们提供的还有 5 个其他变量需要填充
.init
第一个和第二个向量的长度应该相等,否则第二个向量应该比第一个向量短一个元素(没有 .init
)
- 由于我们正在处理
var1
的实际值和先前值,我决定每次使用它两次,分别省略第一个值和最后一个值,例如在计算 var3
时需要prev$var1
其实就是第二个变量的第一个值var1[-n()]
..1
始终是 accumulated/previous 值,这里因为我们有两个 var2
和 var3
我们可以用 $
对其进行子集化以指定哪一个我们指的是
..2
通常是第一个向量 .x
序列中的下一个值,此处 var1[-1]
和 ..3
是第二个向量序列中的下一个值 [=一般为 28=],此处为 var1[-n()]
如果这些注释还不够,我很乐意解释更多。
library(purrr)
dt[,1] %>%
bind_cols(dt %>%
mutate(output = accumulate2(var1[-1], var1[-n()], .init = tibble(var2 = -3.12, var3 = 1),
~ tibble(var2 = (..1$var2 + ..1$var3 - (..2/constant)),
var3 = ..3 + 0.1 * ((..1$var2 + ..1$var3 - (..2/constant)) /constant)))) %>%
select(output) %>%
unnest(output))
var1 var2 var3
1: -92186.747 -3.120000e+00 1.00
2: -19163.504 -2.088501e+00 -92186.75
3: -18178.840 -9.218881e+04 -19163.52
4: -9844.679 -1.113523e+05 -18178.86
5: -16494.780 -1.295311e+05 -9844.70
6: -17088.058 -1.393758e+05 -16494.80
另一个使用 Rcpp 的选项:
library(Rcpp)
cppFunction('List func(NumericVector var1, double c, double v2, double v3) {
int n = var1.size();
NumericVector var2(n);
NumericVector var3(n);
var2[0] = v2;
var3[0] = v3;
for (int i = 1; i < n; i++) {
var2[i] = var2[i-1] + var3[i-1] - var1[i]/c;
var3[i] = var1[i-1] + 0.1 * var2[i]/c;
}
List ret;
ret["var2"] = var2;
ret["var3"] = var3;
return ret;
}')
dt[, c("var2", "var3") := func(var1, constant, var2[1L], var3[1L])]
我希望有一种快速的方法来处理行计算,其中单元格的值取决于不同列的前几行中的值,更喜欢矢量化而不是循环遍历各个行(
假设我有以下数据集 dt
和一个 constant
(加载的库是 data.table
、dplyr
和 purrr
)
dt <- structure(list(var1 = c(-92186.7470607738, -19163.5035325072,
-18178.8396858014, -9844.67882723287, -16494.7802822178, -17088.0576319257
), var2 = c(-3.12, NA, NA, NA, NA, NA), var3 = c(1, NA, NA, NA,
NA, NA)), class = c("data.table", "data.frame"), row.names = c(NA,
-6L))
constant <- 608383
print(dt)
var1 var2 var3
1: -92186.747 -3.12 1
2: -19163.504 NA NA
3: -18178.840 NA NA
4: -9844.679 NA NA
5: -16494.780 NA NA
6: -17088.058 NA NA
的快速矢量化等价物
for(i in 2:nrow(dt)){
prev <- dt[(i-1),]
dt[i, var2 := prev$var2 - var1/constant]
}
会是
dt %>%
mutate(var2 = accumulate(var1[-1], .init = var2[1], ~ .x - .y /constant))
但是如果我想在计算中包含更多列怎么办?在此示例中 var3
,但在实际数据集中有 >10 列。我希望解决方案能够考虑到这一点。循环示例(所需输出):
for(i in 2:nrow(dt)){
prev <- dt[(i-1),]
dt[i, var2 := prev$var2 + prev$var3 - var1/constant]
dt[i, var3 := prev$var1 + 0.1 * var2/constant]
}
print(dt)
var1 var2 var3
1: -92186.747 -3.120000e+00 1.00
2: -19163.504 -2.088501e+00 -92186.75
3: -18178.840 -9.218881e+04 -19163.52
4: -9844.679 -1.113523e+05 -18178.86
5: -16494.780 -1.295311e+05 -9844.70
6: -17088.058 -1.393758e+05 -16494.80
虽然 Reduce()
所以我使用了这个技巧-
- 在
Reduce()
内的 - 如果你想使用
var1
的当前值,请使用.y
- 如果要使用以前的值,请改用
.x$var1
。 - 使用公式,我需要使用任何变量的当前生成值。
- 其余的我想很清楚了。
accumulate = TRUE
很明显,因为您需要所有中间值。- 因为这里输出的是一个列表,即
rbind
usingdo.call
data.frame()
中生成了 var1
的新值
在基础 R 中你可以做到
do.call(rbind, Reduce(function(.x, .y) {data.frame(var1 = .y,
var2 = .x$var2 + .x$var3 -.y/constant,
var3 = .x$var1 + 0.1 * (.x$var2 + .x$var3 -.y/constant)/constant)},
dt$var1[-1],
init = data.frame(var1 = dt$var1[1], var2 = -3.12, var3 = 1),
accumulate = TRUE))
var1 var2 var3
1 -92186.747 -3.120000e+00 1.00
2 -19163.504 -2.088501e+00 -92186.75
3 -18178.840 -9.218881e+04 -19163.52
4 -9844.679 -1.113523e+05 -18178.86
5 -16494.780 -1.295311e+05 -9844.70
6 -17088.058 -1.393758e+05 -16494.80
可以在tidyverse/purrr中模拟如下
library(purrr)
accumulate(dt$var1[-1], .init = data.frame(var1 = dt$var1[1], var2 = -3.12, var3 = 1),
~ data.frame(var1 = .y,
var2 = .x$var2 + .x$var3 -(.y/constant),
var3 = .x$var1 + 0.1 * (.x$var2 + .x$var3 -(.y/constant))/constant)) %>% map_df(~.x)
var1 var2 var3
1 -92186.747 -3.120000e+00 1.00
2 -19163.504 -2.088501e+00 -92186.75
3 -18178.840 -9.218881e+04 -19163.52
4 -9844.679 -1.113523e+05 -18178.86
5 -16494.780 -1.295311e+05 -9844.70
6 -17088.058 -1.393758e+05 -16494.80
这是您可以使用的另一种基于 R 的解决方案:
do.call(rbind, Reduce(function(x, y) {
data.frame(var1 = dt$var1[y],
var2 = x[["var2"]] + x[["var3"]] - (dt$var1[y] / constant),
var3 = dt$var1[y - 1] + 0.1 * ((x[["var2"]] + x[["var3"]] - (dt$var1[y] / constant)) / constant))
}, init = data.frame(var1 = dt$var1[1], var2 = -3.12, var3 = 1), 2:nrow(dt), accumulate = TRUE))
var1 var2 var3
1 -92186.747 -3.120000e+00 1.00
2 -19163.504 -2.088501e+00 -92186.75
3 -18178.840 -9.218881e+04 -19163.52
4 -9844.679 -1.113523e+05 -18178.86
5 -16494.780 -1.295311e+05 -9844.70
6 -17088.058 -1.393758e+05 -16494.80
我认为您可以使用以下解决方案。以下是有关其工作原理的一些说明:
- 在这个问题中,我们需要填充 2 个长度为 6 的向量,其中两个已经通过
.init
参数指定,与上一个问题相反,我们正在填充两个变量,因此我们需要创建一个tibble
并从那里开始 - 我们提供的还有 5 个其他变量需要填充
.init
第一个和第二个向量的长度应该相等,否则第二个向量应该比第一个向量短一个元素(没有.init
) - 由于我们正在处理
var1
的实际值和先前值,我决定每次使用它两次,分别省略第一个值和最后一个值,例如在计算var3
时需要prev$var1
其实就是第二个变量的第一个值var1[-n()]
..1
始终是 accumulated/previous 值,这里因为我们有两个var2
和var3
我们可以用$
对其进行子集化以指定哪一个我们指的是..2
通常是第一个向量.x
序列中的下一个值,此处var1[-1]
和..3
是第二个向量序列中的下一个值 [=一般为 28=],此处为var1[-n()]
如果这些注释还不够,我很乐意解释更多。
library(purrr)
dt[,1] %>%
bind_cols(dt %>%
mutate(output = accumulate2(var1[-1], var1[-n()], .init = tibble(var2 = -3.12, var3 = 1),
~ tibble(var2 = (..1$var2 + ..1$var3 - (..2/constant)),
var3 = ..3 + 0.1 * ((..1$var2 + ..1$var3 - (..2/constant)) /constant)))) %>%
select(output) %>%
unnest(output))
var1 var2 var3
1: -92186.747 -3.120000e+00 1.00
2: -19163.504 -2.088501e+00 -92186.75
3: -18178.840 -9.218881e+04 -19163.52
4: -9844.679 -1.113523e+05 -18178.86
5: -16494.780 -1.295311e+05 -9844.70
6: -17088.058 -1.393758e+05 -16494.80
另一个使用 Rcpp 的选项:
library(Rcpp)
cppFunction('List func(NumericVector var1, double c, double v2, double v3) {
int n = var1.size();
NumericVector var2(n);
NumericVector var3(n);
var2[0] = v2;
var3[0] = v3;
for (int i = 1; i < n; i++) {
var2[i] = var2[i-1] + var3[i-1] - var1[i]/c;
var3[i] = var1[i-1] + 0.1 * var2[i]/c;
}
List ret;
ret["var2"] = var2;
ret["var3"] = var3;
return ret;
}')
dt[, c("var2", "var3") := func(var1, constant, var2[1L], var3[1L])]