用条件替换 NA

Replace NA with conditions

我正在尝试执行一项分析,为此我需要让我的数据的所有列都具有值。

我有很多病人的名单。在 3 种可能的情况下会看到患者: 急诊、门诊和住院。

每位患者可以来一次或多次这些服务。

我们拥有的数据是:

问题是,一个病人来急诊室,到那个日期只填急诊诊断,外诊和住院会有一个"NA"。就像你来门诊一样,你会在急诊室有 NA 并在那个日期住院(当你来门诊时)。

 pacient <- c(10,10,10,10,10,11,11,12,12,12); pacient
date <- as.Date(c("01/01/2018","02/01/2018", "04/04/2018", "10/05/2018", "05/09/2018", "02/01/2018", "06/08/2018", "01/01/2018", "03/01/2018", "06/08/2018"), format = "%d/%m/%Y"); date 
set <- c("URG", "CEX", "CEX", "URG", "HOSP", "CEX", "URG", "CEX", "CEX", "URG")
dx_URG <- c("A", NA, NA, "B", NA, NA, "A", NA, NA, "B")
dx_CEX <- c(NA, "B", "C", NA, NA, "A", NA, "C", "B", NA)
dx_HOSP <- c(NA, NA, NA, NA, "A", NA, NA, NA,NA,NA)

DF <- data.frame(pacient, date, set, dx_URG, dx_CEX, dx_HOSP)); DF

我的数据:

        pacient    date      set   dx_URG dx_CEX dx_HOSP
1       10     01/01/2018    URG      A   <NA>   <NA>
2       10     02/01/2018    CEX   <NA>      B   <NA>
3       10     04/04/2018    CEX   <NA>      C   <NA>
4       10     10/05/2018    URG      B   <NA>   <NA>
5       10     05/09/2018    HOSP   <NA>   <NA>    A
6       11     02/01/2018    CEX   <NA>      A   <NA>
7       11     06/08/2018    URG      A   <NA>   <NA>
8       12     01/01/2018    CEX   <NA>      C   <NA>
9       12     03/01/2018    CEX   <NA>      B   <NA>
10      12     06/08/2018    URG      B   <NA>   <NA>

A, B, B, B, B

第一个值保持不变,第二个值是 NA 变为 B(自 2018 年 10 月 5 日访问 URG 后诊断为 B)等等... 对于患者 10 在 CEX 中的诊断,我在 NA 中填写诊断 C,在 HOSP 中填写诊断 A。 我部分地通过以下代码实现了这一点:

dx_remp <- lapply(DF, function(x){
  setDT(DF)[, dx_URG:= na.aggregate(dx_URG, FUN=function(x){ tail(x[!is.na(x)],1)}), by = pacient]
  setDT(DF)[, dx_CEX:= na.aggregate(dx_CEX, FUN=function(x){ tail(x[!is.na(x)],1)}), by = pacient]
  setDT(DF)[, dx_HOSP:= na.aggregate(dx_HOSP, FUN=function(x){ tail(x[!is.na(x)],1)}), by = pacient]
  return(num_vist)})

问题是,当患者没有诊断为 "fill in" 时,此代码不起作用:例如,我填写 dx_HOSP 时出现错误,因为患者 11 和 12 都没有在那里有任何价值。

我需要的第二件事是与此相关,如果患者没有值来填写我们要替换 NA 的列,请在另一列中查找它: 优先级是 CEX -> HOSP -> URG

综上所述,我必须先填写NA,在它们的列中查找,如果没有值,则在dx_CEX中搜索,然后在dx_HOS中搜索,然后在dx_URG中搜索。

想要的结果:

       pacient    date      set   dx_URG dx_CEX dx_HOSP
1       10     01/01/2018    URG      A      C     A
2       10     02/01/2018    CEX      B      B     A
3       10     04/04/2018    CEX      B      C     A
4       10     10/05/2018    URG      B      C     A
5       10     05/09/2018    HOSP     B      C     A
6       11     02/01/2018    CEX      A      A     A
7       11     06/08/2018    URG      A      A     A
8       12     01/01/2018    CEX      B      C     B
9       12     03/01/2018    CEX      B      B     B
10      12     06/08/2018    URG      B      B     B

例如,dx_cex 中的患者 10 的日期 1、4 和 5 为空; dx_cex 的 NA 必须填写该患者的最后一次 cex 诊断,即 C。在 dx_hosp 中的患者 12 中,它在任何引文中都没有价值,因此有必要在 cex 中寻找最后一个有效诊断,即 B 并用 B.

填充 dx_hosp 的所有 NA

谢谢

根据新数据进行改编。我们创建一个辅助函数,用最后一个非 NA 值(如果有)填充 NA 值,并使用它代替 fill:

fill_with_last = function(x)  {
  if (any(!is.na(x))) x[is.na(x)] = tail(na.omit(x), 1)
  return(x)
}

DF %>% 
  # fix column classes (just in case)
  mutate_if(is.factor, as.character) %>%
  # ensure order is correct
  arrange(pacient, date) %>%
  # by pacient
  group_by(pacient) %>%
  # fill in the diagnosis variables with last observation
  mutate_at(vars(starts_with("dx")), fill_with_last) %>%
  # coalesce in priority order to fill in any blanks
  mutate(dx_URG = coalesce(dx_URG, dx_CEX, dx_HOSP),
         dx_CEX = coalesce(dx_CEX, dx_HOSP, dx_URG),
         dx_HOSP = coalesce(dx_HOSP, dx_CEX, dx_URG))

