根据绘图颜色对相似度矩阵进行排序

问题描述

我有一些文档的相似度矩阵图。我想对numpynd数组的矩阵值进行排序,以对颜色进行分组,同时保持它们的相对位置(对角黄线)和标签

path = "C:\\Users\\user\\Desktop\\texts\\dataset"
text_files = os.listdir(path)
#print (text_files)

tfidf_vectorizer = TfidfVectorizer()
documents = [open(f,encoding="utf-8").read() for f in text_files if f.endswith('.txt')]
sparse_matrix = tfidf_vectorizer.fit_transform(documents)

labels = []
for f in text_files:
    if f.endswith('.txt'):
        labels.append(f)

pairwise_similarity = sparse_matrix * sparse_matrix.T

pairwise_similarity_array = pairwise_similarity.toarray()
 
fig,ax = plt.subplots(figsize=(20,20))
cax = ax.matshow(pairwise_similarity_array,interpolation='spline16')
ax.grid(True)
plt.title('News articles similarity matrix')
plt.xticks(range(23),labels,rotation=90);
plt.yticks(range(23),labels);
fig.colorbar(cax,ticks=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1])
plt.show() 

enter image description here

解决方法

这是一种可能性。 想法是使用相似性矩阵中的信息,并在元素相似时将元素彼此相邻。如果两个项目相似,则它们相对于其他元素也应相似,即具有相似的颜色。 我首先从与所有其他元素(该选择有点随意)[a]最为共同的元素开始,然后从其余元素中选择与当前[b]最接近的元素。>

import numpy as np
import matplotlib.pyplot as plt

def create_dummy_sim_mat(n):
    sm = np.random.random((n,n))
    sm = (sm + sm.T) / 2
    sm[range(n),range(n)] = 1
    return sm

def argsort_sim_mat(sm):
    idx = [np.argmax(np.sum(sm,axis=1))]  # a
    for i in range(1,len(sm)):
        sm_i = sm[idx[-1]].copy()
        sm_i[idx] = -1
        idx.append(np.argmax(sm_i))  # b
    return np.array(idx)


n = 10
sim_mat = create_dummy_sim_mat(n=n)

idx = argsort_sim_mat(sim_mat)
sim_mat2 = sim_mat[idx,:][:,idx]  # apply reordering for rows and columns

# Plot results
fig,ax = plt.subplots(1,2)
ax[0].imshow(sim_mat)
ax[1].imshow(sim_mat2)

def ticks(_ax,ti,la):
    _ax.set_xticks(ti)
    _ax.set_yticks(ti)
    _ax.set_xticklabels(la)
    _ax.set_yticklabels(la)

ticks(_ax=ax[0],ti=range(n),la=range(n))
ticks(_ax=ax[1],la=idx)

similarity sorted


meTchaikovsky回答之后,我还在集群相似矩阵上测试了我的想法(参见第一张图片),该方法有效,但并不完美(参见第二张图片)。

因为我将两个元素之间的相似度用作与所有其他元素相似度的近似值,所以很清楚为什么这不能完美地起作用。 因此,可以不用计算初始相似度来对元素进行排序,而是可以计算一个二阶相似度矩阵,该矩阵可以测量相似度的相似度(sorry。 此度量可以更好地描述您感兴趣的内容。如果两行/两列具有相似的颜色,则它们应该彼此靠近。矩阵排序算法与以前相同

def add_cluster(sm,c=3):
    idx_cluster = np.array_split(np.random.permutation(np.arange(len(sm))),c)
    for ic in idx_cluster:
        cluster_noise = np.random.uniform(0.9,1.0,(len(ic),)*2)
        sm[ic[np.newaxis,:],ic[:,np.newaxis]] = cluster_noise

def get_sim_mat2(sm):
    return 1 / (np.linalg.norm(sm[:,np.newaxis] - sm[np.newaxis],axis=-1) + 1/n)

sim_mat = create_dummy_sim_mat(n=100)
add_cluster(sim_mat,c=4)
sim_mat2 = get_sim_mat2(sim_mat)

idx = argsort_sim_mat(sim_mat)
idx2 = argsort_sim_mat(sim_mat2)
sim_mat_sorted = sim_mat[idx,idx]
sim_mat_sorted2 = sim_mat[idx2,idx2]

# Plot results
fig,3)
ax[0].imshow(sim_mat)
ax[1].imshow(sim_mat_sorted)
ax[2].imshow(sim_mat_sorted2)

similarity**2 sorted

