暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

python数据结构之图

646

在数学中,图是描述于一组对象的结构,其中某些对象对在某种意义上是“相关的”。这些对象对应于称为顶点的数学抽象(也称为节点或点),并且每个相关的顶点对都称为边(也称为链接或线)。通常,图形以图解形式描绘为顶点的一组点或环,并通过边的线或曲线连接。--百度百科

networkx是一个python包,用于创建、操作和研究复杂网络的结构、动态和功能。

图是一种比较复杂的数据结构,包括了一整套数学逻辑,实现起来过于复杂,还是直接用现成的networkx就好。

NetworkX提供:研究社会、生物和基础设施网络结构和动态的工具;一种适用于多种应用的标准编程接口和图形实现;为协作性、多学科项目提供快速发展环境;与现有的数值算法和C、C++和FORTRAN代码的接口;能够轻松处理大型非标准数据集。使用NetworkX,您可以以标准和非标准数据格式加载和存储网络,生成多种类型的随机和经典网络,分析网络结构,构建网络模型,设计新的网络算法,绘制网络,等等

要实现的图的边和节点示意如下,不过在实现的过程中均以无向图为主。

代码主体结构如下:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#                     _ooOoo_
#                   o8888888o
#                    88" . "88
#                 ( | -  _  - | )
#                     O\ = /O
#                 ____/`---'\____
#                  .' \\| |// `.
#                 / \\|||:|||// \
#               / _|||||-:- |||||- \
#                | | \\\ - /// | |
#              | \_| ''\---/'' | _/ |
#               \ .-\__ `-` ___/-. /
#            ___`. .' /--.--\ `. . __
#         ."" '< `.___\_<|>_/___.' >'"".
#       | | : `- \`.;`\  _ /`;.`/ - ` : | |
#          \ \ `-. \_ __\ /__ _/ .-` / /
#      ==`-.____`-.___\_____/___.-`____.-'==
#                     `=---='
'''
@Project :pythonalgorithms
@File :graphdatastructure.py
@Author :不胜人生一场醉
@Date :2021/7/16 22:18
'''


import networkx as nx
import matplotlib.pyplot as plt


if __name__ == "__main__":
    testSimpleGraph()
    testGraphaddfrom()
    testGraphlabelpic()
    testGraphpospic()
    testGraphfunc()
    testGraphAlgorithms()

1、实现一个简单的无向图,并绘制出来

def testSimpleGraph():
    g = nx.Graph()  # 创建空的无向图
    # g = nx.DiGraph()  # 创建空的有向图
    g.add_node(1)
    g.add_node(2)
    g.add_node(3)
    g.add_node(4)
    g.add_edge(12)
    g.add_edge(13)
    g.add_edge(23)
    g.add_edge(34)
    nx.draw_networkx(g)
    plt.show()

结果如下:

2、通过列表实现无向图,并绘制出来

def testGraphaddfrom():
    g = nx.Graph()  # 创建空的无向图
    g.add_nodes_from([1234567])
    g.add_edges_from([(12), (13), (14), (15), (16),
                      (23), (24), (26),
                      (36),
                      (45),
                      (56),
                      (67)])
    nx.draw_networkx(g)
    plt.show()

结果如下:

3、在图可视化中追加节点标签和边的标签

def testGraphlabelpic():
    # 数组,7个节点,13条边,有向图
    #   a   b   c   d   e   f   g
    # a     4   7   4   7   8
    # b         4   5       6
    # c         `           3
    # d                 2
    # e                     5
    # f                         8
    # g
    g = nx.Graph()
    g.add_nodes_from([
        (1, {"name""a"}),
        (2, {"name""b"}),
        (3, {"name""c"}),
        (4, {"name""d"}),
        (5, {"name""e"}),
        (6, {"name""f"}),
        (7, {"name""g"}),
    ])
    g.add_edges_from([(12, {'weight'4}),
                      (13, {'weight'7}),
                      (14, {'weight'4}),
                      (15, {'weight'7}),
                      (16, {'weight'8}),
                      (23, {'weight'4}),
                      (24, {'weight'5}),
                      (26, {'weight'6}),
                      (36, {'weight'3}),
                      (45, {'weight'2}),
                      (56, {'weight'5}),
                      (67, {'weight'8})
                      ])

    pos = nx.spring_layout(g)
    # 调用draw(G, pos)将基础的点边拓扑先画出来
    nx.draw(g, pos)
    node_labels = nx.get_node_attributes(g, 'name')
    # {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g'}
    # 调用draw_networkx_labels画节点标签
    nx.draw_networkx_labels(g, pos, labels=node_labels)
    edge_labels = nx.get_edge_attributes(g, 'weight')
    # {(1, 2): 4, (1, 3): 7, (1, 4): 4, (1, 5): 7, (1, 6): 8, (2, 3): 4, (2, 4): 5, (2, 6): 6, (3, 6): 3, (4, 5): 2, (5, 6): 5, (6, 7): 8}
    # 调用draw_networkx_edge_labels画和边的标签。
    nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels)
    plt.show()

