在
data.table
中,我想用最接近的先前非NA值来填充先前的NA,类似于这篇文章
但是,我想保留非 NA 值之后出现的任何 NA。之后
na.locf()
函数似乎取代了 NA。我的数据是格式化的本体图,因此第一列region_level1
是顶级祖先,后续列是更专业的区域。一些祖先可以分为更多的子术语,例如Cerebral cortex
可以分为Cerebral cortex 1
和Cerebral cortex 2
,而Temporal cortex
不能,这就是为什么后面的列中有NA,因为没有现有的子术语。
我想要实现的目标的一个例子:
输入数据:
input_data <- data.frame(
region_level1 = c("Brain", NA, NA, NA, NA, NA),
region_level2 = c(NA, "Grey Matter", NA, NA, NA, NA),
region_level3 = c(NA, NA, "Cerebral Cortex", NA, NA, "Temporal Cortex"),
region_level4 = c(NA, NA, NA, "Cerebral cortex 1", "Cerebral cortex 2", NA),
stringsAsFactors = FALSE
)
input_data
region_level1 region_level2 region_level3 region_level4
1 Brain <NA> <NA> <NA>
2 <NA> Grey Matter <NA> <NA>
3 <NA> <NA> Cerebral Cortex <NA>
4 <NA> <NA> <NA> Cerebral cortex 1
5 <NA> <NA> <NA> Cerebral cortex 2
6 <NA> <NA> Temporal Cortex <NA>
所需输出:
desired_output <- data.frame(
region_level1 = c("Brain", "Brain", "Brain", "Brain", "Brain", "Brain"),
region_level2 = c(NA, "Grey Matter", "Grey Matter", "Grey Matter", "Grey Matter", "Grey Matter"),
region_level3 = c(NA, NA, "Cerebral Cortex", "Cerebral Cortex", "Cerebral Cortex", "Temporal Cortex"),
region_level4 = c(NA, NA, NA, "Cerebral cortex 1", "Cerebral cortex 2", NA),
stringsAsFactors = FALSE
)
desired_output
region_level1 region_level2 region_level3 region_4
1 Brain <NA> <NA> <NA>
2 Brain Grey Matter <NA> <NA>
3 Brain Grey Matter Cerebral Cortex <NA>
4 Brain Grey Matter Cerebral Cortex Cerebral cortex 1
5 Brain Grey Matter Cerebral Cortex Cerebral cortex 2
6 Brain Grey Matter Temporal Cortex NA
使用
na.locf()
功能:
converted_data <- zoo::zoo(original_data)
converted_data <- zoo::na.locf(converted_data)
converted_data
region_level1 region_level2 region_level3 region_level4
1 Brain <NA> <NA> <NA>
2 Brain Grey Matter <NA> <NA>
3 Brain Grey Matter Cerebral Cortex <NA>
4 Brain Grey Matter Cerebral Cortex Cerebral cortex 1
5 Brain Grey Matter Cerebral Cortex Cerebral cortex 2
6 Brain Grey Matter Temporal Cortex Cerebral cortex 2
有什么办法可以保留后续的NA吗?
这是一种选择:
f <- \(x) {
d = which(!is.na(x))
if(length(d) == 1) x[d:length(x)] <- zoo::na.locf(x)
if(length(d)>1) x[d[1]:(d[2]-1)] <- x[d[1]]
return(x)
}
original_data[,lapply(.SD,f)]
输出:
V1 V2 V3 V4
<char> <char> <char> <char>
1: Brain <NA> <NA> <NA>
2: Brain Grey Matter <NA> <NA>
3: Brain Grey Matter Cerebral Cortex <NA>
4: Brain Grey Matter Cerebral Cortex Cerebral cortex 1
5: Brain Grey Matter Cerebral Cortex Cerebral cortex 2
6: Brain Grey Matter Temporal Cortex <NA>
基本上,
f()
正在摄取一个向量(在本例中是data.table中的一列),并识别非na发生的位置。如果只有一个非 na 值,那么它只是使用 zoo::na.locf()
替换从该点开始的 x 值。如果有多个非 na 值,它将用第一个值从第一个值替换到下一个值。首先你实际上并不需要zoo::na.locf(x)
..即两条 if
行最终都可以分配值 x[d[1]]
这是一个按照您的评论中所述进行操作的答案,即保留出现在非 NA 值右侧的任何 NA,但将左侧的 NA 替换为祖先项。
让我们创建一些更简单的数据。
input_data <- data.table(
a = c("Brain", NA, NA),
b = c(NA, "Cortex", "Cortex"),
c = c(NA, "Cortex 1", NA)
)
input_data
# a b c
# <char> <char> <char>
# 1: Brain <NA> <NA>
# 2: <NA> Cortex Cortex 1
# 3: <NA> Cortex <NA>
# Replace NA that occurs to the left of non-NA
# but not to the right
desired_output <- data.table(
a = c("Brain", "Brain", "Brain"),
b = c(NA, "Cortex", "Cortex"),
c = c(NA, "Cortex 1", NA)
)
# a b c
# <char> <char> <char>
# 1: Brain <NA> <NA>
# 2: Brain Cortex Cortex 1
# 3: Brain Cortex <NA>
我们可以创建一个
set*
函数来进行就地修改。本质上我们是这么做的 zoo::na.locf()
。然后,我们在向左移动一列的 is.na()
的副本上运行 data.table
,并查看哪些值是 NA
,并且仅替换出现在非 NA
左侧的 NA
值:
set_na_shift <- function(dat) {
do_replace <- is.na(cbind(rep(NA, nrow(dat)), dat[,-ncol(dat), with = FALSE])) |>
data.frame() |>
setNames(names(dat))
converted_data <- zoo::zoo(dat)
converted_data <- zoo::na.locf(converted_data) |>
data.frame()
dat[,
(names(dat)) := lapply(names(.SD), \(nm) fifelse(
do_replace[[nm]],
converted_data[[nm]],
dat[[nm]]
)),
.SDcols = names(dat)
]
}
set_na_shift(input_data) # modifies in place, doesn't print
identical(input_data, desired_output) # TRUE