【发布时间】:2020-09-15 21:38:03
【问题描述】:
Uwe 和 GKi 的答案都是正确的。 Gki 收到赏金是因为 Uwe 迟到了,但 Uwe 的解决方案运行速度大约是 15 倍
我有两个数据集,其中包含不同患者在多个测量时刻的得分,如下所示:
df1 <- data.frame("ID" = c("patient1","patient1","patient1","patient1","patient2","patient3"),
"Days" = c(0,25,235,353,100,538),
"Score" = c(NA,2,3,4,5,6),
stringsAsFactors = FALSE)
df2 <- data.frame("ID" = c("patient1","patient1","patient1","patient1","patient2","patient2","patient3"),
"Days" = c(0,25,248,353,100,150,503),
"Score" = c(1,10,3,4,5,7,6),
stringsAsFactors = FALSE)
> df1
ID Days Score
1 patient1 0 NA
2 patient1 25 2
3 patient1 235 3
4 patient1 353 4
5 patient2 100 5
6 patient3 538 6
> df2
ID Days Score
1 patient1 0 1
2 patient1 25 10
3 patient1 248 3
4 patient1 353 4
5 patient2 100 5
6 patient2 150 7
7 patient3 503 6
ID 列显示患者 ID,Days 列显示测量时刻(患者纳入后的天数),Score 列显示测量得分。两个数据集都显示相同的数据,但时间不同(df1 是 2 年前,df2 具有相同的数据,但从今年开始更新)。
我必须比较两个数据集之间每个患者和每个时刻的得分。但是,在某些情况下,Days 变量会随着时间的推移而发生细微的变化,因此通过简单的连接来比较数据集是行不通的。示例:
library(dplyr)
> full_join(df1, df2, by=c("ID","Days")) %>%
+ arrange(.[[1]], as.numeric(.[[2]]))
ID Days Score.x Score.y
1 patient1 0 NA 1
2 patient1 25 2 10
3 patient1 235 3 NA
4 patient1 248 NA 3
5 patient1 353 4 4
6 patient2 100 5 5
7 patient2 150 NA 7
8 patient3 503 NA 6
9 patient3 538 6 NA
此处,第 3 行和第 4 行包含相同测量的数据(得分为 3),但未连接,因为 Days 列的值不同(235 与 248)。
问题:我正在寻找一种在第二列(比如 30 天)上设置阈值的方法,这将导致以下输出:
> threshold <- 30
> *** insert join code ***
ID Days Score.x Score.y
1 patient1 0 NA 1
2 patient1 25 2 10
3 patient1 248 3 3
4 patient1 353 4 4
5 patient2 100 5 5
6 patient2 150 NA 7
7 patient3 503 NA 6
8 patient3 538 6 NA
此输出显示前一个输出的第 3 行和第 4 行已合并(因为 248-235 Days 的值。
要记住的三个主要条件是:
- 在同一 df(第 1 行和第 2 行)内的阈值内的连续天数不会合并。
- 在某些情况下,
Days变量的最多四个值存在于同一数据框中,因此不应合并。可能这些值之一确实存在于另一个数据帧的阈值中,并且必须合并这些值。请参阅下面示例中的第 3 行。 - 每个分数/天数/患者组合只能使用一次。如果合并满足所有条件,但仍有可能进行双重合并,则应使用第一个。
> df1
ID Days Score
1 patient1 0 1
2 patient1 5 2
3 patient1 10 3
4 patient1 15 4
5 patient1 50 5
> df2
ID Days Score
1 patient1 0 1
2 patient1 5 2
3 patient1 12 3
4 patient1 15 4
5 patient1 50 5
> df_combined
ID Days Score.x Score.y
1 patient1 0 1 1
2 patient1 5 2 2
3 patient1 12 3 3
4 patient1 15 4 4
5 patient1 50 5 5
为 CHINSOON12 编辑
> df1
ID Days Score
1: patient1 0 1
2: patient1 116 2
3: patient1 225 3
4: patient1 309 4
5: patient1 351 5
6: patient2 0 6
7: patient2 49 7
> df2
ID Days Score
1: patient1 0 11
2: patient1 86 12
3: patient1 195 13
4: patient1 279 14
5: patient1 315 15
6: patient2 0 16
7: patient2 91 17
8: patient2 117 18
我将您的解决方案包装在这样的函数中:
testSO2 <- function(DT1,DT2) {
setDT(DT1);setDT(DT2)
names(DT1) <- c("ID","Days","X")
names(DT2) <- c("ID","Days","Y")
DT1$Days <- as.numeric(DT1$Days)
DT2$Days <- as.numeric(DT2$Days)
DT1[, c("s1", "e1", "s2", "e2") := .(Days - 30L, Days + 30L, Days, Days)]
DT2[, c("s1", "e1", "s2", "e2") := .(Days, Days, Days - 30L, Days + 30L)]
byk <- c("ID", "s1", "e1")
setkeyv(DT1, byk)
setkeyv(DT2, byk)
o1 <- foverlaps(DT1, DT2)
byk <- c("ID", "s2", "e2")
setkeyv(DT1, byk)
setkeyv(DT2, byk)
o2 <- foverlaps(DT2, DT1)
olaps <- funion(o1, setcolorder(o2, names(o1)))[
is.na(Days), Days := i.Days]
outcome <- olaps[, {
if (all(!is.na(Days)) && any(Days == i.Days)) {
s <- .SD[Days == i.Days, .(Days = Days[1L],
X = X[1L],
Y = Y[1L])]
} else {
s <- .SD[, .(Days = max(Days, i.Days), X, Y)]
}
unique(s)
},
keyby = .(ID, md = pmax(Days, i.Days))][, md := NULL][]
return(outcome)
}
结果:
> testSO2(df1,df2)
ID Days X Y
1: patient1 0 1 11
2: patient1 116 2 12
3: patient1 225 3 13
4: patient1 309 4 14
5: patient1 315 4 15
6: patient1 351 5 NA
7: patient2 0 6 16
8: patient2 49 7 NA
9: patient2 91 NA 17
10: patient2 117 NA 18
如您所见,第 4 行和第 5 行是错误的。 df1 中Score 的值被使用了两次 (4)。这些行周围的正确输出应如下所示,因为每个分数(在本例中为 X 或 Y)只能使用一次:
ID Days X Y
4: patient1 309 4 14
5: patient1 315 NA 15
6: patient1 351 5 NA
以下数据框的代码。
> dput(df1)
structure(list(ID = c("patient1", "patient1", "patient1", "patient1",
"patient1", "patient2", "patient2"), Days = c("0", "116", "225",
"309", "351", "0", "49"), Score = 1:7), row.names = c(NA, 7L), class = "data.frame")
> dput(df2)
structure(list(ID = c("patient1", "patient1", "patient1", "patient1",
"patient1", "patient2", "patient2", "patient2"), Days = c("0",
"86", "195", "279", "315", "0", "91", "117"), Score = 11:18), row.names = c(NA,
8L), class = "data.frame")
【问题讨论】:
-
您是否尝试过采用不同的方法来解决问题?如果有一种方法可以标记测量所代表的内容,您可能会更好。您要求的解决方案容易因边缘情况而失败。
-
我必须在内容上比较许多不同的变量,所以标签并不是一个真正的选择。为了清楚起见,我在这里选择了一个数字分数,但实际上第 3 列的内容并不重要。第二列的合并是主要问题
-
这在您的数据集中没有发生,但是:如果在您的第一个连接数据集的第 3 行和第 4 行中,您会有
Score.x = 3和Score.y = 4怎么办?在这种情况下,您还想放弃其中一项测量吗? -
如果我理解正确,测量值不会被丢弃。在 df1 中,测量值为 3,第 235 天,而在 df2 中,测量值为 4,第 248 天。在这种情况下,这些天将连接在一起,但测量不会,因此最终结果将是
patient1 248 3 4 -
知道了。后续问题:关于您的第一个连接数据框,如果您在第 1 行和第 2 行(称为第 1.5 行)之间有一行
ID = patient1、Days = 13、Score.x = 1和Score.y = NA,该怎么办?您是否只希望第 2 行与第 1.5 行合并,即使它仍在第 1 行的 30 天内?