将列中的NA替换为最近的,就日期列而言,在组内,非NA,具有条件,在R中

vu8f3i0k  于 2023-10-13  发布在  其他
关注(0)|答案(1)|浏览(109)

我有一个类似于下面的框架-我的实际更大-并且想知道如何将NA与组内最近的非NA进行估算,对于一个整数变量-就日期而言最近-距离观察不到30天,之前或之后。当出现平局时,我想选择较早的日期,而不是较晚的日期。我找到了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)
l7mqbcuq

l7mqbcuq1#

样本数据并没有挑战“向前”寻找最近日期的前景。一些自动滚动/最近的填充开始工作,但没有一个固有的荣誉“30天的限制”需要。例如,在data.table-speak中,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

fun路由器的组件:

  • outer(..)计算日期差异;由于我们不想匹配具有NA值的日期,因此在此内部,我们使用replace(dt, is.na(val), NA)来NA出这些日期(然后使用abs(.)整个事件)
  • 一个自然的“好处”是,它的对角线要么是0(self减去self是0),这在赋值时是自引用的,要么是NA(当xNA时),因此非NA的值永远不会被其他值替换
  • z(日期差异矩阵)中,我们将lim上的差异值替换为NA
  • 此时,z中的所有值应该是NA(无法匹配)或小于或等于lim的正值,其中0将始终是which.min找到的最小值
  • 因为z的每一行对应于我们想要的输出值,我们将使用apply(z, 1, ..),它将遍历每一行
  • 如果我们使用which.min(y)本身,那么当没有非NA值时,它将返回c(),这将破坏我们所需要的;但是,将[1]添加到 * 会强制 * 在这种情况下返回NA,在所有其他情况下返回单个整数,因此which.min(y)[1]将返回zNA中日期差最小的列
  • (任何索引为[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和base-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

相关问题