识别并巩固重复的观察结果

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

我正在研究临床试验的数据。在此数据中,存在涉及同一患者的多个观察结果,并且它们具有不同程度的缺失数据。我有四个变量用作标识符,并且我决定,如果患者的四个变量中的三个具有相同的值,则他/她应被视为重复。

所有这些变量中的数据缺失程度极低,因此不存在具有三个 NA 的患者并被视为另一具有三个 NA 的患者的重复的问题。

我已经能够通过创建变量的

comb
inations 并使用
dplyr::distinct()
来删除重复项。不幸的是,这导致其中一个观察结果被保留,该观察结果可能缺少对同一患者的观察结果的数据,但被排除在外。我将用一些假数据来演示:

library(tidyverse)
library(knitr)

tibble(Name = c("Pedro", "Pedro","Pedro","Cristina","Walter","Sara","Mateus"),
       Birth = dmy(c("29/07/1994","29/07/1994","29/07/1994","01/04/1960", "22/12/1956", "20/02/1997","25/07/1994")),
       CNS = c("700",NA,"700","701","702","703","704"),
       Document = c("104","104",NA,"105","106","107","108"),
       SystolicBP1 = c(NA,NA,120,160,152,114,NA),
       DiastolicBP1 = c(NA,NA,80,100,100,92,NA),
       SystolicBP2 = c(NA,NA,NA,148,NA,NA,100),
       DiastolicBP2= c(NA,NA,NA,90,NA,NA,82),
       HBA1c = c(7,7,7,8.2,8,9,6.5)) -> dt

kable(dt)
姓名 出生 中枢神经系统 文件 收缩压1 舒张压1 收缩压2 舒张压2 HBA1c
佩德罗 1994-07-29 700 104 不适用 不适用 不适用 不适用 7.0
佩德罗 1994-07-29 不适用 104 不适用 不适用 不适用 不适用 7.0
佩德罗 1994-07-29 700 不适用 120 80 不适用 不适用 7.0
克里斯蒂娜 1960-04-01 701 105 160 100 148 90 8.2
沃尔特 1956-12-22 702 106 152 100 不适用 不适用 8.0
莎拉 1997-02-20 703 107 114 92 不适用 不适用 9.0
马特乌斯 1994-07-25 704 108 不适用 不适用 100 82 6.5

然后我可以运行以下命令来删除重复项,但最终会丢失一些数据:

identifiers <- c("Name","Birth","CNS","Document")

combn(identifiers, 3) -> comb

kable(comb)
姓名 姓名 姓名 出生
出生 出生 中枢神经系统 中枢神经系统
中枢神经系统 文件 文件 文件
## this takes the result of distinct() for each 
## combination of the 4 identifiers, taken 3 at a time
dt %>% 
        distinct (!!! syms(comb[,1]), .keep_all = TRUE) %>%
        distinct (!!! syms(comb[,2]), .keep_all = TRUE) %>%
        distinct (!!! syms(comb[,3]), .keep_all = TRUE) %>%
        distinct (!!! syms(comb[,4]), .keep_all = TRUE) %>%
        kable()
姓名 出生 中枢神经系统 文件 收缩压1 舒张压1 收缩压2 舒张压2 HBA1c
佩德罗 1994-07-29 700 104 不适用 不适用 不适用 不适用 7.0
克里斯蒂娜 1960-04-01 701 105 160 100 148 90 8.2
沃尔特 1956-12-22 702 106 152 100 不适用 不适用 8.0
莎拉 1997-02-20 703 107 114 92 不适用 不适用 9.0
马特乌斯 1994-07-25 704 108 不适用 不适用 100 82 6.5

我的目标是,当面对丢失的数据时,尝试从重复的观察中获取丢失的数据。如果我能做到这一点,在上面的示例中,第一个观察将从原始小标题中的第三个观察中获取“SystolicBP1”和“DiastolicBP1”的值。

r dplyr duplicates missing-data
2个回答
0
投票

如果您想要做的是从每组中获取第一个非

NA
值,那么我们可以做

library(dplyr)
dt |>
  summarize(
    .by = c(Name, Birth),
    across(everything(), ~ na.omit(.x)[1])
  )
