通过填写缺失的日期并通过上下对称迭代日期以找到 r 中可用的最接近值来平均插补

Posted

技术标签:

【中文标题】通过填写缺失的日期并通过上下对称迭代日期以找到 r 中可用的最接近值来平均插补【英文标题】:mean imputation by filling in missing dates and by symetrically iterating over dates up and down to find the closest value available in r 【发布时间】:2022-01-16 06:20:18 【问题描述】:

我需要在每个 id 的可用日期之间估算所有缺失的日期,然后上下对称地估算缺失。此外,我并不总是需要两者之间的平均值,例如:当我上下 2 个日期并且我只看到 1 个值时,我会估算那个值。

df1 <- data.frame(id = c(11,11,11,11,11,11,11,11),
                  Date = c("2021-06-01", "2021-06-05", "2021-06-08", "2021-06-09", "2021-06-14", "2021-06-16", "2021-06-20", "2021-06-21"),
                  price = c(NA, NA,100, NA, 50, NA, 200, NA)
)

@lovalery 提供了一个很好的解决方案,可以解决对称迭代中缺失的插补 how to groupby and take mean of value by symetrically looping forward and backward on the date value in r

在上述解决方案中,使用了当前日期,但是当两者之间缺少大量日期时,这可能会成为问题。 因此,我想在两者之间插入所有缺失的日期,然后在两个方向上对称移动,直到在任一方向上至少获得 1 个值,如果需要 2 个值,我需要保留它。

更新:我们还需要考虑价格仅出现在第一个日期或最后一个日期的情况。此外,如果在多个日期出现相同的价格

df1 <- data.frame(id = c(11,11,11,11,11,11,11,11,
                     12,12,12,
                     13,13,13),
              Date = c("2021-06-01", "2021-06-05", "2021-06-08", "2021-06-09", "2021-06-14", "2021-06-16", "2021-06-20", "2021-06-21",
                       "2021-07-01","2021-07-03","2021-07-05",
                       "2021-08-01","2021-08-03","2021-08-05"),
              price = c(200, NA,100, NA, 50, NA, 200, NA,
                        10,NA,NA,
                        NA,NA,20)

)

我使用了@lovalery 的函数 NA_imputations_dates_v2

df1 <- setDT(df1)
df2 <- NA_imputations_dates_v2(df1)
df3 <- merge(df1,df2,by = c("id","Date"),all.x = T)

【问题讨论】:

