使用 data.table 按组进行有限制的最后一次观察
Carrying forward last observation with a limit, by group, using data.table
我正在尝试以 2 为限,以大 data.table 分组进行最后一次观察。这里有很多复杂的解决方案,但其中 none 似乎包含了所有 3 个元素:一个具有最大限制的类似 na.locf 的函数,按组,在 data.table.[=13 中=]
我的数据如下:
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 时,要么全部填充它们,要么填充其中的 none 并且 na.locf 已经使用 maxgap 参数处理了该问题,该参数仅填充不超过指定的。这个想法是插值只在短时间内可靠,所以你不应该在较长的时间段内进行插值。尽管如此,下面显示了如何实现问题中的方案,但请考虑是否应该更改策略并改用 maxgap。
1) 使用 na.locf0 给出 locf 计算 na.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")]
我正在尝试以 2 为限,以大 data.table 分组进行最后一次观察。这里有很多复杂的解决方案,但其中 none 似乎包含了所有 3 个元素:一个具有最大限制的类似 na.locf 的函数,按组,在 data.table.[=13 中=]
我的数据如下:
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 时,要么全部填充它们,要么填充其中的 none 并且 na.locf 已经使用 maxgap 参数处理了该问题,该参数仅填充不超过指定的。这个想法是插值只在短时间内可靠,所以你不应该在较长的时间段内进行插值。尽管如此,下面显示了如何实现问题中的方案,但请考虑是否应该更改策略并改用 maxgap。
1) 使用 na.locf0 给出 locf 计算 na.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")]