news 2026/9/21 16:14:42

NetworkX 图论函数接口全解:从 Graph 到 Nodes、Edges、Attributes 与 freeze 的官方 Functions API 实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NetworkX 图论函数接口全解:从 Graph 到 Nodes、Edges、Attributes 与 freeze 的官方 Functions API 实战指南

NetworkX 图论函数接口全解:从 Graph 到 Nodes、Edges、Attributes 与 freeze 的官方 Functions API 实战指南

【免费下载链接】networkxNetwork Analysis in Python项目地址: https://gitcode.com/gh_mirrors/ne/networkx

导读

本文围绕 NetworkX 官方参考文档 doc/reference/functions.rst 所定义的Functions(函数接口)模块展开,系统讲解networkx.classes.function中面向图对象的一整套函数式 API——包括 Graph 级操作(密度、子图、方向转换)、Nodes/Edges 视图查询、Self loops(自环)处理、节点与边属性读写、路径校验与权重求和,以及图的冻结(freeze)机制。读者学完后,将能用函数式调用替代冗长的对象方法调用,理解每个函数的参数语义与返回值形态,并掌握其底层实现原理(本文所有代码示例均可直接复制运行,全部行为可由 networkx/classes/function.py 及其测试 networkx/classes/tests/test_function.py 验证)。

一、函数式接口:把图方法包装成独立函数

NetworkX 的核心数据结构是GraphDiGraphMultiGraphMultiDiGraph四个类(见 networkx/classes/init.py),它们各自带有大量方法。而networkx.classes.function模块(约 1600 行,见 networkx/classes/function.py)提供的是与图类型解耦的独立函数:每个函数接收图对象G作为第一个参数,内部大多直接委托给对应的方法或视图属性。

例如nodes(G)的源码只是:

def nodes(G): """Returns a NodeView over the graph nodes. This function wraps the :func:`G.nodes <networkx.Graph.nodes>` property. """ return G.nodes()

edges(G, nbunch=None)degree(G, nbunch=None, weight=None)neighbors(G, n)等也遵循同样的"薄包装"模式。这种设计带来两个好处:一是算法代码可以统一以nx.xxx(G)的形式调用,无需关心G究竟是Graph还是DiGraph;二是为后端分发(backends dispatch)提供了统一入口(如set_node_attributesget_edge_attributes等函数标注了@nx._dispatchable装饰器)。

从模块的__all__声明(networkx/classes/function.py)可以看出,该模块除了文档列出的函数外,还额外导出了remove_node_attributesremove_edge_attributesdescribe等工具。官方文档将全部函数按职责划分为Graph / Nodes / Edges / Self loops / Attributes / Paths / Freezing graph structure七组,下文逐组展开。

二、Graph 组:图级别的结构查询与构造

文档中 Graph 组包含 15 个函数,覆盖图的整体度量、方向、空图判断、子图与结构追加。

2.1 图的整体度量:density、degree_histogram、is_empty

  • density(G):返回图的密度。无向图公式为d = 2m / (n(n-1)),有向图为d = m / (n(n-1)),其中n为节点数、m为边数。源码(networkx/classes/function.py)在m == 0 or n <= 1时直接返回 0,否则计算后对无向图乘 2。密度为 0 表示无边图,为 1 表示完全图;多重图或含自环的图密度可以大于 1(自环计入总边数)。
>>> G = nx.path_graph(4) >>> nx.density(G) 0.5
  • degree_histogram(G):返回"稠密"度频分布列表,下标即度值,元素为具有该度的节点数,缺失的度值补 0(实现基于collections.Counter,见 networkx/classes/function.py)。注意列表长度可达number_of_edges量级。
>>> G = nx.star_graph(5) >>> nx.degree_histogram(G) [0, 5, 0, 0, 0, 1] # 度0:0个, 度1:5个, ..., 度5:1个 >>> dict(enumerate(nx.degree_histogram(G))) {0: 0, 1: 5, 2: 0, 3: 0, 4: 0, 5: 1}