第二种方法的结果非常好(请参见第三张图片) 但我想有些情况下这种方法也会失败,所以我很乐意提供反馈。


编辑

我试图解释它,并确实将思想与[a]和[b]链接到代码,但是显然我做得不好,所以这里是第二个更详细的解释。

您有n个元素和一个n x n相似度矩阵sm,其中每个单元格(i,j)描述了元素i与元素j有多相似。目的是对行/列进行排序,以便可以看到相似矩阵中的现有模式。我的想法很简单。

从一个空列表开始,然后逐个添加元素。下一个元素的标准是与当前元素的相似性。如果在最后一步中添加了元素i,则接下来选择元素argmax(sm [i,:]),而忽略已添加到列表中的元素。我通过将这些元素的值设置为-1来忽略这些元素。

您可以使用函数ticks对标签重新排序:

labels = np.array(labels)  # make labels an numpy array,to index it with a list
ticks(_ax=ax[0],la=labels[idx])
,

@scleronomic's solution非常优雅,但是它也有一个不足,那就是我们无法在排序的相关矩阵中设置聚类的数量。假设我们正在使用一组变量,其中一些变量之间的相关性很弱

import string 
import numpy as np
import pandas as pd

n_variables = 20
n_clusters = 10
n_samples = 100

np.random.seed(100)
names = list(string.ascii_lowercase)[:n_variables]
belongs_to_cluster = np.random.randint(0,n_clusters,n_variables)

latent = np.random.randn(n_clusters,n_samples)
variables = np.random.rand(n_variables,n_samples)
for ind in range(n_clusters):
    mask = belongs_to_cluster == ind
    # weakening the correlation 
    if ind % 2 == 0:variables[mask] += latent[ind]*0.1
    variables[mask] += latent[ind]

df = pd.DataFrame({key:val for key,val in zip(names,variables)})
corr_mat = np.array(df.corr())

如您所见,按构造有10个变量簇,但是,簇中具有偶数索引的变量之间的相关性很弱。如果我们只想在排序的相关矩阵中看到大约5个聚类,也许我们需要找到另一种方法。

基于this post(是对问题"Clustering a correlation matrix"的公认答案),将相关矩阵分类为块,我们需要找到的是块,其中块内的相关性很高而且区块之间的相关性很低。但是,当我们首先知道有多少个块时,此接受的答案所提供的解决方案效果最好。更重要的是,基础块的大小相同或至少相似。因此,我使用新功能sort_corr_mat

改进了解决方案
def sort_corr_mat(corr_mat,clusters_guess): 
    
    def _swap_rows(corr_mat,var1,var2):
        rs = corr_mat.copy()
        rs[var2,rs[var1,:]= corr_mat[var1,corr_mat[var2,:]
        cs = rs.copy()
        cs[:,var2],cs[:,var1] = rs[:,var1],rs[:,var2]
        return cs

    # analysis
    max_iter = 500
    best_score,current_score,best_count = -1e8,-1e8,0
    num_minimua_to_visit = 20
    
    best_corr = corr_mat
    best_ordering = np.arange(n_variables)
    for i in range(max_iter):
        for row1 in range(n_variables):
            for row2 in range(n_variables):
                if row1 == row2: continue
                option_ordering = best_ordering.copy()
                option_ordering[row1],option_ordering[row2] = best_ordering[row2],best_ordering[row1]
                option_corr = _swap_rows(best_corr,row1,row2)
                option_score = score(option_corr,n_variables,clusters_guess)

                if option_score > best_score:
                    best_corr = option_corr
                    best_ordering = option_ordering
                    best_score = option_score

        if best_score > current_score:
            best_count += 1
            current_corr = best_corr
            current_ordering = best_ordering
            current_score = best_score
            
        if best_count >= num_minimua_to_visit:
            return best_corr#,best_ordering
        
    return best_corr#,best_ordering

首先使用此函数和corr_mat进行构造,然后将通过函数(右侧)获得的结果与通过@scleronomic's solution(中间)获得的结果进行比较

sim_mat_sorted = corr_mat[argsort_sim_mat(corr_mat),argsort_sim_mat(corr_mat)]
corr_mat_sorted = sort_corr_mat(corr_mat,clusters_guess=5)

# Plot results
fig,3,figsize=(18,6))
ax[0].imshow(corr_mat)
ax[1].imshow(sim_mat_sorted)
ax[2].imshow(corr_mat_sorted)

results

很显然,@scleronomic's solution的运行速度越来越好,但是我的解决方案提供了对输出模式的更多控制。