簇质心之间的加权网络 - Python

Weighted network between cluster centroids - Python

下图绘制了从两组数据点导出的向量。我还使用 k 均值聚类测量和绘制这些点的质心。

我希望测量某种形式的邻接矩阵,以根据向量的数量绘制每个集群之间的网络,这也说明了每个集群之间的向量数量。所以显示重量。

我在想邻接矩阵的对角线值可以表示同一簇中向量的数量,而非对角线值可以表示不同簇之间的向量数,同时考虑方向?

我希望为下面的输出生成一个输出。其中节点是集群的质心。节点的直径应表示同一簇中的向量数,线条粗细是两个簇之间的向量数。

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans

fig, ax = plt.subplots(figsize = (6,6))

df = pd.DataFrame(np.random.randint(-80,80,size=(500, 4)), columns=list('ABCD'))

A = df['A']
B = df['B']

C = df['C']
D = df['D']

Y_sklearn = df[['A','B','C','D']].values

ax.quiver(A, B, (C-A), (D-B), angles = 'xy', scale_units = 'xy', scale = 1, alpha = 0.5) 

model = KMeans(n_clusters = 20)
model.fit(Y_sklearn)

model.cluster_centers_ 
cluster_centers = model.cluster_centers_
plt.scatter(cluster_centers[:, 0], cluster_centers[:, 1], 
            color = 'black', s = 100, 
            alpha = 0.7, zorder = 2)

plt.scatter(Y_sklearn[:,0], Y_sklearn[:,1], color = 'blue', alpha = 0.2); 
plt.scatter(Y_sklearn[:,2], Y_sklearn[:,3], color = 'red', alpha = 0.2); 

编辑 2:

如果修复数据得到下面的预期网络,下面总共绘制了12个向量。两组 5 个是重叠的,而两个是唯一的。

df = pd.DataFrame({
    'A' : [5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 5, 4],                        
    'B' : [5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 5, 2],              
    'C' : [7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 7],   
    'D' : [7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 4, 7],                                    
    })

fig,ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)

A = df['A']
B = df['B']

C = df['C']
D = df['D']

ax.quiver(A, B, (C-A), (D-B), angles = 'xy', scale_units = 'xy', scale = 1, alpha = 0.5) 

如果我只是用聚类质心绘制散点图,它应该如下所示:

Y_sklearn = df[['A','B','C','D']].values

model = KMeans(n_clusters = 4)
model.fit(Y_sklearn)

model.cluster_centers_ 
cluster_centers = model.cluster_centers_

plt.scatter(cluster_centers[:, 0], cluster_centers[:, 1], 
        color = 'black', s = 100, 
        alpha = 0.7, zorder = 2)

plt.scatter(cluster_centers[:, 2], cluster_centers[:, 3], 
        color = 'black', s = 100, 
        alpha = 0.7, zorder = 2)

一切正常。下一步是我遇到麻烦的地方。如果我手动绘制网络,它应该看起来像这样。较粗的线显示质心之间的 5 个向量,而较细的线显示 1 个向量。

更新后的代码生成以下网络。 5,5 - 7,7 行是正确的,但我没有得到应该复制类似于上面网络的内容的其他行。

fig,ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)

def kmeans(arr,num_clusters):

    model = KMeans(n_clusters = num_clusters)
    model.fit(arr)
    model.cluster_centers_ 
    cluster_centers = model.cluster_centers_
    all_labels = model.labels_
    mem_count = Counter(all_labels)

    return cluster_centers,all_labels,mem_count

nclusters_1,nclusters_2 = 2,2
points= df[['A','B','C','D']].values
cluster_one = kmeans(points[:,:2],nclusters_1)
cluster_two = kmeans(points[:,2:],nclusters_2)

# find connections between clusters 
all_combs = [[n1,n2] for n1 in range(nclusters_1) for n2 in range(nclusters_2)]
num_connections = {}
for item in all_combs:
    l1,l2 = cluster_one[1],cluster_two[1]
    mask1 = np.where(l1==item[0])[0]
    mask2 = np.where(l2==item[1])[0]
    num_common = len(list(set(mask1).intersection(mask2)))
    num_connections[(item[0],item[1]+nclusters_1)] = num_common

G = nx.Graph()
node_sizes = {}
node_colors = {}
for k,v in num_connections.items():
    # the number of points in the two clusters 
    s1,s2 = cluster_one[2][k[0]],cluster_two[2][k[1]-nclusters_1]
    G.add_node(k[0],pos=points[:,:2][k[0]])
    G.add_node(k[1],pos=points[:,2:][k[1]])
    G.add_edge(k[0],k[1],color='k',weight=v/3)
    node_sizes[k[0]] = s1;node_sizes[k[1]] = s2
    node_colors[k[0]] = 'k';node_colors[k[1]] = 'k'

edges = G.edges()
d = dict(G.degree)
pos=nx.get_node_attributes(G,'pos')
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G,pos,edges=edges,
        node_color=[node_colors[v] for v in d.keys()],
        nodelist=d.keys(),
        width=weights,
        node_size=[node_sizes[v]*20 for v in d.keys()])

在解决问题之前,我猜测应该对df的前两列和其他两列分别进行KMeans聚类。如果将 KMeans 聚类直接应用于整个 df,两个聚类 (A,B)(C,D) 之间的连接将是微不足道的。

如果可以在您的项目中使用 networkx,请按以下方法实现您的目标。一、准备资料

import numpy as np
import pandas as pd
from collections import Counter
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans


def kmeans(arr,num_clusters):
    
    model = KMeans(n_clusters = num_clusters)
    model.fit(arr)
    model.cluster_centers_ 
    cluster_centers = model.cluster_centers_
    all_labels = model.labels_
    mem_count = Counter(all_labels)
    
    return cluster_centers,all_labels,mem_count
    
