如何在 R 中生成自引用变量例如,给定回报的索引级别?

问题描述

我必须生成一个自引用变量 (ind),该变量按 id 分组并且必须满足特定条件(例如,时间 > 1)。这是一个玩具示例:

set.seed(13)
dt <- data.frame(id = rep(letters[1:2],each = 4),time = rep(1:4,2),ret = rnorm(8)/100)
dt$ind <- if_else(dt$time == 1,100,as.numeric(NA))
dt

dt <- dt %>%
  group_by(id) %>%
  mutate(
    ind = if_else(time > 1,lag(ind,1)*(1+ret),ind)
  )

这是输出

Values for ind missing

显然我不能在这个设置中使用 mutate,因为它引用了 ind初始值,并且在计算新值时不会更新。

我想避免运行循环。有什么想法可以最有效地计算所有时间段的 ind 吗?


编辑:

感谢大家的帮助!我对上述问题有一个稍微棘手的扩展。

我该如何处理更高的延迟?例如,滞后 = 2,使得

index_{t} = index_{t-2}*(1+ret_{t})

这是我使用 Excel 生成的示例数据框和示例结果:

set.seed(13)
dt <- data.frame(id = rep(letters[1:2],each = 5),time = rep(1:5,ret = rnorm(10)/100)
dt$ind <- if_else(dt$time == 1,120,if_else(dt$time == 2,125,as.numeric(NA)))

enter image description here

解决方法

更新二 我问了 question,因为您对一个有趣的案例提出了新的要求,我认为这将是一个很好的机会来解决这个问题以学习新东西。希望 Mr. Grothendieck 教会了我们解决问题的巧妙方法。让我先告诉你:

  • 对于这种情况,我们使用复数结构 (a + bi)。您可能已经知道 a 是实部,b 是虚部,而 i 是不确定的。因此,我们以某种方式重构我们的 ind 输出 ind 的前一个值是实部 a,倒数第二个值是虚部 b 例如我们的第一个值可以重组为 120 + 0i 和第二个如125 + 120i
  • 我们这样做是因为我们需要在前一次迭代中保留这两个值,以便我们可以提取我们需要的值。我们使用Re函数提取实部,使用Im函数提取虚部
  • 对于 ret 变量,我们只需要省略前 2 行并使用对应于每次迭代的其余行
  • 最后,我们编写自定义函数的方式是从前一个 ind 中提取虚部,这实际上是我们前两个值并将其分配给 (1 + ret 的当前值)还有一个微妙的点:为了在下一次迭代中保持相同的结构,我们还将上一次迭代的实部添加为当前值的虚部(这实际上是下一次迭代的实部)&最后我们只提取实部

我知道这里可能有太多的事情要做,但请告诉我是否有任何我可以解释的更多内容,并感谢您提出这个好问题。

library(dplyr)
library(purrr)

dt %>%
  group_by(id) %>%
  mutate(ind = c(ind[1],Re(unlist(accumulate(ret[3:n()],.init = ind[2] + ind[1] * 1i,~ Im(..1) * (1 + ..2) + Re(..1) * 1i)))))

# A tibble: 10 x 4
# Groups:   id [2]
   id     time      ret   ind
   <chr> <int>    <dbl> <dbl>
 1 a         1  0.00554  120 
 2 a         2 -0.00280  125 
 3 a         3  0.0178   122.
 4 a         4  0.00187  125.
 5 a         5  0.0114   124.
 6 b         1  0.00416  120 
 7 b         2  0.0123   125 
 8 b         3  0.00237  120.
 9 b         4 -0.00365  125.
10 b         5  0.0111   122.

更新一基于@AnilGoyal的绝妙创意

library(dplyr)
library(purrr)

dt %>%
  group_by(id) %>%
  group_by(d = seq(n()) %% 2,.add = TRUE) %>%
  mutate(ind = accumulate(ret[-1],.init = ind[1],~ (..2 + 1) * ..1)) %>%
  select(-d)

# A tibble: 10 x 5
# Groups:   id,d [4]
       d id     time      ret   ind
   <dbl> <chr> <int>    <dbl> <dbl>
 1     1 a         1  0.00554  120 
 2     0 a         2 -0.00280  125 
 3     1 a         3  0.0178   122.
 4     0 a         4  0.00187  125.
 5     1 a         5  0.0114   124.
 6     0 b         1  0.00416  120 
 7     1 b         2  0.0123   125 
 8     0 b         3  0.00237  120.
 9     1 b         4 -0.00365  125.
10     0 b         5  0.0111   122.

或者在 base R 中我们可以这样做:

do.call(rbind,lapply(split(dt,dt$id),function(x) {
  x$ind <- c(x$ind[1],Re(Reduce(function(a,b) Im(a) * (1 + b) + Re(a) * 1i,init = x$ind[2] + x$ind[1] * 1i,x$ret[3:nrow(x)],accumulate = TRUE)))
  x
}))

     id time          ret      ind
a.1   a    1  0.005543269 120.0000
a.2   a    2 -0.002802719 125.0000
a.3   a    3  0.017751634 122.1302
a.4   a    4  0.001873201 125.2342
a.5   a    5  0.011425261 123.5256
b.6   b    1  0.004155261 120.0000
b.7   b    2  0.012295066 125.0000
b.8   b    3  0.002366797 120.2840
b.9   b    4 -0.003653828 124.5433
b.10  b    5  0.011051443 121.6133
,

作为一种解决方法,您可以在编辑过的情况下使用以下技巧。 请注意,您可以为任意数量的同步系列更改此设置

  • 我刚刚添加了一个额外的 group_by 语句,该语句基于使用 seq(n()) %% 2 的所需变量数的模序列
set.seed(13)
dt <- data.frame(id = rep(letters[1:2],each = 5),time = rep(1:5,2),ret = rnorm(10)/100)
dt$ind <- ifelse(dt$time == 1,120,ifelse(dt$time == 2,125,as.numeric(NA)))
library(dplyr,warn.conflicts = F)

dt %>% group_by(id) %>%
  group_by(d = seq(n()) %% 2,.add = TRUE) %>%
  mutate(ind = cumprod(1 + duplicated(id) * ret)* ind[1])
#> # A tibble: 10 x 5
#> # Groups:   id,d [4]
#>    id     time      ret   ind     d
#>    <chr> <int>    <dbl> <dbl> <dbl>
#>  1 a         1  0.00554  120      1
#>  2 a         2 -0.00280  125      0
#>  3 a         3  0.0178   122.     1
#>  4 a         4  0.00187  125.     0
#>  5 a         5  0.0114   124.     1
#>  6 b         1  0.00416  120      0
#>  7 b         2  0.0123   125      1
#>  8 b         3  0.00237  120.     0
#>  9 b         4 -0.00365  125.     1
#> 10 b         5  0.0111   122.     0

旧答案:不使用 purrr

library(tidyverse)

set.seed(13)
dt <- data.frame(id = rep(letters[1:2],each = 4),time = rep(1:4,ret = rnorm(8)/100)
dt$ind <- if_else(dt$time == 1,100,as.numeric(NA))
dt
#>   id time          ret ind
#> 1  a    1  0.005543269 100
#> 2  a    2 -0.002802719  NA
#> 3  a    3  0.017751634  NA
#> 4  a    4  0.001873201  NA
#> 5  b    1  0.011425261 100
#> 6  b    2  0.004155261  NA
#> 7  b    3  0.012295066  NA
#> 8  b    4  0.002366797  NA

dt %>% group_by(id) %>%
  mutate(ind = cumprod(1 + duplicated(id) * ret)* ind[1])
#> # A tibble: 8 x 4
#> # Groups:   id [2]
#>   id     time      ret   ind
#>   <chr> <int>    <dbl> <dbl>
#> 1 a         1  0.00554 100  
#> 2 a         2 -0.00280  99.7
#> 3 a         3  0.0178  101. 
#> 4 a         4  0.00187 102. 
#> 5 b         1  0.0114  100  
#> 6 b         2  0.00416 100. 
#> 7 b         3  0.0123  102. 
#> 8 b         4  0.00237 102.

reprex package (v2.0.0) 于 2021 年 7 月 27 日创建

,

Anoushiravan 的建议解决了我的问题。这是满足我所有要求的最终代码:(i)按 id 分组,(ii)按时间条件(此处,时间>=2),(iii)除 100 以外的起始值(此处,ind = 150):

library(dplyr)
library(purrr)


set.seed(13)
dt <- data.frame(id = rep(letters[1:2],ret = rnorm(8)/100)
dt$ind <- if_else(dt$time == 2,150,as.numeric(NA))
dt

dt_tmp <- dt %>%
  group_by(id) %>%
  filter(time>=2) %>%
  mutate(
    ind =  accumulate(ret[-1],~ (..2 + 1) * ..1)
         )

dt_tmp <- dt_tmp %>% select(id,time,ind)

dt <- dt %>% left_join(dt_tmp,by = c("id","time"))
dt <- rename(dt,ind_orig = ind.x)
dt <- rename(dt,ind = ind.y)
rm(dt_tmp) 

最终输出:

enter image description here


编辑:

最后,我使用循环解决了更高延迟的问题(我最初想避免):

my_projection <- function(index,ret,lag) {
  if (length(index) != length(ret)) {
    print("error: length of vectors does not match")
    break;
  }
  if (lag < 0) {
    print("error: lag < 0")
    break;
  }
  else {
    for(i in 1:length(index)){
      if (i<=lag){
        print(index[i])
      }
      else {
        print(index[i-lag]*(1+ret[i]))
        index[i] = index[i-lag]*(1+ret[i])
    }
  }
  }
  return(index)
}


dt <- dt %>% group_by(id) %>%
  mutate(ind = my_projection(ind,2))

输出:

enter image description here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...