若只要稀疏表示,可直接用Counter(d for _, d in G.degree)

  • is_empty(G):返回True当且仅当图中没有边(可以有孤立节点)。实现是not any(G._adj.values()),时间复杂度 O(n)(networkx/classes/function.py)。零节点的空图被称为 null graph。

2.2 方向:is_directed、to_directed、to_undirected

  • is_directed(G):等价于G.is_directed()
  • to_directed(graph)/to_undirected(graph):返回图的**视图(view)**而非副本——内部固定调用graph.to_directed(as_view=True)/graph.to_undirected(as_view=True)(networkx/classes/function.py)。这与G.to_directed()默认as_view=False的行为不同:用函数式 API 得到的视图与原图共享数据,对原图的修改会反映到视图中。
>>> G = nx.Graph([(0, 1), (1, 2)]) >>> D = nx.to_directed(G) >>> D.is_directed() True >>> nx.to_undirected(D).is_directed() False
  • create_empty_copy(G, with_data=True):返回一个同类型(G.__class__())但所有边被移除的副本;with_data=True(默认)时,节点数据与图级G.graph数据会被保留(networkx/classes/function.py)。

2.3 结构追加:add_star、add_path、add_cycle

三个函数都接受"容器 + 关键字属性"的方式向现有图G_to_add_to批量添加结构,**attr会作为每条新边的属性(如weight=2):

  • add_star(G, nodes_for_star, **attr)nodes_for_star第一个节点作为中心,与其余所有节点相连。
  • add_path(G, nodes_for_path, **attr):按顺序把节点连成一条路径(每对相邻节点一条边)。
  • add_cycle(G, nodes_for_cycle, **attr):在路径基础上首尾相连形成环。

三者底层都用pairwise迭代相邻节点(add_cycle使用cyclic=True),空容器时静默返回(见 networkx/classes/function.py)。注意中心/首节点的选择是确定性的:star 的中心是容器第一个元素,因此add_star(G, [0,1,2,3])产生边(0,1),(0,2),(0,3);测试 networkx/classes/tests/test_function.py 对空列表、单元素、迭代器等多种边界情况都有覆盖。

2.4 子图视图:subgraph、induced_subgraph、restricted_view、edge_subgraph

这是 Graph 组中最核心的一族函数。它们返回的都是只读视图(SubGraph View),不复制数据,对原图G的修改会实时反映到视图中;如需可变的独立副本,用subgraph.copy()Graph(subgraph)

  • subgraph(G, nbunch):等价于G.subgraph(nbunch),返回由nbunch中节点诱导的子图视图,不在图中的节点被静默忽略(networkx/classes/function.py)。
  • induced_subgraph(G, nbunch):返回节点诱导子图视图——节点集为nbunch,边为两端都落在nbunch中的原图边。实现上通过nx.filters.show_nodes构造节点过滤器后调用nx.subgraph_view(networkx/classes/function.py)。
>>> G = nx.path_graph(4) >>> H = nx.induced_subgraph(G, [0, 1, 3]) >>> list(H.edges) [(0, 1)] # 边 (1,2)/(2,3) 因端点不在节点集内被过滤
  • edge_subgraph(G, edges):返回边诱导子图视图——包含edges中的边及所有关联端点。不存在的边被忽略。多重图必须用三元组(u, v, key)指定边,并可用边属性做条件过滤(networkx/classes/function.py):
>>> G = nx.path_graph(5) >>> H = nx.edge_subgraph(G, [(0, 1), (3, 4)]) >>> list(H.nodes) [0, 1, 3, 4]
  • restricted_view(G, nodes, edges):与"显示"相反,它隐藏指定的节点和边,被隐藏的节点连带过滤其所有关联边(networkx/classes/function.py):
>>> G = nx.path_graph(5) >>> H = nx.restricted_view(G, [0], [(1, 2), (3, 4)]) >>> list(H.nodes) [1, 2, 3, 4] >>> list(H.edges) [(2, 3)]