# # A tibble: 5 × 9
#   Name     Birth      CNS   Document SystolicBP1 DiastolicBP1 SystolicBP2 DiastolicBP2 HBA1c
#   <chr>    <date>     <chr> <chr>          <dbl>        <dbl>       <dbl>        <dbl> <dbl>
# 1 Pedro    1994-07-29 700   104              120           80          NA           NA   7  
# 2 Cristina 1960-04-01 701   105              160          100         148           90   8.2
# 3 Walter   1956-12-22 702   106              152          100          NA           NA   8  
# 4 Sara     1997-02-20 703   107              114           92          NA           NA   9  
# 5 Mateus   1994-07-25 704   108               NA           NA         100           82   6.5

这(默默地)丢弃可能具有不同值(示例数据中不存在)的

Name, Birth
对的多行。我们可以通过更改一个值并看到它被删除来显示这一点:

dt2 <- mutate(dt, Document = replace(Document, 1, "999"))
head(dt2, 3)
# # A tibble: 3 × 9
#   Name  Birth      CNS   Document SystolicBP1 DiastolicBP1 SystolicBP2 DiastolicBP2 HBA1c
#   <chr> <date>     <chr> <chr>          <dbl>        <dbl>       <dbl>        <dbl> <dbl>
# 1 Pedro 1994-07-29 700   999               NA           NA          NA           NA     7
# 2 Pedro 1994-07-29 NA    104               NA           NA          NA           NA     7
# 3 Pedro 1994-07-29 700   NA               120           80          NA           NA     7

dt2 |>
  summarize(
    .by = c(Name, Birth),
    across(everything(), ~ na.omit(.x)[1])
  )
# # A tibble: 5 × 9
#   Name     Birth      CNS   Document SystolicBP1 DiastolicBP1 SystolicBP2 DiastolicBP2 HBA1c
#   <chr>    <date>     <chr> <chr>          <dbl>        <dbl>       <dbl>        <dbl> <dbl>
# 1 Pedro    1994-07-29 700   999              120           80          NA           NA   7  
# 2 Cristina 1960-04-01 701   105              160          100         148           90   8.2
# 3 Walter   1956-12-22 702   106              152          100          NA           NA   8  
# 4 Sara     1997-02-20 703   107              114           92          NA           NA   9  
# 5 Mateus   1994-07-25 704   108               NA           NA         100           82   6.5

如果是这种情况,我们可以通过输入值(我将使用非

NA
值的随机采样)然后进行重复数据删除来使过程变得更智能。

dt2 |>
  mutate(
    .by = c(Name, Birth),
    across(everything(), ~ if_else(is.na(.x), na.omit(.x)[1], .x))
  ) |>
  distinct()
# # A tibble: 6 × 9
#   Name     Birth      CNS   Document SystolicBP1 DiastolicBP1 SystolicBP2 DiastolicBP2 HBA1c
#   <chr>    <date>     <chr> <chr>          <dbl>        <dbl>       <dbl>        <dbl> <dbl>
# 1 Pedro    1994-07-29 700   999              120           80          NA           NA   7  
# 2 Pedro    1994-07-29 700   104              120           80          NA           NA   7  
# 3 Cristina 1960-04-01 701   105              160          100         148           90   8.2
# 4 Walter   1956-12-22 702   106              152          100          NA           NA   8  
# 5 Sara     1997-02-20 703   107              114           92          NA           NA   9  
# 6 Mateus   1994-07-25 704   108               NA           NA         100           82   6.5

执行

na.omit(.x)[1]
的一个副作用是,如果所有值(该列的该组中)都是
NA
,那么虽然
na.omit(.x)
将返回
c()
,但
[1]
将强制其返回
NA 
.

仅供参考,使用

.by=
需要
dplyr_1.1.0
或更新版本;如果您有旧版本,请从
mutate(.by=c(..), stuff)
更改为
group_by(..) |> mutate(stuff) |> ungroup()


0
投票

我们可以从前四个 ident 列中创建字符串,并根据距

hclust
的字符串距离运行
adist
以获得
id
。接下来,使用
by
,我们沿着这些
id
分割数据并减少到一行,留下第一个值或
NA

