根据 R 中的行列相似性修剪数据框

Prune a dataframe based on row-wise column similarity in R

我有一个非常大的基因组位点数据框,基因型评分为 0、1 或 2。这是我认为解决问题的一个非常小的样本:

x1  x2  x3  x4
0   0   1   0
0   0   1   0
1   1   2   1
1   1   1   1
2   2   0   1
2   2   1   2

基因座 x1 和 x2 相同,而 x4 高度相似。我希望实现的是创建一个函数,或者使用一个已经存在的函数,为我的每个基因座按行分配相似性分数,然后根据我设置的阈值相似性修剪数据集。

例如,如果我将阈值设置为 1 (100%),它只会修剪 x1 和 x2,因为它们是重复的——我知道该怎么做。但是,如果我将阈值设置为 0.8,即相似度为 80%,那么除了 x1 和 x2 之外,它还会修剪 x4。

重要的是,该函数作用于逐行相似性,而不仅仅是比较具有相似分布的 0、1 和 2 的列。

以下是我的处理方法。

首先,获取列名称的所有唯一配对的列表:

pairs <- expand.grid(names(df), names(df))
pairs <- pairs[lower.tri(replicate(length(df), names(df))),]

pairs
#>    Var1 Var2
#> 2    x2   x1
#> 3    x3   x1
#> 4    x4   x1
#> 7    x3   x2
#> 8    x4   x2
#> 12   x4   x3

现在遍历它以比较原始数据集中每对唯一列中相同行的比例。这为每对列提供了 0 到 1 之间的相似性分数:

pairs$similarity <- apply(pairs, 1, function(x) sum(df[x[1]] == df[x[2]])/nrow(df))

pairs
#>    Var1 Var2 similarity
#> 2    x2   x1  1.0000000
#> 3    x3   x1  0.1666667
#> 4    x4   x1  0.8333333
#> 7    x3   x2  0.1666667
#> 8    x4   x2  0.8333333
#> 12   x4   x3  0.1666667

现在删除此列表中相似度分数低于您选择的阈值的所有行(我们将在此处设为 0.8)

pairs <- pairs[which(pairs$similarity > 0.8),]

pairs
#>   Var1 Var2 similarity
#> 2   x2   x1  1.0000000
#> 4   x4   x1  0.8333333
#> 8   x4   x2  0.8333333

现在我们提取 Var1Var2 中的所有唯一列名称,因为这些列与至少一个其他列相似:

keep_cols <- as.character(sort(unique(c(pairs$Var1, pairs$Var2))))
#> [1] "x1" "x2" "x4"

然后我们使用它对原始数据框进行子集化以获得我们想要的结果:

df[match(keep_cols, names(df))]
#>   x1 x2 x4
#> 1  0  0  0
#> 2  0  0  0
#> 3  1  1  1
#> 4  1  1  1
#> 5  2  2  1
#> 6  2  2  2

当然,您可以将所有这些放在一个函数中,以便更轻松地调整阈值并迭代应用:

remove_dissimilar <- function(df, threshold = 0.8) {
  
  pairs <- expand.grid(names(df), names(df))
  pairs <- pairs[lower.tri(replicate(length(df), names(df))),]
  pairs$similarity <- apply(pairs, 1, function(x) {
    sum(df[x[1]] == df[x[2]])/nrow(df)})
  pairs <- pairs[which(pairs$similarity > threshold),]
  keep_cols <- as.character(sort(unique(c(pairs$Var1, pairs$Var2))))
  df[match(keep_cols, names(df))]
}

所以现在你可以这样做:

remove_dissimilar(df, 0.8)
#>   x1 x2 x4
#> 1  0  0  0
#> 2  0  0  0
#> 3  1  1  1
#> 4  1  1  1
#> 5  2  2  1
#> 6  2  2  2

remove_dissimilar(df, 0.9)
#>   x1 x2
#> 1  0  0
#> 2  0  0
#> 3  1  1
#> 4  1  1
#> 5  2  2
#> 6  2  2