这族函数依赖 networkx/classes/filters.py 中的过滤器工厂(show_nodeshide_edgesshow_multiedges等),最终统一交给 networkx/classes/graphviews.py 的subgraph_view构建视图。性能提示:源码文档明确指出,递归地"子图的子图"形成的视图链在约 15 层后会明显变慢,建议始终基于原图构造视图;G.subgraphG本身是子图时会自动"短路"回原图,而函数版本允许你自行选择是否叠链。

三、Nodes 组:节点集合、度数统计与邻居查询

  • nodes(G):返回NodeView(等价于G.nodes,networkx/classes/reportviews.py 定义了该视图类型),支持len()、成员判断与迭代。
  • number_of_nodes(G):节点总数,等价于len(G)
  • neighbors(G, n):返回节点n的邻居迭代器。对无向图是全部邻接节点;对DiGraph只返回后继(successors)
  • all_neighbors(graph, node):在neighbors基础上,对有向图额外合并前驱(predecessors)。前驱与后继可能产生重复元素(如双向边或自环):
>>> DG = nx.DiGraph([(0, 1), (1, 2), (2, 1)]) >>> list(nx.all_neighbors(DG, 1)) [0, 2, 2]
  • non_neighbors(graph, node):返回图中不是该节点邻居的所有节点集合(含自身排除),实现为集合差运算graph._adj.keys() - graph._adj[node].keys() - {node}(networkx/classes/function.py)。
  • common_neighbors(G, u, v):返回两个节点共同的邻居集合,仅适用于无向图(装饰器@not_implemented_for("directed")会拒绝有向图),且uv不在图中时抛出NetworkXError(networkx/classes/function.py):
>>> G = nx.complete_graph(5) >>> sorted(nx.common_neighbors(G, 0, 1)) [2, 3, 4]

四、Edges 组:边集合、边数与补边枚举

  • edges(G, nbunch=None):返回EdgeView(对DiGraph即出边视图out_edges)。nbunch给定时只返回与这些节点关联的边。在报告视图类EdgeView/OutEdgeView(networkx/classes/reportviews.py)基础上,视图还支持data=True携带属性字典。
  • number_of_edges(G):边总数。
  • non_edges(graph):生成器,逐个产出图中不存在的边。有向图按(u, v)逐个节点对检查;无向图用"弹出节点 + 集合差"避免重复(networkx/classes/function.py)。对多重图(MultiGraph)无效——多重图同一节点对可有多条平行边,non_edges的语义不再成立。

五、Self loops 组:自环边的三个视角

自环(self-loop)指两端为同一节点的边(如(1, 1))。文档提供三个互补函数:

  • selfloop_edges(G, data=False, keys=False, default=None):迭代器,遍历所有自环边。data控制返回形态:False返回二元组(u, v)True返回三元组(u, v, datadict);传入字符串属性名则返回(u, v, datavalue)(缺失属性用default填充)。keys=True时对多重图额外带上边键k(networkx/classes/function.py):
>>> G = nx.MultiGraph() >>> G.add_edge(1, 1); G.add_edge(1, 2) >>> list(nx.selfloop_edges(G)) [(1, 1)] >>> list(nx.selfloop_edges(G, keys=True, data=True)) [(1, 1, 0, {})]
  • number_of_selfloops(G):自环边数量,实现为sum(1 for _ in nx.selfloop_edges(G))
  • nodes_with_selfloops(G):迭代器,产出至少拥有一条自环的节点(对多重图每节点只出现一次):
>>> G = nx.Graph() >>> G.add_edge(1, 1); G.add_edge(1, 2) >>> list(nx.nodes_with_selfloops(G)) [1]

自环会同时影响图的密度计算(见 2.1 节的说明),这是分析含自环网络时需要留意的细节。

六、Attributes 组:节点与边的属性读写

这一组是实际工程中使用最频繁的函数,文档单独强调了一个历史兼容性警告:v1.x 与 v2.x 之间valuesname两个参数的顺序互换过,写新代码请严格按当前签名调用。

