在R中,将列中的NA替换为其在日期列中最接近的非NA值,且满足条件的值。

3
我有一个类似下面的数据框 - 实际上更大 - 想知道如何用组内最近的非 NA 值填充整数变量的 NA 值 - 最近的日期不超过观测日期的30天之内,无论是之前还是之后。当存在多个最近的非 NA 值时,我希望选择较早的日期而不是较晚的日期。我找到了this,但它没有考虑连续的 NA 值。
非常感谢任何帮助!
df <- data.frame(
  id=c(1,1,1,1,2,2,2,3,3,3,4,4),
  dates = c("2023-09-01", "2023-09-02", "2023-09-05", "2023-09-06","2023-09-10" , "2023-09-11",
            "2023-09-12", "2023-09-14", "2023-09-16", "2023-09-20", "2023-09-27", "2023-09-28"),
  x = c(10, NA, NA, 20, 20, NA, 30, 15, NA, NA, 40, NA)
)

# desired output

x1 <- c(10, 10, 20, 20, 20, 20, 30, 15, 15, 15, 40, 40)

为什么 x1[6] 是30,应该是20吧?因为有一个平局,应该选择最早的日期。 - undefined
啊,抱歉!谢谢你发现了这个问题,我会相应地进行编辑。 - undefined
相关帖子 https://stackoverflow.com/q/28072542/680068 - undefined
1个回答

3
样本数据并不挑战寻找最近日期的前景。一些自动滚动/最近填充开始工作,但它们中没有一个本质上符合所需的“30天限制”。例如,在`data.table`中,`roll="nearest"`将始终匹配最接近的日期,但不会查看日期差异以确定是否符合约束条件。
我建议提供备用数据,其中包含需要向前、向后查找的行,以及一个不匹配任何内容的行,并且我假设简单示例中的差异为8天(而不是30天):
DT2 <- data.table(id=1L, dates=as.Date("2023-09-01")+c(0,1,5,6,7,15), x=replace(1:6, c(3,5,6), NA), expect=c(1L, 2L, 4L, 4L, 4L, NA))[]
DT2
#       id      dates     x expect
#    <int>     <Date> <int>  <int>
# 1:     1 2023-09-01     1      1
# 2:     1 2023-09-02     2      2
# 3:     1 2023-09-06    NA      4  # matches row 4
# 4:     1 2023-09-07     4      4
# 5:     1 2023-09-08    NA      4  # matches row 4
# 6:     1 2023-09-16    NA     NA  # too far, no match

值得注意的是,第3行在第2行和第4行的限制范围内,但由于第4行更接近,应使用其值。
使用`data.table`,我认为这个方法可行。
fun <- function(dt, val, lim) {
  z <- abs(outer(dt, replace(dt, is.na(val), NA), `-`))
  z[z > lim] <- NA
  val[apply(z, 1, function(y) which.min(y)[1])]
}
DT2[, x1 := fun(dates, x, lim = 8), by = .(id)]
DT2
#       id      dates     x expect    x1
#    <int>     <Date> <int>  <int> <int>
# 1:     1 2023-09-01     1      1     1
# 2:     1 2023-09-02     2      2     2
# 3:     1 2023-09-06    NA      4     4
# 4:     1 2023-09-07     4      4     4
# 5:     1 2023-09-08    NA      4     4
# 6:     1 2023-09-16    NA     NA    NA

