繁体   English   中英

使用 data.table 按组继承最后一次观察

[英]Carrying forward last observation with a limit, by group, using data.table

我正在尝试通过限制为2 的组来使用大型 data.table 来推进最后一次观察。 这里有很多复杂的解决方案,但似乎没有一个包含所有 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

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

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

请注意,通常在处理更长的 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

这是另一种选择:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM