【问题标题】:How to change the value in a row if condition met in the previous row under dplyr如果在 dplyr 下的上一行中满足条件,如何更改一行中的值
【发布时间】:2018-07-31 02:36:05
【问题描述】:

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

这是创建一个调整的日期,如果当前 supp_date 小于前一个 supp_date + tablet 的条件满足。

示例数据:(这是包含else case 的新示例数据。)

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

【问题讨论】:

  • "target_dose" 列中有什么内容?你的意思是tablet?我假设tablet 的单位是天?
  • @MauritsEvers 对此感到抱歉。对代码进行了一些编辑,忘记更改它。是的,你可以假设。

标签: r dplyr


【解决方案1】:

我们可以用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 一起使用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"

数据

df <- structure(list(supp_date = structure(c(17366, 17385, 17417, 17469, 
17478, 17507), class = "Date"), tablet = c(30L, 30L, 30L, 30L, 
30L, 30L)), .Names = c("supp_date", "tablet"), row.names = c(NA, 
 -6L), class = "data.frame")

【讨论】:

  • 感谢您的解决方案。你介意解释一下这背后的原因吗?
  • @Fred 在for 循环中,您在每一步都分配了新值。这里的逻辑与accumulate 相同
【解决方案2】:

以下重现您的预期输出

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

请注意,这需要对更大的样本数据进行彻底的测试;使用您提供的示例数据,我们永远不会将其纳入if_else(或在您的情况下为if {...} else {...})条件的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

【讨论】:

  • 我不知道dplyr 有一个内置的if_else() 函数。谢谢你把我介绍给lag()
  • 感谢您的回答。我现在就试试。我刚刚将第 4 行更改为 2017-10-30
  • @CristianE.Nuno 我认为一般if_else 比base R 的ifelse 更受欢迎;除其他外,if_elseifelse 快。 Here 是关于这个主题的一个有趣的 SO 帖子。
  • @MauritsEvers 第 4 行日期更改为 2017-10-30 后,代码不起作用。它保持相同的循环向前,不会在else 处中断。我应该首先提供else 案例。对此感到抱歉。
  • @Fred 嗯。也许我误解了;但是对于新数据,我看不到我们如何将其放入else 部分。您能否修复上面的示例代码并使其可重现?当我尝试运行您的 for 循环时,出现错误。
猜你喜欢
  • 2019-06-05
  • 2015-02-28
  • 1970-01-01
  • 2021-11-24
  • 2018-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
相关资源
最近更新 更多