用条件替换 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>
- 如果可能,用患者自己的值填充列的 NA。也就是说,患者 10 在 5 个不同的日期来过医院 5 次。对于第一次访问,它在 CEX 和 HOSP 中生成 NA,在 URG 和 HOSP 中生成 2 ...等等。
我想在 dx_URG 列中为患者 10 的缺失值填写该患者最近的紧急诊断,也就是说您是:
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
我正在尝试执行一项分析,为此我需要让我的数据的所有列都具有值。
我有很多病人的名单。在 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>
- 如果可能,用患者自己的值填充列的 NA。也就是说,患者 10 在 5 个不同的日期来过医院 5 次。对于第一次访问,它在 CEX 和 HOSP 中生成 NA,在 URG 和 HOSP 中生成 2 ...等等。 我想在 dx_URG 列中为患者 10 的缺失值填写该患者最近的紧急诊断,也就是说您是:
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