用非 NA 值替换前一列中的先前 NA,同时保留后面列中出现的 NA

问题描述 投票:0回答:2

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吗?

r data.table zoo
2个回答
1
投票

这是一种选择:

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]]


0
投票

这是一个按照您的评论中所述进行操作的答案,即保留出现在非 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
© www.soinside.com 2019 - 2024. All rights reserved.