在开发过程中,nx的节点是我自己定义的字典,由于业务需求,我需要将其抽象成一个对象,下面来讲讲我的具体操作流程。
1.创建dataclasses类
`dataclasses` 是 Python 中的一个模块,用于简化创建不可变数据类的过程,自动添加特殊方法(如 `__init__`、`__repr__`、`__eq__` 等),减少样板代码,使数据类的定义更加简洁。由于我建立的图网络不需要经常更改数据,所以使用它简化我的开发流程是再好不过了:
代码语言:python代码运行次数:0复制import dataclasses
@dataclasses.dataclass
class Node:
perma_id: int
value: int
color: str
同时为了使其散列化,重写Node类的__hash__方法和__eq__方法:
__hash__方法将perma_id作为node对象的散列值,由于是perma_id,而且python会动态增加散列表的长度所以基本不会发生散列冲突,__eq__函数将两个对象是否相同的依据改为它们的perma_id是否相同,因为两个对象如果逻辑上相同,那么它们的哈希值一定相同。
代码语言:python代码运行次数:0复制 def __hash__(self):
return self.perma_id
def __eq__(self, other):
if isinstance(other, Node):
return self.perma_id == other.perma_id
return False
现在我们试着实例化一个node对象:
代码语言:python代码运行次数:0复制node = Node(1, 2, 'red')
output:
代码语言:python代码运行次数:0复制Node(perma_id=1, value=2, color='red')
现在我们尝试多加几个点,并将它们放在一张无向图里面,然后输出:
代码语言:python代码运行次数:0复制import networkx as nx
node1 = Node(1, 18, 'red')
node2 = Node(2, 24, 'blue')
node3 = Node(3, 31, 'green')
node4 = Node(4, 4, 'yellow')
node5 = Node(5, 53, 'purple')
graph = nx.Graph()
graph.add_edge(node1, node2)
graph.add_edge(node1, node3)
graph.add_edge(node1, node4)
graph.add_edge(node1, node5)
print(graph)
for n, nbrs in graph.adj.items():
for nbr, eattr in nbrs.items():
print(n, nbr)
output:
代码语言:python代码运行次数:0复制Graph with 5 nodes and 4 edges
Node(perma_id=1, value=18, color='red') Node(perma_id=2, value=24, color='blue')
Node(perma_id=1, value=18, color='red') Node(perma_id=3, value=31, color='green')
Node(perma_id=1, value=18, color='red') Node(perma_id=4, value=4, color='yellow')
Node(perma_id=1, value=18, color='red') Node(perma_id=5, value=53, color='purple')
Node(perma_id=2, value=24, color='blue') Node(perma_id=1, value=18, color='red')
Node(perma_id=3, value=31, color='green') Node(perma_id=1, value=18, color='red')
Node(perma_id=4, value=4, color='yellow') Node(perma_id=1, value=18, color='red')
2.查询对象信息
此时一切美好,但是如果我们想使用perma_id查询一个节点的具体信息呢,我们该怎么做?
这个时候我有两种解决方法:
1.在创建节点时使用一个字典将perma_id和节点对象关联起来,查询信息时就直接将perma_id映射到节点对象,然后再去查询,字典查询的复杂度永远为O(1),但是会有额外的字典存储的空间开销。
如我想查询perma_id为1的节点的相关边的信息我可以这样写代码:
代码语言:python代码运行次数:0复制node_list = [node1, node2, node3, node4, node5]
node_map = {node.perma_id: node for node in node_list}
edges = graph.edges(node_map[1])
for edge in edges:
print(edge)
output:
代码语言:python代码运行次数:0复制(Node(perma_id=1, value=18, color='red'), Node(perma_id=2, value=24, color='blue'))
(Node(perma_id=1, value=18, color='red'), Node(perma_id=3, value=31, color='green'))
(Node(perma_id=1, value=18, color='red'), Node(perma_id=4, value=4, color='yellow'))
(Node(perma_id=1, value=18, color='red'), Node(perma_id=5, value=53, color='purple'))
2.我们也可以使用python自带的filter方法:
代码语言:python代码运行次数:0复制def filter_nodes(edge,perma_id):
return edge[0].perma_id == perma_id or edge[1].perma_id == perma_id
edges = graph.edges()
edges = list(filter(lambda edge: filter_nodes(edge, 1), edges))
for edge in edges:
print(edge)
output:
代码语言:python代码运行次数:0复制(Node(perma_id=1, value=18, color='red'), Node(perma_id=2, value=24, color='blue'))
(Node(perma_id=1, value=18, color='red'), Node(perma_id=3, value=31, color='green'))
(Node(perma_id=1, value=18, color='red'), Node(perma_id=4, value=4, color='yellow'))
(Node(perma_id=1, value=18, color='red'), Node(perma_id=5, value=53, color='purple'))
由于fiter方法需要遍历整个可迭代对象,所以在大规模数据场景下,使用filter会带来额外的查询时间开销,所以方法的选择还是要看具体的应用场景,我选择了使用字典映射的方法,因为我的node节点具体业务中也才不过几千个而已。
同时,如果使用的是字典类型的数据,也可以使用映射或者filter的方法去获取字典的详细数据,也可以将字典映射存储到数据库中,或者将节点和边存储到数据库中,而不是存储整个图结构。也可以使用专门的图数据库进行复杂网络的研究,但是它们往往在个人开发中的显得比较臃肿,小型项目里面又显得成本比较昂贵,所以nx不失为一个优雅的选择。
当然,各位看官大大们如果有更好的方法也欢迎交流学习。
我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表