【问题标题】:Carrying forward last observation with a limit, by group, using data.table使用 data.table 按组进行最后一次观察
【发布时间】:2021-09-25 21:43:54
【问题描述】:

我正在尝试使用大型 data.table 以 2 的限制分组来推进最后一次观察。这里有很多复杂的解决方案,但似乎没有一个包含所有 3 个元素:一个类似于 na.locf 的函数,在 data.table 中按组划分。

我的数据如下:

df <- structure(list(country = c("USA", "USA", "USA", "USA", "USA", 
"FR", "FR", "FR", "FR", "FR"), values = c(2, 1, NA, NA, NA, 2, 
1, 2, NA, NA)), class = c("data.table", "data.frame"), row.names = c(NA, 
-10L))

      country values
 1:     USA      2
 2:     USA      1
 3:     USA     NA
 4:     USA     NA
 5:     USA     NA
 6:      FR      2
 7:      FR      1
 8:      FR      2
 9:      FR     NA
10:      FR     NA

我希望它看起来像这样:

     country values
1      USA      2
2      USA      1
3      USA      1
4      USA      1
5      USA     NA
6       FR      2
7       FR      1
8       FR      2
9       FR      2
10      FR      2

【问题讨论】:

  • 这似乎正是您所需要的:stackoverflow.com/questions/38822441/…。只需将PriceID 分别更改为valuescountry
  • 那里缺少使用 data.table 的分组元素。这些似乎都没有为我提供解决方案。 @MarceloAvila 不幸的是,该解决方案既包含最后一个已知观察的向后携带,也包括向前携带。我已经搞砸了,但我无法让它工作。
  • 这似乎很有用df$values |&gt; dendextend::na_locf(recursive = F) |&gt; dendextend::na_locf(recursive = F)

标签: r data.table


【解决方案1】:

请注意,通常在处理较长的 NA 时,要么全部填充它们,要么不填充它们,而 na.locf 已经使用 maxgap 参数处理了它,该参数仅填充不超过指定的间隙。这个想法是插值仅在较短的范围内是可靠的,因此您根本不应该在较长的范围内进行插值。不过,下面展示了如何实现问题中的方案,但要考虑是否应该改变策略并改用 maxgap。

1) 使用 na.locf0 计算 na.locf,给出 locf 并为 NA 和非 NA 的延伸创建一个分组变量,g。然后对于每次运行的 NA,获取 na.locf 列的前两个元素,并用来自值的 NA 填充剩余部分。这不会覆盖 df,因此它可以在没有副作用的管道中使用。

library(data.table)
library(zoo)

df[, .(values, locf = na.locf0(values), g = rleid(is.na(values))), by = country][
   , .(values = c(head(locf, 2), tail(values, -2))), by = .(country, g)][
   , .(country, values)]

给予:

    country values
 1:     USA      2
 2:     USA      1
 3:     USA      1
 4:     USA      1
 5:     USA     NA
 6:      FR      2
 7:      FR      1
 8:      FR      2
 9:      FR      2
10:      FR      2

2) 稍作修改但仍使用相同基本思想的公式如下。它也不会覆盖。

library(data.table)
library(zoo)

# like na.locf0 but only specifies vector, x, and limit to fill, k
na.locf2 <- function(x, k) {
  nalocf <- na.locf0(x)
  f <- function(ix) c(head(nalocf[ix], k), tail(x[ix], -k))
  unlist(tapply(seq_along(x), rleid(is.na(x)), f))
}
df[, .(values = na.locf2(values, 2)), by = country]

给予:

    country values
 1:     USA      2
 2:     USA      1
 3:     USA      1
 4:     USA      1
 5:     USA     NA
 6:      FR      2
 7:      FR      1
 8:      FR      2
 9:      FR      2
10:      FR      2

【讨论】:

    【解决方案2】:

    您可以借助this 功能并按组应用它-

    library(data.table)
    library(zoo)
    
    replace_NA_with_limit <- function(a, n) {
      r <- rle(is.na(a))
      a <- na.locf(a)
      is.na(a) <- sequence(r$lengths) > n & rep(r$values, r$lengths)
      a
    }
    
    setDT(df)[, values := replace_NA_with_limit(values, 2), country]
    df
    
    #    country values
    # 1:     USA      2
    # 2:     USA      1
    # 3:     USA      1
    # 4:     USA      1
    # 5:     USA     NA
    # 6:      FR      2
    # 7:      FR      1
    # 8:      FR      2
    # 9:      FR      2
    #10:      FR      2
    

    【讨论】:

      【解决方案3】:

      这是另一种选择:

      library(data.table)
      setDT(df)[, ri := rowid(country, values)]
      df[!is.na(values) | ri <= 2L, values := nafill(values, "locf")]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-01
        • 2021-06-27
        • 2016-09-02
        • 1970-01-01
        • 2015-12-14
        • 1970-01-01
        • 1970-01-01
        • 2019-08-26
        相关资源
        最近更新 更多