如果条件在dplyr下的上一行中满足,则如何更改行中的值

时间:2018-07-31 02:36:06

标签: r dplyr

是否有其他方法可以替换dplyr下的以下代码,以避免显式循环和数据名称来实现以下目的?

如果满足当前supp_date小于先前supp_date +数位板的条件,这将创建一个调整日期。

样本数据:(这是带有else格的新样本数据。)

test <- read.table(text =
    "supp_date    tablet
2017-07-19    30
2017-08-07    30
2017-09-08    30
2017-10-30    30
2017-11-08    30
2017-12-07    30", header = T)

R代码:

test$supp_date <- as.Date(test$supp_date, "%Y-%m-%d")
test$adj_fill_dt <- as.Date(NA, "%Y-%m-%d")
test$adj_fill_dt[1] <- test$supp_date[1]
for(i in 2:6) {
  if (test[i, "supp_date"] < test[i-1, "adj_fill_dt"] + test[i-1, "tablet"]) {
      test[i, "adj_fill_dt"] <- test[i-1, "adj_fill_dt"] + test[i-1, "tablet"]
  } else {
    test[i, "adj_fill_dt"] <- test[i, "supp_date"]
  }
}

发件人:

supp_date    tablet
2017-07-19    30
2017-08-07    30
2017-09-08    30
2017-10-30    30
2017-11-08    30
2017-12-07    30

收件人:

supp_date    tablet   adj_fill_dt
2017-07-19    30       2017-07-19
2017-08-07    30       2017-08-18
2017-09-08    30       2017-09-17
2017-10-30    30       2017-10-30
2017-11-08    30       2017-11-29
2017-12-07    30       2017-12-29

2 个答案:

答案 0 :(得分:2)

我们可以使用accumulate

进行递归操作
library(tidyverse)
df %>% 
  mutate(tmp = as.numeric(supp_date),
        adj_fill_dt = as.Date(accumulate(tmp[-1], ~
             pmax(.x + tablet[1], .y), .init = tmp[1]), 
                           origin = '1970-01-01'),
        tmp = NULL)
#   supp_date tablet adj_fill_dt
#1 2017-07-19     30  2017-07-19
#2 2017-08-07     30  2017-08-18
#3 2017-09-08     30  2017-09-17
#4 2017-10-30     30  2017-10-30
#5 2017-11-08     30  2017-11-29
#6 2017-12-07     30  2017-12-29

或者使用base R的{​​{1}}

Reduce

数据

v1 <- as.numeric(df$supp_date)
as.Date(Reduce(function(u, v)  pmax(u + 30, v), v1[-1], 
      init = v1[1], accumulate = TRUE), origin = '1970-01-01')
#[1] "2017-07-19" "2017-08-18" "2017-09-17" "2017-10-30" "2017-11-29"
#[6] "2017-12-29"

答案 1 :(得分:1)

以下内容再现了您的预期输出

library(tidyverse)

df %>%
    mutate(
        supp_date = as.Date(supp_date, format = "%Y-%m-%d"),
        adj_fill_dt = if_else(
            supp_date < supp_date[1] + cumsum(tablet),
            lag(supp_date[1] + cumsum(tablet), default = supp_date[1]),
            supp_date))
#   supp_date tablet adj_fill_dt
#1 2017-07-19     30  2017-07-19
#2 2017-08-07     30  2017-08-18
#3 2017-09-08     30  2017-09-17
#4 2017-10-11     30  2017-10-17
#5 2017-11-08     30  2017-11-16
#6 2017-12-07     30  2017-12-16

请注意,这需要对更大的样本数据进行彻底的测试;根据您提供的示例数据,我们绝不会将其纳入else(或您的情况if_else)条件的if {...} else {...}部分中。

这里的关键是要认识到在if条件下的递归关系可以重写为supp_date[1] + cumsum(tablet)


样本数据

df <- read.table(text =
    "supp_date    tablet
2017-07-19    30
2017-08-07    30
2017-09-08    30
2017-10-11    30
2017-11-08    30
2017-12-07    30", header = T)

更新

下面的代码重现了两个示例的输出

df %>%
    mutate(
        supp_date = as.Date(supp_date, format = "%Y-%m-%d"),
        grp = cumsum(!(supp_date < lag(supp_date[1] + cumsum(tablet), default = supp_date[1])))) %>%
    group_by(grp) %>%
    mutate(adj_fill_dt = lag(supp_date[1] + cumsum(tablet), default = supp_date[1]))
## A tibble: 6 x 4
## Groups:   grp [2]
#  supp_date  tablet   grp adj_fill_dt
#  <date>      <int> <int> <date>
#1 2017-07-19     30     1 2017-07-19
#2 2017-08-07     30     1 2017-08-18
#3 2017-09-08     30     1 2017-09-17
#4 2017-10-30     30     2 2017-10-30
#5 2017-11-08     30     2 2017-11-29
#6 2017-12-07     30     2 2017-12-29