R.n 另一列数据框中的前一个元素

R. n Previous Elements in a data frame of another column

我想添加一个新列,其中包含另一列中前 n 个元素的向量(或列表)。分组后计算。

这里是一个 n=2 的例子。输入:

v0 = c(rep("a",5),rep("b",5))
v1 = 1:10
DF1 <- data.frame(v0,v1)

> DF1
   v0 v1
1   a  1
2   a  2
3   a  3
4   a  4
5   a  5
6   b  6
7   b  7
8   b  8
9   b  9
10  b 10

输出:新列应该是整数向量(或列表)并包含以下值:

> DF2_L
   v0 v1    myL
1   a  1 NA, NA
2   a  2  1, NA
3   a  3   2, 1
4   a  4   3, 2
5   a  5   4, 3
6   b  6   5, 4
7   b  7   6, 5
8   b  8   7, 6
9   b  9   8, 7
10  b 10   9, 8

一个简单的解决方案是

DF2 <- DF1 %>% group_by(v0) %>% 
  mutate(i1=lag(v1,1), i2=lag(v1,2), 
                      myL = mapply(c, i1, i2, SIMPLIFY = F))%>%
  select(-c(i1,i2))

但是这只是一个简化的 table。对于我的计算 n 是 36。这意味着我需要为 lag(v1,1)、lag(v1,2) ... lag(v1,36) 创建 36 个新的“虚拟”列并删除它们,将这些值组合成一个列表。这不方便。必须换一种方式。

我想使用 rollapply。使用 F = list 但我收到错误消息

t <- DF1 %>% group_by(v0) %>% 
  mutate( myL= rollapply(lag(v1),
                         2, fill=NA, align="right",
                         list))

Error: Problem with `mutate()` input `myL`.
x “x” : attempt to define invalid zoo object
i Input `myL` is `rollapply(lag(v1), 2, fill = NA, align = "right", list)`.
i The error occurred in group 1: v0 = "a".
Run `rlang::last_error()` to see where the error occurred.

当我使用 FUN = c.我将每个元素作为单独的列

t <- DF1 %>% group_by(v0) %>% 
  mutate( myL= rollapply(lag(v1),
                         2, fill=NA, align="right",
                         c))

   v0       v1 myL[,1]  [,2]
   <fct> <int>   <int> <int>
 1 a         1      NA    NA
 2 a         2      NA     1
 3 a         3       1     2
 4 a         4       2     3
 5 a         5       3     4
 6 b         6      NA    NA
 7 b         7      NA     6
 8 b         8       6     7
 9 b         9       7     8
10 b        10       8     9

这符合你的要求吗?

v0 = c(rep("a",5),rep("b",5))
v1 = 1:10
DF1 <- data.frame(v0,v1)

n <- 2

bind_cols(DF1,
          map_dfc(1:n,
                  ~ lag(DF1$v1, .x))) %>%  #simple loop creating lagged columns
  group_by(v0, v1) %>%   # the variables we don't want to include
  nest() %>%
  mutate(my_list = list(set_names(unlist(data), NULL))) #make each tibble into an unnamed vector
# A tibble: 10 x 4
# Groups:   v0, v1 [10]
#   v0       v1 data             my_list  
#   <chr> <int> <list>           <list>   
# 1 a         1 <tibble [1 x 2]> <int [2]>
# 2 a         2 <tibble [1 x 2]> <int [2]>
# 3 a         3 <tibble [1 x 2]> <int [2]>
# 4 a         4 <tibble [1 x 2]> <int [2]>
# 5 a         5 <tibble [1 x 2]> <int [2]>
# 6 b         6 <tibble [1 x 2]> <int [2]>
# 7 b         7 <tibble [1 x 2]> <int [2]>
# 8 b         8 <tibble [1 x 2]> <int [2]>
# 9 b         9 <tibble [1 x 2]> <int [2]>
#10 b        10 <tibble [1 x 2]> <int [2]>

xx$my_list
# [[1]]
# [1] NA NA
# 
# [[2]]
# [1]  1 NA
# 
# [[3]]
# [1] 2 1
# 
# [[4]]
# [1] 3 2
# 
# [[5]]
# [1] 4 3
# 
# [[6]]
# [1] 5 4
# 
# [[7]]
# [1] 6 5
# 
# [[8]]
# [1] 7 6
# 
# [[9]]
# [1] 8 7
# 
# [[10]]
# [1] 9 8