6.1 设置属性:set_node_attributes / set_edge_attributes

set_node_attributes(G, values, name=None)的参数语义有三种形态(networkx/classes/function.py):

  1. 标量 + name:给所有节点设置同一属性值。
  2. 字典 + name{node: value},按节点赋值;不在图中的节点静默忽略。
  3. 字典的字典(不传 name){node: {attr: value, ...}},整体更新各节点属性。
>>> G = nx.path_graph(3) >>> bb = nx.betweenness_centrality(G) >>> nx.set_node_attributes(G, bb, "betweenness") >>> G.nodes[1]["betweenness"] 1.0

可变对象陷阱:如果values传的是列表等可变对象,所有节点会共享同一个引用,对列表的后续修改会"传染"到所有节点(源码文档明确给出了此例)。同理,set_edge_attributes(G, values, name=None)支持标量、{(u, v): value}{(u, v): {attr: value}}三种形态;多重图的字典键必须是三元组(u, v, key)(networkx/classes/function.py):

>>> MG = nx.MultiGraph() >>> MG.add_edges_from([(0, 1), (0, 1)]) # 返回边键列表 [0, 1] >>> nx.set_edge_attributes(MG, {(0, 1, 0): {"cost": 21}, (0, 1, 1): {"cost": 7}}) >>> MG[0][1][0]["cost"] 21

两个 setter 都标注了@nx._dispatchable(..., mutates_input=True)并会在修改后调用nx._clear_cache(G)使缓存失效。

6.2 读取属性:get_node_attributes / get_edge_attributes

  • get_node_attributes(G, name, default=None):返回{node: 属性值}字典。default给定时,缺失属性的节点以默认值入字典;default=None(默认)时,缺失属性的节点不出现在结果中(networkx/classes/function.py)。
  • get_edge_attributes(G, name, default=None):同样语义,但返回字典的键:普通(有向)图为二元组(u, v),多重图为三元组(u, v, key)(networkx/classes/function.py)。
>>> G = nx.Graph() >>> nx.add_path(G, [1, 2, 3], color="red") >>> nx.get_edge_attributes(G, "color") {(1, 2): 'red', (2, 3): 'red'}

6.3 权重探测:is_weighted / is_negatively_weighted

  • is_weighted(G, edge=None, weight="weight"):默认检查图中每条边是否都带有weight属性,全带返回True;空图返回False(规避all([]) == True的陷阱)。指定edge时只检查该边,边不存在则抛NetworkXError(networkx/classes/function.py)。
  • is_negatively_weighted(G, edge=None, weight="weight"):检查是否存在至少一条权重为负的边(用any(...)实现,networkx/classes/function.py)。在选最短路径算法前用它预检负权边,可以避免误用 Dijkstra。
>>> G = nx.path_graph(4) >>> nx.is_weighted(G) False >>> G = nx.DiGraph(); G.add_edge(1, 2, weight=1) >>> nx.is_weighted(G) True

七、Paths 组:路径合法性与路径权重

  • is_path(G, path):判断节点列表path是否构成一条有效路径——要求每个节点都存在且任意相邻节点对在图中相邻("connected via one or more edges",因此多重图同样适用)。实现利用pairwise逐一检查邻接关系,并捕获KeyError/TypeError返回False(networkx/classes/function.py)。
  • path_weight(G, path, weight):返回沿path按指定边属性weight累加的总代价。先校验路径合法性(不合法抛nx.NetworkXNoPath);对多重图取同一节点对多条平行边中的最小权重参与累加(min(v[weight] for v in G._adj[node][nbr].values())),普通图直接读取该边权重(networkx/classes/function.py)。
>>> G = nx.path_graph(4) >>> G[0][1]["weight"] = 2; G[1][2]["weight"] = 3; G[2][3]["weight"] = 4 >>> nx.path_weight(G, [0, 1, 2, 3], "weight") 9

八、Freezing graph structure:图的冻结机制