那个函数的组成部分:
  • outer(..)计算日期差异;由于我们不想与具有NA值的日期匹配,因此在其中我们使用replace(dt, is.na(val), NA)将这些日期替换为NA(然后对整个结果应用abs(.)
  • 这种方法的一个自然的好处是,对角线要么是0(自己减去自己得到0),在分配值时是自我参照的,要么是NA(当xNA时),因此非NA值永远不会被替换为其他值
  • z(日期差异矩阵)中,我们将差异大于lim的值替换为NA
  • 此时,z中的所有值应该是NA(无法进行匹配)或小于或等于lim的正值,其中0始终是which.min找到的最小值
  • 因为z的每一行对应我们想要的一个输出值,所以我们将使用apply(z, 1, ..),它将迭代每一行
  • 如果我们直接使用which.min(y),那么当没有非NA值时,它将返回c(),这将破坏我们的需求;然而,将[1]添加到which.min(y)强制它在这种情况下返回NA,在其他所有情况下返回单个整数,因此which.min(y)[1]将返回具有最低日期差异的z列或NA
  • (使用[NA]索引的任何内容都将是NA
使用最大差异为2天的原始数据进行演示,
DT <- as.data.table(df)[, dates := as.Date(dates)][]
DT
#        id      dates     x expect
#     <num>     <Date> <num>  <num>
#  1:     1 2023-09-01    10     10
#  2:     1 2023-09-02    NA     10
#  3:     1 2023-09-05    NA     20
#  4:     1 2023-09-06    20     20
#  5:     2 2023-09-10    20     20
#  6:     2 2023-09-11    NA     20
#  7:     2 2023-09-12    30     30
#  8:     3 2023-09-14    15     15
#  9:     3 2023-09-16    NA     15
# 10:     3 2023-09-20    NA     15
# 11:     4 2023-09-27    40     40
# 12:     4 2023-09-28    NA     40

DT[, x1 := fun(dates, x, lim = 30), by = .(id)]
DT
#        id      dates     x expect    x1
#     <num>     <Date> <num>  <num> <num>
#  1:     1 2023-09-01    10     10    10
#  2:     1 2023-09-02    NA     10    10
#  3:     1 2023-09-05    NA     20    20
#  4:     1 2023-09-06    20     20    20
#  5:     2 2023-09-10    20     20    20
#  6:     2 2023-09-11    NA     20    20
#  7:     2 2023-09-12    30     30    30
#  8:     3 2023-09-14    15     15    15
#  9:     3 2023-09-16    NA     15    15
# 10:     3 2023-09-20    NA     15    15
# 11:     4 2023-09-27    40     40    40
# 12:     4 2023-09-28    NA     40    40

我们这里并不严格需要使用 `data.table`。(请注意,之前的版本中使用了 `fcoalesce` 并且有 `dplyr::coalesce` 和基于 R 的变体的翻译,但是 `fun` 现在不再需要它,所以这些额外的步骤已经被移除。)
library(dplyr)

df %>%
  mutate(dates = as.Date(dates)) %>%
  mutate(x1 = fun(dates, x, lim = 30), .by = id)
#    id      dates  x expect x1
# 1   1 2023-09-01 10     10 10
# 2   1 2023-09-02 NA     10 10
# 3   1 2023-09-05 NA     20 20
# 4   1 2023-09-06 20     20 20
# 5   2 2023-09-10 20     20 20
# 6   2 2023-09-11 NA     20 20
# 7   2 2023-09-12 30     30 30
# 8   3 2023-09-14 15     15 15
# 9   3 2023-09-16 NA     15 15
# 10  3 2023-09-20 NA     15 15
# 11  4 2023-09-27 40     40 40
# 12  4 2023-09-28 NA     40 40

和基本R:

# convert to Date-class
df$dates <- as.Date(df$dates)
# prefill, needed for `split(..)<-` to work
df$x1 <- NA

split(df, df$id) <- split(df, df$id) |>
  lapply(function(X) transform(X, x1 = fun(dates, x, lim = 8)))
df
#    id      dates  x expect x1
# 1   1 2023-09-01 10     10 10
# 2   1 2023-09-02 NA     10 10
# 3   1 2023-09-05 NA     20 20
# 4   1 2023-09-06 20     20 20
# 5   2 2023-09-10 20     20 20
# 6   2 2023-09-11 NA     20 20
# 7   2 2023-09-12 30     30 30
# 8   3 2023-09-14 15     15 15
# 9   3 2023-09-16 NA     15 15
# 10  3 2023-09-20 NA     15 15
# 11  4 2023-09-27 40     40 40
# 12  4 2023-09-28 NA     40 40

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接