结果如下:

4、在图可视化中继续追加节点的位置和边的权重

def testGraphpospic():
    # 数组,7个节点,13条边,有向图
    #   a   b   c   d   e   f   g
    # a     4   7   4   7   8
    # b         4   5       6
    # c         `           3
    # d                 2
    # e                     5
    # f                         8
    # g
    g = nx.Graph()
    g.add_nodes_from([
        (1, {"name""a""pos": (317), "degree"5"out_degree"5"in_degree"0}),
        (2, {"name""b""pos": (113), "degree"4"out_degree"3"in_degree"1}),
        (3, {"name""c""pos": (28), "degree"2"out_degree"1"in_degree"1}),
        (4, {"name""d""pos": (513), "degree"3"out_degree"1"in_degree"2}),
        (5, {"name""e""pos": (713), "degree"3"out_degree"1"in_degree"2}),
        (6, {"name""f""pos": (58), "degree"5"out_degree"1"in_degree"4}),
        (7, {"name""g""pos": (71), "degree"1"out_degree"0"in_degree"1})
    ])

    g.add_edges_from([(12, {'weight'4}),
                      (13, {'weight'7}),
                      (14, {'weight'4}),
                      (15, {'weight'7}),
                      (16, {'weight'8}),
                      (23, {'weight'4}),
                      (24, {'weight'5}),
                      (26, {'weight'6}),
                      (36, {'weight'3}),
                      (45, {'weight'2}),
                      (56, {'weight'5}),
                      (67, {'weight'8})
                      ])

    # 获取节点位置
    pos = nx.get_node_attributes(g, 'pos')
    # {1: (3, 17), 2: (1, 13), 3: (2, 8), 4: (5, 13), 5: (7, 13), 6: (5, 8), 7: (7, 1)}
    # 获取节点大小值=出入度*100
    nodesize = list(nx.get_node_attributes(g, 'degree').values())
    nodesize = [i * 100 for i in nodesize]
    # 调用draw(G, pos)将基础的点边拓扑先画出来
    nx.draw(g, pos, node_size=nodesize)
    # 获取节点标签名称
    node_labels = nx.get_node_attributes(g, 'name')
    # {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g'}
    # 调用draw_networkx_labels画节点标签
    nx.draw_networkx_labels(g, pos, labels=node_labels)
    # 获取边的标签名称
    edge_labels = nx.get_edge_attributes(g, 'weight')
    # {(1, 2): 4, (1, 3): 7, (1, 4): 4, (1, 5): 7, (1, 6): 8, (2, 3): 4, (2, 4): 5, (2, 6): 6, (3, 6): 3, (4, 5): 2, (5, 6): 5, (6, 7): 8}
    # 调用draw_networkx_edge_labels画和边的标签。
    nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels)
    # 获取边的权重,即对长度进行转换
    edgewidth = list(nx.get_edge_attributes(g, 'weight').values())
    nx.draw_networkx_edges(g, pos, width=edgewidth)
    plt.show()

6、测试networkx中关于日常操作的基本函数