df = pd.DataFrame(np.random.randint(-80,80,size=(200, 4)), columns=list('ABCD'))
# tail 
A,B = df['A'],df['B']
# end 
C,D = df['C'],df['D']

nclusters_1,nclusters_2 = 20,20
points= df[['A','B','C','D']].values
cluster_one = kmeans(points[:,:2],nclusters_1)
cluster_two = kmeans(points[:,2:],nclusters_2)

# find connections between clusters 
all_combs = [[n1,n2] for n1 in range(nclusters_1) for n2 in range(nclusters_2)]
num_connections = {}
for item in all_combs:
    l1,l2 = cluster_one[1],cluster_two[1]
    mask1 = np.where(l1==item[0])[0]
    mask2 = np.where(l2==item[1])[0]
    num_common = len(list(set(mask1).intersection(mask2)))
    num_connections[(item[0],item[1]+nclusters_1)] = num_common

正如你在上面的代码中看到的,我分别对(A,B)(C,D)进行了KMeans聚类,你可以对两组点使用不同数量的聚类通过使用不同的 nclusters_1nclusters_2。准备好数据后,我们现在可以使用 networkx

将其可视化
# plot the graph 
import networkx as nx

G = nx.Graph()
node_sizes = {}
node_colors = {}
for k,v in num_connections.items():
    # the number of points in the two clusters 
    s1,s2 = cluster_one[2][k[0]],cluster_two[2][k[1]-nclusters_1]
    G.add_node(k[0],pos=cluster_one[0][k[0]])
    G.add_node(k[1],pos=cluster_two[0][k[1]-nclusters_1])
    G.add_edge(k[0],k[1],color='k',weight=v/3)
    node_sizes[k[0]] = s1;node_sizes[k[1]] = s2
    node_colors[k[0]] = 'navy';node_colors[k[1]] = 'darkviolet'
    
edges = G.edges()
d = dict(G.degree)
pos=nx.get_node_attributes(G,'pos')
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G,pos,edges=edges,
        node_color=[node_colors[v] for v in d.keys()],
        nodelist=d.keys(),
        width=weights,
        node_size=[node_sizes[v]*20 for v in d.keys()])

输出图看起来像

在此图中,点的实际位置用于绘制,(A,B) 着色为 navy(C,D) 着色为 violet。如果你想扩大节点大小,只需使用与此行 20 不同的数字

node_size=[node_sizes[v]*20 for v in d.keys()]

为了调整边缘的宽度,可以在此行中使用 3 以外的其他数字

G.add_edge(k[0],k[1],color='k',weight=v/3)

更新

上面的脚本中,num_connections的键代表两个图节点,对应的值代表连接数。为了从num_connections中提取邻接矩阵,你可以尝试

adj_mat = np.zeros((nclusters_1,nclusters_2))
for k,v in num_connections.items():
    entry = (k[0],k[1]-nclusters_1)
    adj_mat[entry] = v

更新 2

解决 OP 更新后的问题 post,

  1. 添加此 pos=nx.get_node_attributes(G,'pos') 以固定节点位置
  2. G.add_node(k[0],pos=points[:,:2][k[0]])G.add_node(k[1],pos=points[:,2:][k[1]])更改为G.add_node(k[0],pos=cluster_one[0][k[0]])G.add_node(k[1],pos=cluster_two[0][k[1]-nclusters_1])

经过这两项修改,

df = pd.DataFrame({
    'A' : [5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 5, 4],                        
    'B' : [5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 5, 2],              
    'C' : [7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 7],   
    'D' : [7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 4, 7],                                    
    })

fig,ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)

A = df['A']
B = df['B']

C = df['C']
D = df['D']

def kmeans(arr,num_clusters):

    model = KMeans(n_clusters = num_clusters)
    model.fit(arr)
    model.cluster_centers_ 
    cluster_centers = model.cluster_centers_
    all_labels = model.labels_
    mem_count = Counter(all_labels)

    return cluster_centers,all_labels,mem_count

nclusters_1,nclusters_2 = 2,2
points= df[['A','B','C','D']].values
cluster_one = kmeans(points[:,:2],nclusters_1)
cluster_two = kmeans(points[:,2:],nclusters_2)

# find connections between clusters 
all_combs = [[n1,n2] for n1 in range(nclusters_1) for n2 in range(nclusters_2)]
num_connections = {}
for item in all_combs:
    l1,l2 = cluster_one[1],cluster_two[1]
    mask1 = np.where(l1==item[0])[0]
    mask2 = np.where(l2==item[1])[0]
    num_common = len(list(set(mask1).intersection(mask2)))
    num_connections[(item[0],item[1]+nclusters_1)] = num_common

G = nx.Graph()
node_sizes = {}
node_colors = {}
for k,v in num_connections.items():
    # the number of points in the two clusters 
    s1,s2 = cluster_one[2][k[0]],cluster_two[2][k[1]-nclusters_1]
    G.add_node(k[0],pos=cluster_one[0][k[0]])
    G.add_node(k[1],pos=cluster_two[0][k[1]-nclusters_1])
    G.add_edge(k[0],k[1],color='k',weight=v/3)
    node_sizes[k[0]] = s1;node_sizes[k[1]] = s2
    node_colors[k[0]] = 'k';node_colors[k[1]] = 'k'

edges = G.edges()
d = dict(G.degree)
pos=nx.get_node_attributes(G,'pos')
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G,pos,edges=edges,
        node_color=[node_colors[v] for v in d.keys()],
        nodelist=d.keys(),
        width=weights,
        node_size=[node_sizes[v]*20 for v in d.keys()])