构造相似度矩阵的最有效方法
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
中有两个有用的函数可供您使用:pdist
和 squareform
。使用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.pdist
或sklearn.metrics.pairwise.pairwise_distances
【讨论】:
【参考方案3】:我认为你可以直接使用 pdist
和 squareform
在你的 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)
【讨论】:
以上是关于构造相似度矩阵的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章