矢量化跨行的迭代操作

时间:2017-05-22 12:39:41

标签: r for-loop data.table vectorization

我在这里看到很多关于循环矢量化的问题,但是找不到任何涉及矢量化for循环以基于下面一行中的单元格的值来填充单元格(如果我是道歉的话)我只是失明了......)。

我有一个包含160万行工资的数据框,以及每个人开始赚取该工资的日期。每个人可以有多个工资,因此有多个行,每个行都有不同的更新日期。

虚拟数据集的代码如下:

df1 <- data.frame("id" = c(1,1,2,2,3,3,4,4,5,5,6,6),
                  "salary" = c(15456,16594,
                               17364,34564,
                               34525,33656,
                               23464,23467,
                               16794,27454,
                               40663,42743),
                  "start_date" = sample(seq(as.Date('2016/01/01'),as.Date(Sys.Date()), by="day"), 12))

df1 <- df1[order(df1$id,df1$start_date),]

我想创建一个列,其中包含每个薪水的结束日期,该列计算为后续薪资输入的前一天。如果没有后续的工资条目,则将其设置为今天的日期。这是我的代码,包括for循环,这样做:

df1$end_date <- Sys.Date()

for (i in 1:(nrow(df1)-1)){
  if(df1[i,1]== df1[i+1,1]){
    df1[i,4] <- df1[i+1,3]-1
  }
  print(i)
} 

但是,我知道for循环不是最有效的方法,但我如何进行矢量化呢?

4 个答案:

答案 0 :(得分:2)

使用dplyr包,您可以执行以下操作:

library(dplyr)
df1 %>% 
group_by(id) %>% 
mutate(end_date=lead(start_date-1,default=Sys.Date()))

返回:

id salary start_date   end_date
   <dbl>  <dbl>     <date>     <date>
 1     1  15456 2016-02-14 2016-03-02
 2     1  16594 2016-03-03 2017-05-22
 3     2  17364 2016-01-17 2016-11-28
 4     2  34564 2016-11-29 2017-05-22
 5     3  33656 2016-08-17 2016-11-25
 6     3  34525 2016-11-26 2017-05-22
 7     4  23464 2016-01-20 2017-05-05
 8     4  23467 2017-05-06 2017-05-22
 9     5  27454 2016-02-29 2016-12-15
10     5  16794 2016-12-16 2017-05-22
11     6  42743 2016-03-14 2017-01-29
12     6  40663 2017-01-30 2017-05-22

答案 1 :(得分:1)

您可以使用library(data.table)

setDT(df1)[, end_date := shift(start_date, type = "lead", fill = Sys.Date()), id][]

答案 2 :(得分:0)

使用data.table和shift,您可以使用以下内容:

df1 <- data.table("id" = c(1,1,2,2,3,3,4,4,5,5,6,6),
                  "salary" = c(15456,16594,
                               17364,34564,
                               34525,33656,
                               23464,23467,
                               16794,27454,
                               40663,42743),
                  "start_date" = sample(seq(as.Date('2016/01/01'),as.Date(Sys.Date()), by="day"), 12))

df1 <- df1[order(id,start_date),]

df1[, EndDate := shift(start_date, type="lead"), id]
df1[is.na(EndDate), EndDate := Sys.Date()]

答案 3 :(得分:0)

如果我理解您的问题,以下基本R代码将起作用。

df1$end <- ave(df1$start_date, df1$id, FUN=function(x) c(tail(x, -1) - 1, Sys.Date()))

ave用于执行组级操作。执行的功能采用第二个到最终日期并减去1.这与最终日期连接。

返回

df1
   id salary start_date        end
1   1  15456 2016-03-20 2016-12-06
2   1  16594 2016-12-07 2017-05-22
3   2  17364 2016-10-17 2016-07-27
4   2  34564 2016-07-28 2017-05-22
5   3  34525 2016-05-26 2016-05-01
6   3  33656 2016-05-02 2017-05-22
7   4  23464 2017-04-17 2016-01-19
8   4  23467 2016-01-20 2017-05-22
9   5  16794 2016-09-12 2016-05-06
10  5  27454 2016-05-07 2017-05-22
11  6  40663 2016-10-03 2016-03-28
12  6  42743 2016-03-29 2017-05-22