创建网络图

8

我的样本数据集以CSV格式呈现如下。

该无向图有90个节点,用数字{10、11、12……99}表示,它的边权重定义如下。

[样本数据]

node1 node2 weight
23     89    34.9  (i.e. there is an edge between node 23 and 89 with weight 34.9)
75     14    28.5
so on....

我希望能以网络形式展示这个问题。有哪些有效的方式可以表示它(例如Gephi、networkx等)。边的粗细应该代表边的权重。

4个回答

6
使用networkx,您可以添加带有属性的边。
import networkx as nx
G = nx.Graph()
G.add_edge(23, 89, weight=34.9)
G.add_edge(75, 14, weight=28.5)

5
如果您有一个大的csv文件,我建议在I/O部分使用pandasnetworkx有一个与pandas交互的有用方法,称为from_pandas_dataframe。 假设您的数据以上述格式存储在csv中,则此命令应该适用于您:
df = pd.read_csv('path/to/file.csv', columns=['node1', 'node2', 'weight'])

但为了演示,我将使用符合您要求的10个随机边缘(您不需要导入numpy,我只是用它来生成随机数):

import matplotlib as plt
import networkx as nx
import pandas as pd

#Generate Random edges and weights
import numpy as np
np.random.seed(0) # for reproducibility

w = np.random.rand(10) # weights 0-1
node1 = np.random.randint(10,19, (10))  # I used 10-19 for demo
node2 = np.random.randint(10,19, (10))
df = pd.DataFrame({'node1': node1, 'node2': node2, 'weight': w}, index=range(10))

前面的所有内容应该与您的pd.read_csv命令生成相同。 这将导致此DataFrame,df:
    node1   node2   weight
0   16  13  0.548814
1   17  15  0.715189
2   17  10  0.602763
3   18  12  0.544883
4   11  13  0.423655
5   15  18  0.645894
6   18  11  0.437587
7   14  13  0.891773
8   13  13  0.963663
9   10  13  0.383442

使用from_pandas_dataframe初始化MultiGraph。这假设您将有多个边连接到一个节点(未在OP中指定)。要使用此方法,您将需要在convert_matrix.py文件中对networkx源代码进行简单更改,实现 这里(它是一个简单的错误)。
MG = nx.from_pandas_dataframe(df, 
                              'node1', 
                              'node2', 
                               edge_attr='weight',
                               create_using=nx.MultiGraph()
                              )

这将生成您的MultiGraph,您可以使用draw来可视化它:

positions = nx.spring_layout(MG) # saves the positions of the nodes on the visualization
# pass positions and set hold=True
nx.draw(MG, pos=positions, hold=True, with_labels=True, node_size=1000, font_size=16)

详细说明: positions 是一个字典,其中每个节点都是一个键,其值是图表上的位置。我将在下面说明为什么我们存储 positions。通用的 draw 将使用指定的 positions 在你的 MultiGraph 实例 MG 上绘制节点。然而,正如你所看到的,边缘的宽度都是相同的:
Unweighted

但是你已经拥有了添加权重所需的一切。首先将权重放入名为 weights 的列表中。通过迭代(使用列表理解)每个边缘 edges,我们可以提取权重。我选择乘以5,因为它看起来最干净:

weights = [w[2]['weight']*5 for w in  MG.edges(data=True)]

最后,我们将使用 draw_networkx_edges,它仅绘制图的边缘(没有节点)。由于我们有节点的positions,并且我们设置了hold=True,我们可以在前面的可视化上直接绘制加权边缘。
nx.draw_networkx_edges(MG, pos=positions, width=weights) #width can be array of floats

Weighted

您可以看到节点 (14, 13) 在 DataFrame df中具有最重的线和最大的值(除了(13,13))。


在 nx.Multigraph() 中,我遇到了这个错误:TypeError: unhashable type: 'dict'。 - swyx
如果您在代码块之前的段落中所指出的更改,那么它应该可以正常工作。另一个关于 SO 问题的链接GH Issue。此外,如果您知道您的图形是 Graph 而不是 MultiGraph,则完全删除 create_using 参数也可以正常工作。 - Kevin

4
如果您使用Linux操作系统,并且假设您的CSV文件长这样(例如):
23;89;3.49
23;14;1.29
75;14;2.85
14;75;2.9
75;23;0.9
23;27;4.9

您可以使用这个程序:

import os

def build_G(csv_file):

    #init graph dict
    g={}

    #here we open csv file
    with open(csv_file,'r') as f:
        cont=f.read()

    #here we get field content
    for line in cont.split('\n'):
        if line != '':

            fields=line.split(';')

            #build origin node
            if g.has_key(fields[0])==False:
                g[fields[0]]={}

            #build destination node         
            if g.has_key(fields[1])==False:
                g[fields[1]]={}

            #build edge origin>destination
            if g[fields[0]].has_key(fields[1])==False:
                g[fields[0]][fields[1]]=float(fields[2])

    return g

def main():

    #filename
    csv_file="mynode.csv"

    #build graph
    G=build_G(csv_file)

    #G is now a python dict
    #G={'27': {}, '75': {'14': 2.85, '23': 0.9}, '89': {}, '14': {'75': 2.9}, '23': {'27': 4.9, '89': 3.49, '14': 1.29}}


    #write to file
    f = open('dotgraph.txt','w')
    f.writelines('digraph G {\nnode [width=.3,height=.3,shape=octagon,style=filled,color=skyblue];\noverlap="false";\nrankdir="LR";\n')
    f.writelines

    for i in G:
        for j in G[i]:
            #get weight
            weight = G[i][j]
            s= '      '+ i
            s +=  ' -> ' +  j + ' [dir=none,label="' + str(G[i][j]) + '",penwidth='+str(weight)+',color=black]'
            if s!='      '+ i:
                s+=';\n'
                f.writelines(s)

    f.writelines('}')
    f.close()

    #generate graph image from graph text file
    os.system("dot -Tjpg -omyImage.jpg dotgraph.txt")

main()

我之前一直在寻找一个有效的方法来构建复杂的图表,而这是我发现的最简单的方法(没有任何 Python 模块依赖)。
下面是使用 "dir=none" 的无向图结果:

enter image description here


如果您的系统上不存在 dot 二进制文件,您可以在终端中使用 sudo apt-get install graphviz 进行安装。 - A. STEFANI
@ Stefani 谢谢..!! 我的图是无向的,我该如何去掉方向。 - user1659936
@user1659936 您好,欢迎您。在构建过程中,您需要添加 dir=none,请将以下这行代码:s += ' -> ' + j + ' [label="' + str(G[i][j]) + '",penwidth='+str(weight)+',color=black]' 替换为 s += ' -> ' + j + ' [dir=none,label="' + str(G[i][j]) + '",penwidth='+str(weight)+',color=black]' 以去除方向。 - A. STEFANI
我已根据您的需求更改了代码。此致敬礼。 - A. STEFANI
通过上述代码,我的dotgraph.txt文件在Windows上已经生成(看起来)正确,但是我找不到.png输出文件。如何让它在Windows上工作而不仅限于Linux? - blacksite
你尝试过JPEG或GIF扩展名吗?(微软不太友好于免费文件格式...) - A. STEFANI

0

您应该编辑csv文件开头的行如下:

源节点 目标节点 类型 权重 23 89 无向 34.9 (即节点23和89之间有一条权重为34.9的边) 75 14 无向 28.5 等等....

之后,您可以将csv文件导入Gephi中,以表示图形,其中边的粗细代表权重,例如: 输入图像描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接