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

问题描述

我想添加一个新列,其中包含来自另一列的前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))

但是,这只是一个简化表。对于我的计算,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),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),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,.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

rollapply

要获取作为整数矢量列表的列,请使用rollapply运行c,然后将结果矩阵分成一个列表:

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。如下面的代码所示。如果需要,可以通过使用lag来使用dplyr的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,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,则可以使用data.frame将结果转换为setDF()
  • 也可以在常规dplyr设置下使用:
DF1 %>% group_by(v0) %>% mutate(myL = lapply(transpose(shift(v1,c))