def testGraphfunc():
    # 数组,7个节点,13条边,有向图
    #   a   b   c   d   e   f   g
    # a     4   7   4   7   8
    # b         4   5       6
    # c         `           3
    # d                 2
    # e                     5
    # f                         8
    # g
    # 判断两节点间是否有边    Graph.has_edge(u, v)
    # 网络中结点的个数  G.number_of_nodes()
    # 网络中边的个数   G.number_of_edges()
    # 获取结点i的邻居节点    G.neighbors(i)  或   G[i](在有向图中输出的是出度的邻居,G[i]会输出权重)
    # 获取所有节点的度  G.degree()
    # 获取节点i的属性  G.nodes[i]["wight"]
    # 查看图中所有点的信息    G.nodes.data()
    g = nx.Graph()
    g.add_nodes_from([
        (1, {"name""a""pos": (317), "degree"5"out_degree"5"in_degree"0"wight"5}),
        (2, {"name""b""pos": (113), "degree"4"out_degree"3"in_degree"1}),
        (3, {"name""c""pos": (28), "degree"2"out_degree"1"in_degree"1}),
        (4, {"name""d""pos": (513), "degree"3"out_degree"1"in_degree"2}),
        (5, {"name""e""pos": (713), "degree"3"out_degree"1"in_degree"2}),
        (6, {"name""f""pos": (58), "degree"5"out_degree"1"in_degree"4}),
        (7, {"name""g""pos": (71), "degree"1"out_degree"0"in_degree"1})
    ])

    g.add_edges_from([(12, {'weight'4}),
                      (13, {'weight'7}),
                      (14, {'weight'4}),
                      (15, {'weight'7}),
                      (16, {'weight'8}),
                      (23, {'weight'4}),
                      (24, {'weight'5}),
                      (26, {'weight'6}),
                      (36, {'weight'3}),
                      (45, {'weight'2}),
                      (56, {'weight'5}),
                      (67, {'weight'8})
                      ])
    # 判断两节点间是否有边    Graph.has_edge(u, v)
    print('节点2和节点4是否有边=', g.has_edge(24))  # True
    print('节点3和节点4是否有边=', g.has_edge(34))  # False
    # 网络中结点的个数  G.number_of_nodes()
    print('网络中结点的个数=', g.number_of_nodes())  # 7
    # 网络中边的个数   G.number_of_edges()
    print('网络中边的个数=', g.number_of_edges())  # 12
    # 获取结点i的邻居节点    G.neighbors(i)
    print('获取结点1的邻居节点=', g.neighbors(1))
    # 获取结点1的邻居节点= <dict_keyiterator object at 0x00000261558B2D10>
    print('获取结点1的邻居节点=', list(g.neighbors(1)))
    # 获取结点1的邻居节点= [23456]
    # 获取所有节点的度  G.degree()
    print('获取所有节点的度=', g.degree())
    # 获取所有节点的度= [(15), (24), (33), (43), (53), (65), (71)]
    # 获取节点i G.nodes[i]
    print('获取节点i=', g.nodes[1])
    # 获取节点i= {'name''a''pos': (317), 'degree'5'out_degree'5'in_degree'0'wight'5}
    # 获取节点i的属性名 G.nodes[i]
    print('获取节点i的属性名=', list(g.nodes[1]))
    # 获取节点i= ['name''pos''degree''out_degree''in_degree''wight']
    # 获取节点i的属性值 = 5
    # 获取节点i的属性值 G.nodes[i]["wight"]
    print('获取节点i的属性值=', g.nodes[1]["wight"])
    # 获取节点i的属性 = 5
    # 查看图中所有点的信息    G.nodes.data()
    print('查看图中所有点的信息=', g.nodes.data())

    # 查看图中所有点的信息= [
    # (1, {'name''a''pos': (317), 'degree'5'out_degree'5'in_degree'0'wight'5}),
    # (2, {'name''b''pos': (113), 'degree'4'out_degree'3'in_degree'1}),
    # (3, {'name''c''pos': (28), 'degree'2'out_degree'1'in_degree'1}),
    # (4, {'name''d''pos': (513), 'degree'3'out_degree'1'in_degree'2}),
    # (5, {'name''e''pos': (713), 'degree'3'out_degree'1'in_degree'2}),
    # (6, {'name''f''pos': (58), 'degree'5'out_degree'1'in_degree'4}),
    # (7, {'name''g''pos': (71), 'degree'1'out_degree'0'in_degree'1})]

结果如下:

节点2和节点4是否有边= True
节点3和节点4是否有边= False
网络中结点的个数= 7
网络中边的个数= 12
获取结点1的邻居节点= <dict_keyiterator object at 0x0000026FA0EC27C0>
获取结点1的邻居节点= [23456]
获取所有节点的度= [(15), (24), (33), (43), (53), (65), (71)]
获取节点i= {'name''a''pos': (317), 'degree'5'out_degree'5'in_degree'0'wight'5}
获取节点i的属性名= ['name''pos''degree''out_degree''in_degree''wight']
获取节点i的属性值= 5
查看图中所有点的信息= [(1, {'name''a''pos': (317), 'degree'5'out_degree'5'in_degree'0'wight'5}), (2, {'name''b''pos': (113), 'degree'4'out_degree'3'in_degree'1}), (3, {'name''c''pos': (28), 'degree'2'out_degree'1'in_degree'1}), (4, {'name''d''pos': (513), 'degree'3'out_degree'1'in_degree'2}), (5, {'name''e''pos': (713), 'degree'3'out_degree'1'in_degree'2}), (6, {'name''f''pos': (58), 'degree'5'out_degree'1'in_degree'4}), (7, {'name''g''pos': (71), 'degree'1'out_degree'0'in_degree'1})]
[(12, {'weight'4}), (14, {'weight'4}), (23, {'weight'4}), (36, {'weight'3}), (45, {'weight'2}), (67, {'weight'8})]
节点1-节点7最短路径= [167]