@lovalery 可能存在日期差异可能很大的情况,因此不是检查可用的邻近值,而是添加所有日期然后在两个方向上迭代以便估算值将接近可用的最接近的日期值。让我知道你的 cmets 嗨@Rijin,感谢您发布新问题。请在下面找到一个可能的答案。如果它满足您的需求,请考虑将此答案标记为“已验证”和/或“已投票”。干杯。 @lovalery 您的解决方案完美运行。但是在我们的工作中,让我们尝试预见所有可能发生的错误,以便我们可以提出一个可扩展的解决方案。如果价格值仅出现在开始日期或结束日期,您提供的解决方案将引发错误。我在问题中更新了一个新的数据框代码。 错误如下:“vecseq(f__, len__, if (allow.cartesian || notjoin || !anyDuplicated(f__, : Join 结果为 41 行;超过 30 = nrow( x)+nrow(i). 检查 i 中的重复键值,每个键值都加入 x 中的同一组。如果没问题,请尝试 by=.EACHI 为每个组运行 j 以避免大分配. 如果您确定要继续,请使用 allow.cartesian=TRUE 重新运行。否则,请在 FAQ、Wiki、Stack Overflow 和 data.table 问题跟踪器中搜索此错误消息以获取建议。 调用自:vecseq(f__, len__, if (allow.cartesian || notjoin || !anyDuplicated(f__, incomparables = c(0L, NA_integer_))) NULL else as.double( nrow(x) + nrow(i)))" 【参考方案1】:

请在下面找到使用data.tablepadr 库的一种可能的解决方案。

我构建了一个函数让它更易于使用。

Reprex

您的数据集 #1
df1 <- data.frame(id = c(11,11,11,11,11,11,11,11),
                  Date = c("2021-06-01", "2021-06-05", "2021-06-08", "2021-06-09", "2021-06-14", "2021-06-16", "2021-06-20", "2021-06-21"),
                  price = c(NA, NA,100, NA, 50, NA, 200, NA))
NA_imputations_dates() 函数的代码
library(data.table)
library(padr)

NA_imputations_dates <- function(x) 
  
  setDT(x)[, Date := as.Date(Date)]
  
  x <- pad(x, interval = "day", group = "id")
  
  setDT(x)[, rows := .I]
  
  z <- x[, .I[!is.na(price)]]
  
  id_1 <- z[-length(z)]
  id_2 <- z[-1]
  
  values <- x[z, .(price = price, id = id)]
  values_1 <- values[-nrow(values)]
  names(values_1) <- c("price_1", "id_o1")
  values_2 <- values[-1]
  names(values_2) <- c("price_2", "id_o2")
  
  subtract <- z[-1] - z[-length(z)]
  
  r <- data.table(id_1, values_1, id_2, values_2, subtract)
  
  r <- r[, `:=` (id_mean = fifelse(subtract > 2 & subtract %% 2 == 0, id_1+(subtract/2), (id_1+id_2)/2),
                 mean = fifelse(subtract >= 2 & subtract %% 2 == 0 & id_o1 == id_o2, (price_1+price_2)/2, NA_real_))
         ][, `:=` (price_1 = NULL, id_1 = NULL, id_o1 = NULL, id_2 = NULL, price_2 = NULL, id_o2 = NULL, subtract = NULL)
           ][x, on = .(id_mean = rows)][, dummy := cumsum(!is.na(mean)), by = .(id)]
  
  h <-  r[, .(price = na.omit(price)), by = .(dummy)]
  
  Results <- r[, price := NULL
               ][h, on = .(dummy)
                 ][, price := fifelse(!is.na(mean), mean, price)
                   ][, `:=` (id_mean = NULL, mean = NULL, dummy = NULL)][]
  
  return(Results)

NA_imputations_dates() 函数的输出
NA_imputations_dates(df1)
#>     id       Date price
#>  1: 11 2021-06-01   100
#>  2: 11 2021-06-02   100
#>  3: 11 2021-06-03   100
#>  4: 11 2021-06-04   100
#>  5: 11 2021-06-05   100
#>  6: 11 2021-06-06   100
#>  7: 11 2021-06-07   100
#>  8: 11 2021-06-08   100
#>  9: 11 2021-06-09   100
#> 10: 11 2021-06-10   100
#> 11: 11 2021-06-11    75
#> 12: 11 2021-06-12    50
#> 13: 11 2021-06-13    50
#> 14: 11 2021-06-14    50
#> 15: 11 2021-06-15    50
#> 16: 11 2021-06-16    50
#> 17: 11 2021-06-17   125
#> 18: 11 2021-06-18   200
#> 19: 11 2021-06-19   200
#> 20: 11 2021-06-20   200
#> 21: 11 2021-06-21   200
#>     id       Date price

由reprex package (v2.0.1) 于 2021 年 12 月 12 日创建


编辑处理更通用数据集的函数 #2

作为您评论的后续,请在下面找到函数的修改版本(即NA_imputations_dates_v2())以处理您的新数据集提供的更一般情况(即dataset #2)。

Reprex

您的数据集 #2
df1 <- data.frame(id = c(11,11,11,11,11,11,11,11,
                         12,12,12,
                         13,13,13),
                  Date = c("2021-06-01", "2021-06-05", "2021-06-08", "2021-06-09", "2021-06-14", "2021-06-16", "2021-06-20", "2021-06-21",
                           "2021-07-01","2021-07-03","2021-07-05",
                           "2021-08-01","2021-08-03","2021-08-05"),
                  price = c(NA, NA,100, NA, 50, NA, 200, NA,
                            10,NA,NA,
                            NA,NA,20))
NA_imputations_dates_v2() 函数的代码
library(data.table)
library(padr)  
  
NA_imputations_dates_v2 <- function(x) 
  
  setDT(x)[, Date := as.Date(Date)]
  
  x <- pad(x, interval = "day", group = "id")

  setDT(x)[, rows := .I]
  
  z <- x[, .I[!is.na(price)]]
  
  id_1 <- z[-length(z)]
  id_2 <- z[-1]
  
  values <- x[z, .(price = price, id = id)]
  values_1 <- values[-nrow(values)]
  names(values_1) <- c("price_1", "id_o1")
  values_2 <- values[-1]
  names(values_2) <- c("price_2", "id_o2")
  
  subtract <- z[-1] - z[-length(z)]
  
  r <- data.table(id_1, values_1, id_2, values_2, subtract)

  r <- r[, `:=` (id_mean = fifelse(subtract > 2 & subtract %% 2 == 0 & id_o1 == id_o2, id_1+(subtract/2), NA_real_),
                 mean = fifelse(subtract >= 2 & subtract %% 2 == 0 & id_o1 == id_o2, (price_1+price_2)/2, NA_real_))
         ][, `:=` (price_1 = NULL, id_1 = NULL, id_o1 = NULL, id_2 = NULL, price_2 = NULL, id_o2 = NULL, subtract = NULL)
           ][x, on = .(id_mean = rows)][, dummy := cumsum(!is.na(mean)), by = .(id)]
  
  h <-  r[, .(price = na.omit(price)), by = .(dummy, id)]
  
  Results <- r[, price := NULL
               ][h, on = .(dummy, id)
                 ][, price := fifelse(!is.na(mean), mean, price)
                   ][, `:=` (id_mean = NULL, mean = NULL, dummy = NULL)][]
  
  return(Results)
 
NA_imputations_dates_v2() 函数的输出
NA_imputations_dates_v2(df1)
#>     id       Date price
#>  1: 11 2021-06-01   100
#>  2: 11 2021-06-02   100
#>  3: 11 2021-06-03   100
#>  4: 11 2021-06-04   100
#>  5: 11 2021-06-05   100
#>  6: 11 2021-06-06   100
#>  7: 11 2021-06-07   100
#>  8: 11 2021-06-08   100
#>  9: 11 2021-06-09   100
#> 10: 11 2021-06-10   100
#> 11: 11 2021-06-11    75
#> 12: 11 2021-06-12    50
#> 13: 11 2021-06-13    50
#> 14: 11 2021-06-14    50
#> 15: 11 2021-06-15    50
#> 16: 11 2021-06-16    50
#> 17: 11 2021-06-17   125
#> 18: 11 2021-06-18   200
#> 19: 11 2021-06-19   200
#> 20: 11 2021-06-20   200
#> 21: 11 2021-06-21   200
#> 22: 12 2021-07-01    10
#> 23: 12 2021-07-02    10
#> 24: 12 2021-07-03    10
#> 25: 12 2021-07-04    10
#> 26: 12 2021-07-05    10
#> 27: 13 2021-08-01    20
#> 28: 13 2021-08-02    20
#> 29: 13 2021-08-03    20
#> 30: 13 2021-08-04    20
#> 31: 13 2021-08-05    20
#>     id       Date price

由reprex package (v2.0.1) 于 2021 年 12 月 14 日创建


处理更通用数据集的函数的第二次编辑 #3

作为您第二条评论的后续,请在下面找到该函数的修改版本(即NA_imputations_dates_v3()),以处理您的新数据集提供的更一般的情况(即dataset #3)。

Reprex

您的数据集 #3
df1 <- data.frame(id = c(11,11,11,11,11,11,11,11,
                         12,12,12,
                         13,13,13),
                  Date = c("2021-06-01", "2021-06-05", "2021-06-08", "2021-06-09", "2021-06-14", "2021-06-16", "2021-06-20", "2021-06-21",
                           "2021-07-01","2021-07-03","2021-07-05",
                           "2021-08-01","2021-08-03","2021-08-05"),
                  price = c(NA, NA,100, NA, 50, NA, 200, 200,
                            10,NA,NA,
                            NA,NA,20))
NA_imputations_dates_v3() 函数的代码
library(data.table)
library(padr)  
  
NA_imputations_dates_v3 <- function(x) 
  
  setDT(x)[, Date := as.Date(Date)]
  
  x <- pad(x, interval = "day", group = "id")
  
  setDT(x)[, rows := .I]
  
  z <- x[, .I[!is.na(price)]]
  
  id_1 <- z[-length(z)]
  id_2 <- z[-1]
  
  values <- x[z, .(price = price, id = id)]
  values_1 <- values[-nrow(values)]
  names(values_1) <- c("price_1", "id_o1")
  values_2 <- values[-1]
  names(values_2) <- c("price_2", "id_o2")
  
  subtract <- z[-1] - z[-length(z)]
  
  r <- data.table(id_1, values_1, id_2, values_2, subtract)
  
  r <- r[, `:=` (id_mean = fifelse(subtract > 2 & subtract %% 2 == 0 & id_o1 == id_o2, id_1+(subtract/2), NA_real_),
                 mean = fifelse(subtract >= 2 & subtract %% 2 == 0 & id_o1 == id_o2, (price_1+price_2)/2, NA_real_))
         ][, `:=` (price_1 = NULL, id_1 = NULL, id_o1 = NULL, id_2 = NULL, price_2 = NULL, id_o2 = NULL, subtract = NULL)
           ][x, on = .(id_mean = rows)][, dummy := cumsum(!is.na(mean)), by = .(id)]
  
  r <- r[, price_lag := shift(price, 1), by = .(dummy, id)]
  
  h <-  r[, .(price = na.omit(price)), by = .(dummy, id, price_lag)]
  
  h <- h[h[,.I[is.na(price_lag)]]][, price_lag := NULL]
  
  Results <- r[, `:=` (price = NULL, price_lag = NULL)
               ][h, on = .(dummy, id)
                 ][, price := fifelse(!is.na(mean), mean, price)
                   ][, `:=` (id_mean = NULL, mean = NULL, dummy = NULL)][]
  
  return(Results)
   
NA_imputations_dates_v3() 函数的输出
NA_imputations_dates_v3(df1)  
#>     id       Date price
#>  1: 11 2021-06-01   100
#>  2: 11 2021-06-02   100
#>  3: 11 2021-06-03   100
#>  4: 11 2021-06-04   100
#>  5: 11 2021-06-05   100
#>  6: 11 2021-06-06   100
#>  7: 11 2021-06-07   100
#>  8: 11 2021-06-08   100
#>  9: 11 2021-06-09   100
#> 10: 11 2021-06-10   100
#> 11: 11 2021-06-11    75
#> 12: 11 2021-06-12    50
#> 13: 11 2021-06-13    50
#> 14: 11 2021-06-14    50
#> 15: 11 2021-06-15    50
#> 16: 11 2021-06-16    50
#> 17: 11 2021-06-17   125
#> 18: 11 2021-06-18   200
#> 19: 11 2021-06-19   200
#> 20: 11 2021-06-20   200
#> 21: 11 2021-06-21   200
#> 22: 12 2021-07-01    10
#> 23: 12 2021-07-02    10
#> 24: 12 2021-07-03    10
#> 25: 12 2021-07-04    10
#> 26: 12 2021-07-05    10
#> 27: 13 2021-08-01    20
#> 28: 13 2021-08-02    20
#> 29: 13 2021-08-03    20
#> 30: 13 2021-08-04    20
#> 31: 13 2021-08-05    20
#>     id       Date price

由reprex package (v2.0.1) 于 2021 年 12 月 14 日创建

【讨论】:

感谢您的解决方案。我刚刚遇到了另一个数据场景,其中可以在多个日期为一个 id 重复相同的价格。在修改后的数据框代码中,我为 id 11 添加了两次价格 200。现在,如果您可以运行 df2 和 df3 的代码,在 df3 上,您将看到同一日期有多个值 嗨@Rijin,(i)请找到第二个编辑(位于我的原始答案和第一个编辑的底部)以考虑到您新更新的数据集(即数据集)提供的更一般的情况#3)。我希望这个功能能够满足您的需求(......并希望这是您需要的最后一次改编;-)); (ii) 您不需要使用allow.cartesian = TRUE 我认为您的问题是您通过merge 生成数据集:如果您使用我在后续编辑中包含的数据集dataset #2dataset #3,一切工作正常。干杯。 抱歉重复编辑,我应该想到所有可能的错误。我确实有更复杂的数据,但我使用所有这些 df 作为样本,这样很容易。最后一件事,您用于dataset 3 的代码与我发布的代码不同。我在第 1 位和第 7 位保持了 200 的值,最后你两次保持了 200 对不起,如果我误解了您的dataset #3 的结构。也就是说,这对我来说并不清楚。您希望如何在第一个 200 和下一个值(即 100)之间估算 NA 值:这两个值由 6 个 NA 隔开......在这种情况下是否应该计算平均值,如果那么,该平均值应归入哪一行?还是应该用 3 个值 200 和 3 个值 100 填充 NA? 我会解释的。日期 2021-06-01, 2021-06-05 and 2021-06-08 的价格值如下 `200, NA, 100. First, we impute the missing dates in between the existing date and then try to get an average value for the date 2021-06-05` 缺少价格。为此,在 groupby 'id' 上,我们上下对称地查找日期,直到找到一个取平均值的值。在我们的例子中,当我们上下迭代 3 次时,我们得到值 100 at date 2021-06-08,而 2021-06-02 没有值,因此我们需要保留 100。

以上是关于通过填写缺失的日期并通过上下对称迭代日期以找到 r 中可用的最接近值来平均插补的主要内容,如果未能解决你的问题,请参考以下文章

按日期和组聚合并在大查询中填写缺失的日期

如何在 Oracle 中按组填写缺失的日期

填写缺失的日期 Redshift

SQL Server:填写每个实体具有不同日期范围的缺失日期

根据 max 和 min 填写缺失的日期 pandas

根据 max 和 min 填写缺失的日期 pandas