【问题标题】:Updating column value with its predecessor使用其前身更新列值
【发布时间】:2017-10-05 15:59:49
【问题描述】:

考虑一个测试数据集

 test = data.table("a"=c(NA,NA, 0, NA, NA), "b"=c(1,3,4,7,8), "c"=c(NA, 2,1,3,1), "group"=c(1,1,1,1,1))
    a  b c  group
 1: NA 1 NA 1
 2: NA 3  2 1
 3:  0 4  1 1
 4: NA 7  3 1
 5:  0 8  1 1
 6: NA 9  1 1

我想更新一列的值,这样:

  t = a_{i-1}+c_i
  if(is.na(a_i)) {
    a_i = t       
  }

这应该会产生以下数据集:

    a  b c  group
 1: NA 1 NA 1
 2: NA 3  2 1
 3:  0 4  1 1
 4:  3 7  3 1
 5:  0 8  1 1
 6:  1 9  1 1

我将示例缩减为一组,但可以有多个。我宁愿避免使用 for 循环解决方案,因为我的实际数据集有数百万行。

【问题讨论】:

  • 这是因为 NA 加上任何东西仍然是 NA。第 4 行计算 0 + 3 = 3,第 5 行计算第 4 行中的 NA(尚未被替换)加上 1,并生成 NA。如果您想按顺序更新,则不能使用:=*apply 结构,您必须使用for 循环或类似结构。
  • 为什么a 在第 3 行仍然等于 0?为什么不是所有NA

标签: r dplyr data.table


【解决方案1】:

当你在第一个非NA a 之前有一个NA 值时,我不清楚逻辑是什么。我认为是保持a 的当前值。在那种情况下,那么这可能就是你所期待的:

> library(zoo)
> test = data.table("a"=c(NA,NA, 0, NA, NA, 1, 2), 
+                   "b"=c(1,3,4,7,8, 9, 10), 
+                   "c"=c(NA, 2,1,3,1, 1, 2), 
+                   "group"=c(1,1,1,1,1,1, 1))
> test
    a  b  c group
1: NA  1 NA     1
2: NA  3  2     1
3:  0  4  1     1
4: NA  7  3     1
5: NA  8  1     1
6:  1  9  1     1
7:  2 10  2     1
> 
> test[, `:=`(tmp_idx = shift(cumsum(!is.na(a)), fill = 0) > 0)][
+   , c_cum := cumsum(c), by = tmp_idx][
+     tmp_idx == 0, c_cum := 0][
+       tmp_idx == TRUE, a := NA][
+         , a := na.locf(a, na.rm = FALSE)]
> test
    a  b  c group tmp_idx c_cum
1: NA  1 NA     1   FALSE     0
2: NA  3  2     1   FALSE     0
3:  0  4  1     1   FALSE     0
4:  0  7  3     1    TRUE     3
5:  0  8  1     1    TRUE     4
6:  0  9  1     1    TRUE     5
7:  0 10  2     1    TRUE     7
> 
> test[, a := ifelse(is.na(shift(a)), a, shift(a)) + c_cum][
+   , `:=`(tmp_idx = NULL, c_cum = NULL)]
> test
    a  b  c group
1: NA  1 NA     1
2: NA  3  2     1
3:  0  4  1     1
4:  3  7  3     1
5:  4  8  1     1
6:  5  9  1     1
7:  7 10  2     1

如果您有更多组,请在[.data.table 调用中使用.SD 上的上述内容,并带有by 参数。

【讨论】:

  • 可能有更聪明的方法来填写第一个非NA 值,然后使用上面的zoo::na.locf 函数。
  • 感谢本杰明的回复。我犯了一个错误,并没有说明如果你有 NA 和一些额外的数字会发生什么。在您的情况下,计数继续,在我的示例中,我想使用 NA 之后的值重新启动计数器。在这种情况下,您的解决方案将不起作用:(
猜你喜欢
  • 2011-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-16
  • 1970-01-01
  • 1970-01-01
  • 2019-01-20
  • 2021-09-19
相关资源
最近更新 更多