48k患者数据与预测变量之间的余弦相似性(患者相似性度量)

时间:2017-06-09 13:09:53

标签: r data-mining similarity cosine-similarity

我必须在48k患者数据与一些预测变量之间计算R中的余弦相似性(患者相似性度量)。这是等式:PSM(P1,P2)= P1.P2 / || P1 || || P2 ||
  PSM(P1,P2) = P1.P2/ ||P1|| ||P2||
其中P1和P2是对应于两个不同患者的预测矢量,其中例如P1指数患者和P2将与指数(P1)进行比较,并且最终将计算成对患者相似性度量PSM(P1,P2)。

这个过程将继续为所有48k患者。

我在.csv文件中为300名患者添加了样本数据集。请在此处找到示例数据集。https://1drv.ms/u/s!AhoddsPPvdj3hVTSbosv2KcPIx5a

2 个答案:

答案 0 :(得分:1)

首先要做的事情:你可以在以下任何一个帖子中找到更严格的余弦相似性处理:

现在,您的输入中显然有多种数据类型,至少

  • 小数
  • 整数
  • 分类

我怀疑某些整数值是布尔值或其他分类。通常,如果要将它们用作相似度计算的输入,则由您将它们转换为连续数值向量。例如,录取类型ELECTIVEEMERGENCY之间的距离是什么?它是名义变量还是有序变量?我只会将我信任的列建模为数值因变量。

另外,您做了什么来确保您的某些专栏与其他专栏无关?仅使用一点数据科学和生物医学术语的意识,似乎可能是以下内容都是相关的:

  

diasbp_maxdiasbp_minmeanbp_maxmeanbp_minsysbp_maxsysbp_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)

通常我不会添加第二个答案,但这可能是最好的解决方案。不要担心投票。

这里的算法与第一个答案相同,适用于虹膜数据集。每行包含四种空间测量的花形成三种不同品种的虹膜植物。

在下面你会发现虹膜分析,写成嵌套循环,这样你就可以看到等价。但是不推荐用于大数据集的制作。

请熟悉启动数据和所有中间数据帧:

  1. 输入iris数据
  2. psm_scaled(空间测量,缩放为平均值= 0,SD = 1)
  3. cs(成对相似性的矩阵)
  4. cs.melt(长格式的成对相似性)
  5. 最后,我汇总了一个品种与另一品种之间所有比较的平均相似性。您将看到相同品种的个体之间的比较具有接近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度。

    enter image description here

    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.Acs.melt$class.Bcs.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

    也许你应该更关注

    • 选择变量,包括删除高度相关的变量
    • 缩放/规范化/标准化数据
    • 使用大量输入数据集的性能
    • 识别已知的类似物和异化剂以进行质量控制

    如前所述