我必须在48k患者数据与一些预测变量之间计算R中的余弦相似性(患者相似性度量)。这是等式:PSM(P1,P2)= P1.P2 / || P1 || || P2 ||
其中P1和P2是对应于两个不同患者的预测矢量,其中例如P1指数患者和P2将与指数(P1)进行比较,并且最终将计算成对患者相似性度量PSM(P1,P2)。
这个过程将继续为所有48k患者。
我在.csv文件中为300名患者添加了样本数据集。请在此处找到示例数据集。https://1drv.ms/u/s!AhoddsPPvdj3hVTSbosv2KcPIx5a
答案 0 :(得分:1)
首先要做的事情:你可以在以下任何一个帖子中找到更严格的余弦相似性处理:
现在,您的输入中显然有多种数据类型,至少
我怀疑某些整数值是布尔值或其他分类。通常,如果要将它们用作相似度计算的输入,则由您将它们转换为连续数值向量。例如,录取类型ELECTIVE
和EMERGENCY
之间的距离是什么?它是名义变量还是有序变量?我只会将我信任的列建模为数值因变量。
另外,您做了什么来确保您的某些专栏与其他专栏无关?仅使用一点数据科学和生物医学术语的意识,似乎可能是以下内容都是相关的:
diasbp_max
,diasbp_min
,meanbp_max
,meanbp_min
,sysbp_max
和sysbp_min
我建议去一家印刷厂订购海报大小的psm_pairs.pdf
打印件。 :-)你的眼睛更善于检测变量之间有意义的(但非线性)依赖关系。包括相同基本现象的多次测量可能会在相似度计算中超重该现象。不要忘记你可以派生出像
diasbp_rage <- diasbp_max - diasbp_min
现在,我并不擅长线性代数,所以我从lsa
文本分析包中导入余弦相似度函数。我很乐意看到你在你的问题中写出公式作为R函数。我会把它写成比较一行到另一行,并使用两个嵌套的apply循环来进行所有比较。希望我们能得到相同的结果!
在计算相似度后,我试图找到两个不同遭遇的不同患者。
由于您正在处理相对较大的行数,因此您需要比较效率的各种算法方法。此外,您可以在群集上使用SparkR /其他一些Hadoop解决方案,或在具有多个内核和批次 RAM的单台计算机上使用parallel包。我不知道我提供的解决方案是否是线程安全的。
考虑到这一点,单独换位(因为我实施它)对于一组100万患者来说可能是计算成本高昂的。总的来说,(如果我记得我的计算复杂度正确)随着输入行数的增加,性能会呈指数级下降。
library(lsa)
library(reshape2)
psm_sample <- read.csv("psm_sample.csv")
row.names(psm_sample) <-
make.names(paste0("patid.", as.character(psm_sample$subject_id)), unique = TRUE)
temp <- sapply(psm_sample, class)
temp <- cbind.data.frame(names(temp), as.character(temp))
names(temp) <- c("variable", "possible.type")
numeric.cols <- (temp$possible.type %in% c("factor", "integer") &
(!(grepl(
pattern = "_id$", x = temp$variable
))) &
(!(
grepl(pattern = "_code$", x = temp$variable)
)) &
(!(
grepl(pattern = "_type$", x = temp$variable)
))) | temp$possible.type == "numeric"
psm_numerics <- psm_sample[, numeric.cols]
row.names(psm_numerics) <- row.names(psm_sample)
psm_numerics$gender <- as.integer(psm_numerics$gender)
psm_scaled <- scale(psm_numerics)
pair.these.up <- psm_scaled
# checking for independence of variables
# if the following PDF pair plot is too big for your computer to open,
# try pair-plotting some random subset of columns
# keep.frac <- 0.5
# keep.flag <- runif(ncol(psm_scaled)) < keep.frac
# pair.these.up <- psm_scaled[, keep.flag]
# pdf device sizes are in inches
dev <-
pdf(
file = "psm_pairs.pdf",
width = 50,
height = 50,
paper = "special"
)
pairs(pair.these.up)
dev.off()
#transpose the dataframe to get the
#similarity between patients
cs <- lsa::cosine(t(psm_scaled))
# this is super inefficnet, because cs contains
# two identical triangular matrices
cs.melt <- melt(cs)
cs.melt <- as.data.frame(cs.melt)
names(cs.melt) <- c("enc.A", "enc.B", "similarity")
extract.pat <- function(enc.col) {
my.patients <-
sapply(enc.col, function(one.pat) {
temp <- (strsplit(as.character(one.pat), ".", fixed = TRUE))
return(temp[[1]][[2]])
})
return(my.patients)
}
cs.melt$pat.A <- extract.pat(cs.melt$enc.A)
cs.melt$pat.B <- extract.pat(cs.melt$enc.B)
same.pat <- cs.melt[cs.melt$pat.A == cs.melt$pat.B ,]
different.pat <- cs.melt[cs.melt$pat.A != cs.melt$pat.B ,]
most.dissimilar <-
different.pat[which.min(different.pat$similarity),]
dissimilar.pat.frame <- rbind(psm_numerics[rownames(psm_numerics) ==
as.character(most.dissimilar$enc.A) ,],
psm_numerics[rownames(psm_numerics) ==
as.character(most.dissimilar$enc.B) ,])
print(t(dissimilar.pat.frame))
给出了
patid.68.49 patid.9
gender 1.00000 2.00000
age 41.85000 41.79000
sysbp_min 72.00000 106.00000
sysbp_max 95.00000 217.00000
diasbp_min 42.00000 53.00000
diasbp_max 61.00000 107.00000
meanbp_min 52.00000 67.00000
meanbp_max 72.00000 132.00000
resprate_min 20.00000 14.00000
resprate_max 35.00000 19.00000
tempc_min 36.00000 35.50000
tempc_max 37.55555 37.88889
spo2_min 90.00000 95.00000
spo2_max 100.00000 100.00000
bicarbonate_min 22.00000 26.00000
bicarbonate_max 22.00000 30.00000
creatinine_min 2.50000 1.20000
creatinine_max 2.50000 1.40000
glucose_min 82.00000 129.00000
glucose_max 82.00000 178.00000
hematocrit_min 28.10000 37.40000
hematocrit_max 28.10000 45.20000
potassium_min 5.50000 2.80000
potassium_max 5.50000 3.00000
sodium_min 138.00000 136.00000
sodium_max 138.00000 140.00000
bun_min 28.00000 16.00000
bun_max 28.00000 17.00000
wbc_min 2.50000 7.50000
wbc_max 2.50000 13.70000
mingcs 15.00000 15.00000
gcsmotor 6.00000 5.00000
gcsverbal 5.00000 0.00000
gcseyes 4.00000 1.00000
endotrachflag 0.00000 1.00000
urineoutput 1674.00000 887.00000
vasopressor 0.00000 0.00000
vent 0.00000 1.00000
los_hospital 19.09310 4.88130
los_icu 3.53680 5.32310
sofa 3.00000 5.00000
saps 17.00000 18.00000
posthospmort30day 1.00000 0.00000
答案 1 :(得分:0)
通常我不会添加第二个答案,但这可能是最好的解决方案。不要担心投票。
这里的算法与第一个答案相同,适用于虹膜数据集。每行包含四种空间测量的花形成三种不同品种的虹膜植物。
在下面你会发现虹膜分析,写成嵌套循环,这样你就可以看到等价。但是不推荐用于大数据集的制作。
请熟悉启动数据和所有中间数据帧:
iris
数据psm_scaled
(空间测量,缩放为平均值= 0,SD = 1)cs
(成对相似性的矩阵)cs.melt
(长格式的成对相似性)最后,我汇总了一个品种与另一品种之间所有比较的平均相似性。您将看到相同品种的个体之间的比较具有接近1的平均相似性,并且相同品种的个体之间的比较具有接近负 1的平均相似性。
library(lsa)
library(reshape2)
temp <- iris[, 1:4]
iris.names <- paste0(iris$Species, '.', rownames(iris))
psm_scaled <- scale(temp)
rownames(psm_scaled) <- iris.names
cs <- lsa::cosine(t(psm_scaled))
# this is super inefficient, because cs contains
# two identical triangular matrices
cs.melt <- melt(cs)
cs.melt <- as.data.frame(cs.melt)
names(cs.melt) <- c("enc.A", "enc.B", "similarity")
names(cs.melt) <- c("flower.A", "flower.B", "similarity")
class.A <-
strsplit(as.character(cs.melt$flower.A), '.', fixed = TRUE)
cs.melt$class.A <- sapply(class.A, function(one.split) {
return(one.split[1])
})
class.B <-
strsplit(as.character(cs.melt$flower.B), '.', fixed = TRUE)
cs.melt$class.B <- sapply(class.B, function(one.split) {
return(one.split[1])
})
cs.melt$comparison <-
paste0(cs.melt$class.A , '_vs_', cs.melt$class.B)
cs.agg <-
aggregate(cs.melt$similarity, by = list(cs.melt$comparison), mean)
print(cs.agg[order(cs.agg$x),])
给出了
# Group.1 x
# 3 setosa_vs_virginica -0.7945321
# 7 virginica_vs_setosa -0.7945321
# 2 setosa_vs_versicolor -0.4868352
# 4 versicolor_vs_setosa -0.4868352
# 6 versicolor_vs_virginica 0.3774612
# 8 virginica_vs_versicolor 0.3774612
# 5 versicolor_vs_versicolor 0.4134413
# 9 virginica_vs_virginica 0.7622797
# 1 setosa_vs_setosa 0.8698189
如果您仍然不熟悉在缩放的数值数据框架上执行lsa::cosine()
,我们当然可以进行明确的成对计算。
您为PSM提供的公式或患者的余弦相似性在Wikipedia
以两种格式表示记住 A 和 B 的向量代表 PatientA 和 PatientB 的属性的有序列表,PSM是 A 和 B 的点积除以([强> A 的幅度]和[强度>的强度的标量乘积>乙强>])
在R中简洁的说法是
cosine.sim <- function(A, B) { A %*% B / sqrt(A %*% A * B %*% B) }
但我们可以重写一下,看起来与你的帖子更相似
cosine.sim <- function(A, B) { A %*% B / (sqrt(A %*% A) * sqrt(B %*% B)) }
我猜你甚至可以重写(一对个体之间的相似性计算)作为一堆嵌套循环,但是在可管理的数据量的情况下,请不要< / strong>即可。 R针对向量和矩阵的操作进行了高度优化。如果您是R的新手,请不要再猜测它。顺便说一句,你的数百万行发生了什么?现在你的压力肯定会小到数万。
无论如何,让我们说每个人只有两个元素。
individual.1 <- c(1, 0)
individual.2 <- c(1, 1)
因此,您可以将individual.1视为在原点(0,0)和(0,1)之间传递的线,以及在原点和(1,1)之间传递的线。 / p>
some.data <- rbind.data.frame(individual.1, individual.2)
names(some.data) <- c('element.i', 'element.j')
rownames(some.data) <- c('individual.1', 'individual.2')
plot(some.data, xlim = c(-0.5, 2), ylim = c(-0.5, 2))
text(
some.data,
rownames(some.data),
xlim = c(-0.5, 2),
ylim = c(-0.5, 2),
adj = c(0, 0)
)
segments(0, 0, x1 = some.data[1, 1], y1 = some.data[1, 2])
segments(0, 0, x1 = some.data[2, 1], y1 = some.data[2, 2])
那么vector individual.1 和vector individual.2 之间的角度是什么?你猜对了,0.785弧度,或45度。
cosine.sim <- function(A, B) { A %*% B / (sqrt(A %*% A) * sqrt(B %*% B)) }
cos.sim.result <- cosine.sim(individual.1, individual.2)
angle.radians <- acos(cos.sim.result)
angle.degrees <- angle.radians * 180 / pi
print(angle.degrees)
# [,1]
# [1,] 45
现在我们可以使用我之前在两个嵌套循环中定义的cosine.sim
函数来明确计算每个鸢尾花之间的成对相似性。请注意,psm_scaled
已被定义为iris
数据集中的缩放数值。
cs.melt <- lapply(rownames(psm_scaled), function(name.A) {
inner.loop.result <-
lapply(rownames(psm_scaled), function(name.B) {
individual.A <- psm_scaled[rownames(psm_scaled) == name.A, ]
individual.B <- psm_scaled[rownames(psm_scaled) == name.B, ]
similarity <- cosine.sim(individual.A, individual.B)
return(list(name.A, name.B, similarity))
})
inner.loop.result <-
do.call(rbind.data.frame, inner.loop.result)
names(inner.loop.result) <-
c('flower.A', 'flower.B', 'similarity')
return(inner.loop.result)
})
cs.melt <- do.call(rbind.data.frame, cs.melt)
现在我们重复上述cs.melt$class.A
,cs.melt$class.B
和cs.melt$comparison
的计算,并将cs.agg.from.loops
计算为各种比较之间的平均相似度:
cs.agg.from.loops <-
aggregate(cs.agg.from.loops$similarity, by = list(cs.agg.from.loops $comparison), mean)
print(cs.agg.from.loops[order(cs.agg.from.loops$x),])
# Group.1 x
# 3 setosa_vs_virginica -0.7945321
# 7 virginica_vs_setosa -0.7945321
# 2 setosa_vs_versicolor -0.4868352
# 4 versicolor_vs_setosa -0.4868352
# 6 versicolor_vs_virginica 0.3774612
# 8 virginica_vs_versicolor 0.3774612
# 5 versicolor_vs_versicolor 0.4134413
# 9 virginica_vs_virginica 0.7622797
# 1 setosa_vs_setosa 0.8698189
我认为这与我们使用lsa::cosine
得到的结果相同。
所以我想说的是......为什么你不能使用lsa::cosine
?
也许你应该更关注
如前所述