7、测试networkx中关于最短路径、连通性、各种遍历等算法功能

def testGraphAlgorithms():
    # 数组,7个节点,13条边,有向图
    #   a   b   c   d   e   f   g
    # a     4   7   4   7   8
    # b         4   5       6
    # c         `           3
    # d                 2
    # e                     5
    # f                         8
    # g
    g = nx.Graph()
    g.add_nodes_from([
        (1, {"name""a""pos": (317), "degree"5"out_degree"5"in_degree"0"wight"5}),
        (2, {"name""b""pos": (113), "degree"4"out_degree"3"in_degree"1}),
        (3, {"name""c""pos": (28), "degree"2"out_degree"1"in_degree"1}),
        (4, {"name""d""pos": (513), "degree"3"out_degree"1"in_degree"2}),
        (5, {"name""e""pos": (713), "degree"3"out_degree"1"in_degree"2}),
        (6, {"name""f""pos": (58), "degree"5"out_degree"1"in_degree"4}),
        (7, {"name""g""pos": (71), "degree"1"out_degree"0"in_degree"1})
    ])

    g.add_edges_from([(12, {'weight'4}),
                      (13, {'weight'7}),
                      (14, {'weight'4}),
                      (15, {'weight'7}),
                      (16, {'weight'8}),
                      (23, {'weight'4}),
                      (24, {'weight'5}),
                      (26, {'weight'6}),
                      (36, {'weight'3}),
                      (45, {'weight'2}),
                      (56, {'weight'5}),
                      (67, {'weight'8})
                      ])

    # 最小生成树
    tree = nx.minimum_spanning_tree(g, algorithm='prim')
    print(tree.edges(data=True))
    # [(1, 2, {'weight': 4}),
    # (1, 4, {'weight': 4}),
    # (2, 3, {'weight': 4}),
    # (3, 6, {'weight': 3}),
    # (4, 5, {'weight': 2}),
    # (6, 7, {'weight': 8})]

    # 最短路径
    print("节点1-节点7最短路径=",nx.dijkstra_path(g, 17))
    # [1, 6, 7]
    # 所有节点之间的最短路径
    gen = nx.all_pairs_shortest_path(g)
    print("所有节点之间的最短路径=",dict(gen))
    # {
    # 1: {1: [1], 2: [1, 2], 3: [1, 3], 4: [1, 4], 5: [1, 5], 6: [1, 6], 7: [1, 6, 7]},
    # 2: {2: [2], 1: [2, 1], 3: [2, 3], 4: [2, 4], 6: [2, 6], 5: [2, 1, 5], 7: [2, 6, 7]},
    # 3: {3: [3], 1: [3, 1], 2: [3, 2], 6: [3, 6], 4: [3, 1, 4], 5: [3, 1, 5], 7: [3, 6, 7]},
    # 4: {4: [4], 1: [4, 1], 2: [4, 2], 5: [4, 5], 3: [4, 1, 3], 6: [4, 1, 6], 7: [4, 1, 6, 7]},
    # 5: {5: [5], 1: [5, 1], 4: [5, 4], 6: [5, 6], 2: [5, 1, 2], 3: [5, 1, 3], 7: [5, 6, 7]},
    # 6: {6: [6], 1: [6, 1], 2: [6, 2], 3: [6, 3], 5: [6, 5], 7: [6, 7], 4: [6, 1, 4]},
    # 7: {7: [7], 6: [7, 6], 1: [7, 6, 1], 2: [7, 6, 2], 3: [7, 6, 3], 5: [7, 6, 5], 4: [7, 6, 1, 4]}}

    print("各点之间可达性=",nx.communicability(g))
    # 各点之间可达性= {
    # 1: {1: 11.66037697759954, 2: 9.730688193140374, 3: 8.180239781697592, 4: 7.645194148638486, 5: 7.720951055876168, 6: 10.188202427433492, 7: 2.4556767248680185},
    # 2: {1: 9.730688193140374, 2: 8.966431076518731, 3: 7.2767859598006615, 4: 6.497877866101427, 5: 6.132652147982442, 6: 8.800266284965774, 7: 2.136327166832645},
    # 3: {1: 8.180239781697592, 2: 7.2767859598006615, 3: 6.750798019391801, 4: 4.922928096425332, 5: 4.985335865445396, 6: 7.643419657110174, 7: 1.9072819535912968},
    # 4: {1: 7.645194148638486, 2: 6.497877866101427, 3: 4.922928096425332, 4: 5.962359580374006, 5: 5.4295610915115375, 6: 6.2992895922037295, 7: 1.3858918410969554},
    # 5: {1: 7.720951055876168, 2: 6.132652147982442, 3: 4.985335865445396, 4: 5.4295610915115375, 5: 6.053240117357615, 6: 6.892984331374484, 7: 1.7072857005030746},
    # 6: {1: 10.188202427433492, 2: 8.800266284965774, 3: 7.643419657110174, 4: 6.2992895922037295, 5: 6.892984331374484, 6: 10.11197286752748, 7: 3.0514157654095175},
    # 7: {1: 2.4556767248680185, 2: 2.136327166832645, 3: 1.9072819535912968, 4: 1.3858918410969554, 5: 1.7072857005030746, 6: 3.0514157654095175, 7: 1.9054013217324557}}


    print("获得图中非连通点的列表=",list(nx.isolates(g)))
    # 获得图中非连通点的列表 = []
    g.add_node(8)
    print("获得图中非连通点的列表="list(nx.isolates(g)))
    # 获得图中非连通点的列表= [8]

    # 遍历
    d_gen = nx.dfs_edges(g, 1)  # 按边深度搜索, 1为起点
    b_gen = nx.bfs_edges(g, 1)
    print("深度搜索:",list(d_gen))
    # [(1, 2), (2, 3), (3, 6), (6, 5), (5, 4), (6, 7)]
    print("广度搜索:",list(b_gen))
    # [(1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (6, 7)]
    print(nx.dfs_tree(g, 1).nodes())  # 按点深搜g
    # [1, 2, 3, 6, 5, 4, 7]
    print("先序遍历:"list(nx.dfs_preorder_nodes(g, 1)))
    # 先序遍历: [1, 2, 3, 6, 5, 4, 7]
    print("后序遍历:"list(nx.dfs_postorder_nodes(g, 1)))
    # 后序遍历: [4, 5, 7, 6, 3, 2, 1]
    print("节点1和节点2的共同邻居:"list(nx.common_neighbors(g, 12)))
    # 节点0和节点33的共同邻居: [3, 4, 6]
    print("节点1和节点7的共同邻居:"list(nx.common_neighbors(g, 17)))
    # 节点0和节点33的共同邻居: [6]

