r - 基于在没有 for() 循环的情况下递归调用列的先前元素,在 R 中生成一个新的变量/列?
问题描述
我在 R 中有一些按以下方式组织的数据(简化示例):
x <- as.data.frame(cbind(c(1,2,3,4,5),c(6,7,8,9,10)))
这将创建一个两列数据框(“V1”和“V2”)。我想创建第三和第四列(“V3”和“V4”),它们都根据这两列中的每一个中的先前条目调用不同的函数。V3 基于不断附加向量的函数,而 V4 是简单的数学运算(几何平均)。到目前为止,我可以通过指定新列和使用for()
循环来获得我需要的东西(使用简化函数的示例):
x <- x %>% mutate(V3 = 0, V4_0 = 0, V4 = 0)
for(i in 2:nrow(x)){x$V3[i] <- sum(c(x$V1[1:i - 1], x$V1[i] + x$V2[i]))}
for(i in 2:nrow(x)){x$V4_0[i] <- ((x$V2[i] - x$V2[i - 1]) / x$V2[i - 1])}
for(i in 2:nrow(x)){x$V4[i] <- prod(x$V4_0[1:i - 1] + 1, na.rm = TRUE)^(1/i) - 1} # essentially, a geometric mean
输出:
V1 V2 V3 V4_0 V4
1 1 6 0 0.0000000 0.00000000
2 2 7 10 0.1666667 0.00000000
3 3 8 14 0.1428571 0.05272660
4 4 9 19 0.1250000 0.07456993
5 5 10 25 0.1111111 0.08447177
我很好奇:使用 tidyverse 函数(例如mutate()
)或基本 R 是否有更清洁或更简单的方法?还是for()
循环是我最好的选择?在小范围内一切正常,但我担心更大的数据集,因为for()
循环据说效率较低。我认为这cumsum()
接近于此,但具体到总和。任何帮助将不胜感激!
解决方案
x$V3 <- c(0, cumsum(x$V1)[-1] + x$V2[-1])
x$V4_0 <- c(0, diff(x$V2) / x$V2[-nrow(x)])
x$V4 <- c(1, cumprod(x$V4_0 + 1)[-nrow(x)])^(1/seq_len(nrow(x))) - 1
x
# V1 V2 V3 V4_0 V4
# 1 1 6 0 0.0000000 0.00000000
# 2 2 7 10 0.1666667 0.00000000
# 3 3 8 14 0.1428571 0.05272660
# 4 4 9 19 0.1250000 0.07456993
# 5 5 10 25 0.1111111 0.08447177
替代#1
如果在某些情况下您不能像这样进行矢量化处理(例如,如果jrvFinance::irr
需要对每个新值的长度为 2 的向量进行操作),那么我建议zoo::rollapply
:
zoo::rollapply(seq_along(x$V1), 2, FUN = function(vec) {
# ...
})
内部函数以向量长度 2 调用;第一次是c(1,2)
,第二c(2,3)
次等。通常,rollapply
对数据本身进行操作(即,x$V1
),但由于您想同时使用V1
和V2
,我将滚动向量的索引而不是向量本身。
这改变了你的
sum(c(x$V1[1:i - 1], x$V1[i] + x$V2[i]))
至
sum(c(x$V1[1:vec[1]], x$V1[vec[2]] + x$V2[vec[2]]))
关于这一点的更多说明:
zoo::rollapply(seq_along(x$V1), 2,
FUN = function(vec) sum(c(x$V1[1:vec[1]], x$V1[vec[2]] + x$V2[vec[2]])))
# [1] 10 14 19 25
这将返回
2-1=1
比输入短的元素。(也就是说,如果rollapply(vec, 5, ...)
,那么它将返回比输入向量少 4 个元素。)为了解决这个问题,我们可以手动填充它c(0, rollapply(...))
,或者我们可以fill=
使用值。滚动函数有
align
ment 的概念。从帮助文档:align: specifyies whether the index of the result should be left- or right-aligned or centered (default) compared to the rolling window of observations. This argument is only used if 'width' represents widths.
以及它们的演示:
zoo::rollapply(1:5, 3, sum, align = "left", fill = NA) # [1] 6 9 12 NA NA zoo::rollapply(1:5, 3, sum, align = "center", fill = NA) # [1] NA 6 9 12 NA zoo::rollapply(1:5, 3, sum, align = "right", fill = NA) # [1] NA NA 6 9 12
我的解释
align="left"
是输出进入用于生成总和的三个值(此处)的最左边的位置。那是,1 2 3 4 5 `-----' sum these ^ align="left", output goes here ^ align="center", output goes here ^ align="right", output goes here
(意识到
align=
没有fill=something
.)
所以使用align=
and fill=
,我们现在可以这样做:
x$V3_again <- zoo::rollapply(seq_along(x$V1), 2,
FUN = function(vec) sum(c(x$V1[1:vec[1]], x$V1[vec[2]] + x$V2[vec[2]])),
align = "right", fill = 0)
x
# V1 V2 V3 V4_0 V4 V3_again
# 1 1 6 0 0.0000000 0.00000000 0
# 2 2 7 10 0.1666667 0.00000000 10
# 3 3 8 14 0.1428571 0.05272660 14
# 4 4 9 19 0.1250000 0.07456993 19
# 5 5 10 25 0.1111111 0.08447177 25
替代#2
inds <- seq_len(nrow(x))
c(0, mapply(function(a,b) sum(c(x$V1[1:a], x$V1[b] + x$V2[b])),
inds[-length(inds)], inds[-1]))
# [1] 0 10 14 19 25
(这c(0, ...)
是在模仿rollapply
的align="right",fill=0
论点。)
mapply
类似于lapply
/ sapply
/ vapply
,但是虽然这三个函数一次对单个向量进行操作,但mapply
对一个或多个向量起作用,有效地将它们“压缩”在一起。例如,
mapply(FUN, 1:5, 6:10)
展开有效地调用
FUN(1, 6)
FUN(2, 7)
FUN(3, 8)
FUN(4, 9)
FUN(5, 10)
推荐阅读
- python - 端口转发时球童给空白页
- sql - 从现有排名和其他附加查找中得出优先级排名
- python - Python 多处理不再起作用,即使是示例代码
- php - 从 WooCommerce 购物车中删除特定数量的特定产品
- php - 访问 localhost/phpmyadmin/ 时出现 Laragon 致命错误
- amazon-web-services - AWS 中特定实例的 IP 禁令
- javascript - 如何在 Vuejs 中对输入使用 HTML5 验证检查
- reactjs - konva react.js 文本可拖动无法正常工作
- python - 返回值为 alist 时使用 pandas 分配函数时出错
- powershell - 如何通过应用程序 guid 或 IdentificationNumber 启动进程