构造相似度矩阵的最有效方法

Posted

技术标签:

【中文标题】构造相似度矩阵的最有效方法【英文标题】:Most efficient way to construct similarity matrix 【发布时间】:2016-06-15 23:28:49 【问题描述】:

我正在使用以下链接创建“欧几里得相似矩阵”(我将其转换为 DataFrame)。 https://stats.stackexchange.com/questions/53068/euclidean-distance-score-and-similarity http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.spatial.distance.euclidean.html

我这样做的方式是一种迭代方法,它有效,但是当数据集很大时需要一段时间。 pandas pd.DataFrame.corr() 对于 pearson 相关性非常快速且有用。

如何在没有穷举迭代的情况下执行欧几里得相似度测量?

下面是我的幼稚代码:

#Euclidean Similarity

#Create DataFrame
DF_var = pd.DataFrame.from_dict("s1":[1.2,3.4,10.2],"s2":[1.4,3.1,10.7],"s3":[2.1,3.7,11.3],"s4":[1.5,3.2,10.9]).T
DF_var.columns = ["g1","g2","g3"]
#      g1   g2    g3
# s1  1.2  3.4  10.2
# s2  1.4  3.1  10.7
# s3  2.1  3.7  11.3
# s4  1.5  3.2  10.9

#Create empty matrix to fill
M_euclid = np.zeros((DF_var.shape[1],DF_var.shape[1]))

#Iterate through DataFrame columns to measure euclidean distance
for i in range(DF_var.shape[1]):
    u = DF_var[DF_var.columns[i]]
    for j in range(DF_var.shape[1]):
        v = DF_var[DF_var.columns[j]]
        #Euclidean distance -> Euclidean similarity
        M_euclid[i,j] = (1/(1+sp.spatial.distance.euclidean(u,v)))
DF_euclid = pd.DataFrame(M_euclid,columns=DF_var.columns,index=DF_var.columns)

#           g1        g2        g3
# g1  1.000000  0.215963  0.051408
# g2  0.215963  1.000000  0.063021
# g3  0.051408  0.063021  1.000000

【问题讨论】:

【参考方案1】:

scipy.spatial.distance 中有两个有用的函数可供您使用:pdistsquareform。使用pdist 会将观测值之间的成对距离作为一维数组提供,squareform 会将其转换为距离矩阵。

一个问题是pdist 默认使用距离度量,而不是相似度,因此您需要手动指定相似度函数。从您的代码中的注释输出判断,您的 DataFrame 也不是 pdist 期望的方向,所以我已经撤消了您在代码中所做的转置。

import pandas as pd
from scipy.spatial.distance import euclidean, pdist, squareform


def similarity_func(u, v):
    return 1/(1+euclidean(u,v))

DF_var = pd.DataFrame.from_dict("s1":[1.2,3.4,10.2],"s2":[1.4,3.1,10.7],"s3":[2.1,3.7,11.3],"s4":[1.5,3.2,10.9])
DF_var.index = ["g1","g2","g3"]

dists = pdist(DF_var, similarity_func)
DF_euclid = pd.DataFrame(squareform(dists), columns=DF_var.index, index=DF_var.index)

【讨论】:

嘿@root,感谢您阐明 pdist 和 squareform 的使用!为什么相似度函数后的对角矩阵是0.0? 注意!对角线可以用@B.M.的凯文回答中的评论来修复【参考方案2】:

你想要scipy.spatial.distance.pdistsklearn.metrics.pairwise.pairwise_distances

【讨论】:

【参考方案3】:

我认为你可以直接使用 pdistsquareform 在你的 DataFrame 上广播:

from scipy.spatial.distance import pdist,squareform

In [6]: squareform(pdist(DF_var, metric='euclidean'))

Out[6]:
array([[ 0.        ,  0.6164414 ,  1.4525839 ,  0.78740079],
       [ 0.6164414 ,  0.        ,  1.1       ,  0.24494897],
       [ 1.4525839 ,  1.1       ,  0.        ,  0.87749644],
       [ 0.78740079,  0.24494897,  0.87749644,  0.        ]])

【讨论】:

先转置:In [247]: 1/(1+squareform(pdist(DF_var.T))) Out[247]: array([[ 1. , 0.21596281, 0.05140761], [ 0.21596281 , 1. , 0.06302091], [ 0.05140761, 0.06302091, 1. ]])【参考方案4】:

我能找到的获得与 OP 相同结果的最简单方法是使用distance_matrix,同样来自 scipy.spatial。整个事情可以用一种长线来完成。

import numpy as np
import pandas as pd
from scipy.spatial import distance_matrix

# Original code from OP, slightly reformatted
DF_var = pd.DataFrame.from_dict(
    "s1":[1.2,3.4,10.2],
    "s2":[1.4,3.1,10.7],
    "s3":[2.1,3.7,11.3],
    "s4":[1.5,3.2,10.9]
).T
DF_var.columns = ["g1","g2","g3"]

# Whole similarity algorithm in one line
df_euclid = pd.DataFrame(
    1 / (1 + distance_matrix(DF_var.T, DF_var.T)),
    columns=DF_var.columns, index=DF_var.columns
)

#           g1        g2        g3
# g1  1.000000  0.215963  0.051408
# g2  0.215963  1.000000  0.063021
# g3  0.051408  0.063021  1.000000

上面的代码应该复制粘贴并在任何 python IDE 中运行。

【讨论】:

你将如何用你的方法计算余弦距离/相似度? @moritz - 这个解决方案使用了一个明确用于欧几里得距离的函数,我经常使用它。 scipy.spatial.distance.cosine (docs.scipy.org/doc/scipy/reference/generated/…) 可能会做你想做的事。但我没有使用或测试过它。如果仅用 distance.cosine 替换 distance_matrix 函数不起作用,您应该尝试一下并提出一个新问题。 OP 要求提供相似度矩阵,而不是距离矩阵,因此您还应该包含将距离转换为相似度的位。【参考方案5】:

这就是我所做的:

from scipy.spatial.distance import euclidean

DF_var = pd.DataFrame.from_dict("s1":[1.2,3.4,10.2],"s2":[1.4,3.1,10.7],"s3":[2.1,3.7,11.3],"s4":[1.5,3.2,10.9]).T
DF_var.columns = ["g1","g2","g3"]

def m_euclid(v1, v2):
    return (1/(1 + euclidean(v1,v2)))

dist_list = []
for j1 in DF_var.columns:
    dist_list.append([m_euclid(DF_var[j1], DF_var[j2]) for j2 in DF_var.columns])

dist_matrix = pd.DataFrame(dist_list)

【讨论】:

以上是关于构造相似度矩阵的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章

测量两个短音频相似度的最简单算法

在给定稀疏矩阵数据的情况下,Python 中计算余弦相似度的最快方法是啥?

怎么比较两个向量组相似度

余弦相似度计算

余弦相似度的应用

有没有一种根据 Jaccard 相似度对图进行聚类的有效方法?