编辑:我实际上不确定你想要什么,如果你只想要串联滞后值的向量,那么这样做会更清楚:

lagged_cols <- map_dfc(1:n,
                       ~ lag(DF1$v1, .x))

apply(lagged_cols, 1, paste, collapse=" ")
# [1] "NA NA" "1 NA"  "2 1"   "3 2"   "4 3"   "5 4"   "6 5"   "7 6"   "8 7"  
# [10] "9 8"  

首先注意问题中的t并没有将两个元素放在t的不同列中。 t有3列,不是4列,但第3列是矩阵。

dim(t)
## [1] 10  3

dim(t[[3]])
## [1] 10  2

滚动应用

要使用 c 获取整数向量列表 运行 rollapply 的列,然后将结果矩阵拆分为列表:

library(dplyr)
library(zoo)

k <- 2
out <- DF1 %>%
  group_by(v0) %>%
  mutate(v2 = rollapply(c(rep(NA, k), v1), list(-seq(k)), c) %>%
              split(1:n()) %>%
              unname) %>%
  ungroup

str(out)

给予:

tibble [10 x 3] (S3: tbl_df/tbl/data.frame)
 $ v0: chr [1:10] "a" "a" "a" "a" ...
 $ v1: int [1:10] 1 2 3 4 5 6 7 8 9 10
 $ v2:List of 10
  ..$ : int [1:2] NA NA
  ..$ : int [1:2] 1 NA
  ..$ : int [1:2] 2 1
  ..$ : int [1:2] 3 2
  ..$ : int [1:2] 4 3
  ..$ : int [1:2] NA NA
  ..$ : int [1:2] 6 NA
  ..$ : int [1:2] 7 6
  ..$ : int [1:2] 8 7
  ..$ : int [1:2] 9 8

lag.zoo

类似的方法是转换为动物园并使用 lag.zoo。它可以处理多个滞后。之后我们使用 coredata 从 zoo 转换回矩阵。

请注意,dplyr 会覆盖基础 lag 泛型,它会禁用其他包中的所有 lag 方法,因此请务必在加载 dplyr 时排除 dplyr 的 lag。如下面的代码所示。如果需要,仍然可以使用 dplyr 的 lag dplyr::lag 。交替使用下面的 stats::lag 以确保调度 lag.zoo

结果与上面的 rollapply 相同。

library(dplyr, exclude = "lag")  # important!
library(zoo)

k <- 2
out <- DF1 %>%
  group_by(v0) %>%
  mutate(v2 = lag(zoo(c(rep(NA, k-1), v1)), -seq(2)) %>%
              coredata %>%
              split(1:n()) %>%
              unname) %>%
  ungroup 

toString

另一种可能性(不等同)是使用 toString 创建一个字符串列。每个单元格都是一个字符串(不是字符向量)。

k <- 2
DF1 %>%
  group_by(v0) %>%
  mutate(v2 = rollapply(c(rep(NA, k), v1), list(-seq(k)), toString)) %>%
  ungroup

给予:

# A tibble: 10 x 3
   v0       v1 v2    
   <chr> <int> <chr> 
 1 a         1 NA, NA
 2 a         2 1, NA 
 3 a         3 2, 1  
 4 a         4 3, 2  
 5 a         5 4, 3  
 6 b         6 NA, NA
 7 b         7 6, NA 
 8 b         8 7, 6  
 9 b         9 8, 7  
10 b        10 9, 8  

一个data.table解决方案:

library(data.table)
setDT(DF1)
DF1[, myL := sapply(transpose(shift(v1, n=1:2)), toString), by = v0]

#     v0 v1    myL
#  1:  a  1 NA, NA
#  2:  a  2  1, NA
#  3:  a  3   2, 1
#  4:  a  4   3, 2
#  5:  a  5   4, 3
#  6:  b  6 NA, NA
#  7:  b  7  6, NA
#  8:  b  8   7, 6
#  9:  b  9   8, 7
# 10:  b 10   9, 8

这导致列是向量列表:

# > sapply(DF1, class)
#          v0          v1         myL 
# "character"   "integer"      "list" 

备注

  • 您可以替换函数c来获取列表列表(list)、字符串列表(toString)等
  • 如果您不想使用 data.table,您可以使用 setDF() 将结果变为 data.frame
  • 也可以在常规 dplyr 设置中工作:
DF1 %>% group_by(v0) %>% mutate(myL = lapply(transpose(shift(v1, n=1:2)), c))