# # A tibble: 10 x 6
# # Groups:   pacient [3]
#    pacient date       set   dx_URG dx_CEX dx_HOSP
#      <dbl> <date>     <chr> <chr>  <chr>  <chr>  
#  1      10 2018-01-01 URG   A      C      A      
#  2      10 2018-01-02 CEX   B      B      A      
#  3      10 2018-04-04 CEX   B      C      A      
#  4      10 2018-05-10 URG   B      C      A      
#  5      10 2018-09-05 HOSP  B      C      A      
#  6      11 2018-01-02 CEX   A      A      A      
#  7      11 2018-08-06 URG   A      A      A      
#  8      12 2018-01-01 CEX   B      C      C      
#  9      12 2018-01-03 CEX   B      B      B      
# 10      12 2018-08-06 URG   B      B      B      

请注意第 8 行 dx_Hosp 中存在差异。您想要的结果显示 "B",但您描述的逻辑(由我和 sindri 实施)导致 "C"因为 dx_CEX 在优先顺序中排在 dx_URG 之前。


这是我认为您想填写 next 诊断而不是 last 诊断时的原始解决方案。它使用 tidyr 作为其 fill 函数(用之前的观察填充缺失值),并使用 dplyr 作为其 coalesce 函数(跨列取第一个非缺失值):

library(tidyr)
library(dplyr)

DF %>%
  # convert any factor columns to character for easy modification
  mutate_if(is.factor, as.character) %>%
  # make sure things are in the right order
  arrange(pacient, date) %>%
  # do subsequent operations "by pacient"
  group_by(pacient) %>% 
  # fill in missing values "up" with the most recent observation,
  # then fill "down" if there are other holes
  fill(dx_URG, dx_CEX, dx_HOSP, .direction = "up") %>%
  fill(dx_URG, dx_CEX, dx_HOSP, .direction = "down") %>%
  # "coalesce" column in the order of preference
  mutate(dx_URG = coalesce(dx_URG, dx_CEX, dx_HOSP),
         dx_CEX = coalesce(dx_CEX, dx_HOSP, dx_URG),
         dx_HOSP = coalesce(dx_HOSP, dx_CEX, dx_URG))
# # A tibble: 10 x 6
# # Groups:   pacient [3]
#    pacient date       set   dx_URG dx_CEX dx_HOSP
#      <dbl> <date>     <chr> <chr>  <chr>  <chr>  
#  1      10 2018-01-01 URG   A      B      A      
#  2      10 2018-01-02 CEX   B      B      A      
#  3      10 2018-04-04 CEX   B      C      A      
#  4      10 2018-05-10 URG   B      C      A      
#  5      10 2018-09-05 HOSP  B      C      A      
#  6      11 2018-01-02 CEX   A      A      A      
#  7      11 2018-08-06 URG   A      A      A      
#  8      12 2008-01-01 CEX   B      C      C      
#  9      12 2018-01-03 CEX   B      B      B      
# 10      12 2018-08-06 URG   B      B      B      

几个数据说明。在您的代码中,第 5 行有年份 2008,但在您的 table 中,您有 2018 和其他所有年份一样。我将 2008 更改为 2018 以匹配 table.

当您执行 cbind() 时,它会将所有内容转换为 matrix,这会丢失您的列 类。这是不好的。直接调用 data.frame() 比使用 as.data.frame(cbind()).

要好得多
# change this
DF <- as.data.frame(cbind(pacient, date, set, dx_URG, dx_CEX, dx_HOSP))
# to this
DF <- data.frame(pacient, date, set, dx_URG, dx_CEX, dx_HOSP)

认为这是一个有趣的问题,并提出了这个 data.table 解决方案,该解决方案依赖于 dplyr::case_when() 的可读性:

# Setup ----
# Packages
library(dplyr)
library(data.table)

# Convert to data.table
setDT(DF)
# Get the order of in terms of pacient and date
setkey(DF, pacient, date)
# Convert factors to characters (easier to work with)
factors <- names(DF)[sapply(DF, is.factor)]
DF[,(factors):= lapply(.SD, as.character), .SDcols = factors]
# Define helper function
ext_most_recent <- function(x) tail(x[!is.na(x)], 1) 
# Which columns to update
cols2fill <- c("dx_URG", "dx_CEX", "dx_HOSP")

# Update columns ----
DF[, (cols2fill) := lapply(
                      .SD, 
                      function(x) {
                        most_recent <- ext_most_recent(x)[1]
                        case_when(
                          !is.na(x) ~ x,
                          !is.na(most_recent) ~ most_recent,
                          !is.na(dx_CEX) ~ dx_CEX,
                          !is.na(dx_HOSP) ~ dx_HOSP,
                          !is.na(dx_URG) ~ dx_URG,
                          TRUE ~ NA_character_
                        )
                      }
                    ), 
   by = pacient, 
   .SDcols = c("dx_URG", "dx_CEX", "dx_HOSP")]

#  1:      10 2018-01-01  URG      A      C       A
#  2:      10 2018-01-02  CEX      B      B       A
#  3:      10 2018-04-04  CEX      B      C       A
#  4:      10 2018-05-10  URG      B      C       A
#  5:      10 2018-09-05 HOSP      B      C       A
#  6:      11 2018-01-02  CEX      A      A       A
#  7:      11 2018-08-06  URG      A      A       A
#  8:      12 2018-01-01  CEX      B      C       C
#  9:      12 2018-01-03  CEX      B      B       B
# 10:      12 2018-08-06  URG      B      B       B