> cl_fun <- \(data, h., ident=c("Name", "Birth", "CNS", 'Document')) {
+   strings <- Reduce(paste0, data[ident])  ## create ident strings
+   ld  <- adist(tolower(strings))  ## distance matrix
+   hc <- hclust(as.dist(ld))  ## cluster analysis
+   id <- cutree(hc, h=h.)  ## cut tree to groups
+   within(data, id <- id)  ## add id to data
+ }
> na_fun <- \(.) {
+   o <- vapply(., \(x) {
+     o <- if (all(is.na(x))) {
+       NA_character_
+     } else {
+       na.omit(as.character(x))[[1]]
+     }
+   }, FUN.VALUE=character(1))
+   o <- type.convert(as.data.frame(t(o)), as.is=TRUE)
+   within(o, {Birth <- as.Date(Birth)})
+ }
> by(cl_fun(df, h.=8.0), ~id, na_fun) |> do.call(what='rbind')
      Name      Birth CNS Document SystolicBP1 DiastolicBP1 SystolicBP2 DiastolicBP2 HBA1c id
1    Pedro 1994-07-29 700      104         120           80         100           NA   7.0  1
2 Cristina 1960-04-01 701      105         160          100         148           90   8.2  2
3   Walter 1956-12-22 702      106         152          100          NA           NA   8.0  3
4     Sara 1997-02-20 703      107         114           92          NA           NA   9.0  4
5   Mateus 1994-07-25 704      108          NA           NA         100           82   6.5  5
6      Ana       <NA>  NA       NA         130           94         100           89    NA  6

Ordep 被认为是 Pedro 的复制品,Laura 被认为是 Ana 的复制品,根据您的说法,这很好。作为

cl_fun()
中的调整参数,我使用
h
中的
cutree()
,它指定了绘制
hc
时的高度,您也可以尝试使用
k
,它会指定具有缺点的簇的数量,
k
是一个整数,灵活性较差。您还可以指定更多
adist()
参数(例如
costs
)进行微调。在此
h=8.0
产生了想要的结果。


数据:

> dput(df)
structure(list(Name = c("Pedro", "Pedro", "Pedro", "Ordep", "Cristina", 
"Walter", "Sara", "Mateus", "Ana", "Ana", "Laura"), Birth = structure(c(8975, 
8975, 8975, 8975, -3562, -4758, 9912, 8971, NA, NA, NA), class = "Date"), 
    CNS = c("700", "<NA>", "700", "700", "701", "702", "703", 
    "704", NA, NA, NA), Document = c("104", "104", "<NA>", "104", 
    "105", "106", "107", "108", NA, NA, NA), SystolicBP1 = c(NA, 
    NA, 120L, NA, 160L, 152L, 114L, NA, NA, 130L, 120L), DiastolicBP1 = c(NA, 
    NA, 80L, NA, 100L, 100L, 92L, NA, 94L, NA, 96L), SystolicBP2 = c(NA, 
    NA, NA, 100L, 148L, NA, NA, 100L, 100L, 110L, 110L), DiastolicBP2 = c(NA, 
    NA, NA, NA, 90L, NA, NA, 82L, 89L, NA, 91L), HBA1c = c(7, 
    7, 7, NA, 8.2, 8, 9, 6.5, NA, NA, NA)), row.names = c(NA, 
-11L), class = "data.frame")

看起来像:

> df
       Name      Birth  CNS Document SystolicBP1 DiastolicBP1 SystolicBP2 DiastolicBP2 HBA1c
1     Pedro 1994-07-29  700      104          NA           NA          NA           NA   7.0
2     Pedro 1994-07-29 <NA>      104          NA           NA          NA           NA   7.0
3     Pedro 1994-07-29  700     <NA>         120           80          NA           NA   7.0
4     Ordep 1994-07-29  700      104          NA           NA         100           NA    NA
5  Cristina 1960-04-01  701      105         160          100         148           90   8.2
6    Walter 1956-12-22  702      106         152          100          NA           NA   8.0
7      Sara 1997-02-20  703      107         114           92          NA           NA   9.0
8    Mateus 1994-07-25  704      108          NA           NA         100           82   6.5
9       Ana       <NA> <NA>     <NA>          NA           94         100           89    NA
10      Ana       <NA> <NA>     <NA>         130           NA         110           NA    NA
11    Laura       <NA> <NA>     <NA>         120           96         110           91    NA
© www.soinside.com 2019 - 2024. All rights reserved.