文档最后一组是freezeis_frozen,用于防止图的结构被意外修改——这在把图对象作为函数参数传递、防止下游代码误改数据时非常有用。

  • freeze(G):将G的所有结构修改方法(add_nodeadd_nodes_fromremove_nodeadd_edgeadd_edges_fromadd_weighted_edges_fromremove_edgeremove_edges_fromclearclear_edges等)替换为抛错函数frozen,并置G.frozen = True,最后返回G(networkx/classes/function.py):
>>> G = nx.path_graph(4) >>> G = nx.freeze(G) >>> try: ... G.add_edge(4, 5) ... except nx.NetworkXError as err: ... print(str(err)) Frozen graph can't be modified
  • is_frozen(G):通过探测G.frozen属性判断是否冻结;属性不存在(如自定义图类)时返回False
  • 关键限制:冻结只拦截"结构"修改,节点与边的属性数据仍可修改G.nodes[0]["x"] = 1合法)。若需"解冻",必须通过构造新对象复制(源码文档明确给出的标准做法):
>>> unfrozen_graph = nx.Graph(frozen_graph) >>> nx.is_frozen(unfrozen_graph) False

九、源码结构、测试覆盖与进一步探索

  • 实现主体:全部函数集中在 networkx/classes/function.py(__all__见该文件 第 9-50 行),并通过 networkx/classes/init.py 的from .function import *暴露为nx.xxx
  • 配套视图类:节点、边、度三种视图定义于 networkx/classes/reportviews.py(NodeView见 第 226 行、DegreeView见 第 586 行、EdgeView见 第 1297 行);子图视图机制见 networkx/classes/graphviews.py 的subgraph_view
  • 测试验证:完整测试套件位于 networkx/classes/tests/test_function.py,覆盖空图边界(test_degree_histogram_empty)、与对象方法的一致性断言(如test_edgestest_degree)、add_star/add_path/add_cycle的多种输入形态(空列表、单元素、生成器)以及describe信息字典等。运行测试:
pytest networkx/classes/tests/test_function.py
  • describe(G, describe_hook=None):模块还附带一个未列入七组文档的实用函数,一行打印图的概览(节点数、边数、有向/多重标志、树/二分图判定、平均度、连通分量数、密度),并可通过describe_hook注入自定义统计项(networkx/classes/function.py),是快速探查陌生图数据的便捷入口。

结语

networkx.classes.function是 NetworkX 中最贴近日常使用的函数层:它把图对象的方法、视图与属性操作统一成nx.xxx(G, ...)的函数式调用,七组函数分别覆盖图度量与构造、节点/边/自环查询、属性读写、路径计算与冻结保护。本文给出的全部示例都来自官方文档 docstring 与仓库测试的交叉验证,可直接在本地 NetworkX 环境中运行复现。建议读者结合 doc/reference/functions.rst 的 API 索引,按需深入阅读 networkx/classes/function.py 中每个函数的完整 docstring,以获得最精确的参数行为说明。

【免费下载链接】networkxNetwork Analysis in Python项目地址: https://gitcode.com/gh_mirrors/ne/networkx

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 16:14:03

单文件Bash实现配环境Agent:探测-计划-执行-验证

如果你管过哪怕一台开发机&#xff0c;大概都经历过这种时刻&#xff1a;照着文档敲完几十条命令&#xff0c;以为环境终于好了&#xff0c;结果node -v能跑、npm install报错&#xff1b;PyCharm 里解释器怎么都选不对&#xff1b;git push 提示找不到 ssh key。问题不是“命令…

作者头像 李华
网站建设 2026/9/21 16:07:45

PowerPMAC上位机开发实战:用C#构建Winform运动控制界面

去年接手一个三轴检测设备的上位机项目&#xff0c;厂家只留了一台装着 PowerPMAC 调试软件的工控机。操作员每天开工要盯着命令行窗口&#xff0c;敲一堆类似#1j/#2j/的指令做回零和点动&#xff0c;稍微按错一个符号&#xff0c;轴就停在半路。于是"做一个能给人用的 Wi…

作者头像 李华