结果如下:


所有节点之间的最短路径= {1: {1: [1], 2: [12], 3: [13], 4: [14], 5: [15], 6: [16], 7: [167]}, 2: {2: [2], 1: [21], 3: [23], 4: [24], 6: [26], 5: [215], 7: [267]}, 3: {3: [3], 1: [31], 2: [32], 6: [36], 4: [314], 5: [315], 7: [367]}, 4: {4: [4], 1: [41], 2: [42], 5: [45], 3: [413], 6: [416], 7: [4167]}, 5: {5: [5], 1: [51], 4: [54], 6: [56], 2: [512], 3: [513], 7: [567]}, 6: {6: [6], 1: [61], 2: [62], 3: [63], 5: [65], 7: [67], 4: [614]}, 7: {7: [7], 6: [76], 1: [761], 2: [762], 3: [763], 5: [765], 4: [7614]}}
各点之间可达性= {1: {111.6603769775995429.73068819314037438.18023978169759247.64519414863848657.720951055876168610.18820242743349272.4556767248680185}, 2: {19.73068819314037428.96643107651873137.276785959800661546.49787786610142756.13265214798244268.80026628496577472.136327166832645}, 3: {18.18023978169759227.276785959800661536.75079801939180144.92292809642533254.98533586544539667.64341965711017471.9072819535912968}, 4: {17.64519414863848626.49787786610142734.92292809642533245.96235958037400655.429561091511537566.299289592203729571.3858918410969554}, 5: {17.72095105587616826.13265214798244234.98533586544539645.429561091511537556.05324011735761566.89298433137448471.7072857005030746}, 6: {110.18820242743349228.80026628496577437.64341965711017446.299289592203729556.892984331374484610.1119728675274873.0514157654095175}, 7: {12.455676724868018522.13632716683264531.907281953591296841.385891841096955451.707285700503074663.051415765409517571.9054013217324557}}
获得图中非连通点的列表= []
获得图中非连通点的列表= [8]
深度搜索: [(12), (23), (36), (65), (54), (67)]
广度搜索: [(12), (13), (14), (15), (16), (67)]
[1236547]
先序遍历: [1236547]
后序遍历: [4576321]
节点1和节点2的共同邻居: [346]
节点1和节点7的共同邻居: [6]



文章转载自python与大数据分析,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论