news 2026/9/21 7:18:44

NetworkX cd_index 时间依赖算法完全指南:度量专利引用网络中的技术变革动态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NetworkX cd_index 时间依赖算法完全指南:度量专利引用网络中的技术变革动态

NetworkX cd_index 时间依赖算法完全指南:度量专利引用网络中的技术变革动态

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

本指南深入讲解 NetworkX 提供的时间依赖(time dependent)算法——cd_index(CD 指数),它是衡量专利引用网络等时序有向网络中"技术整合/失稳"动态的核心指标。你将掌握该函数的参数语义、完整可运行示例、底层数学公式与源码实现细节,以及时间类型兼容性、闰年陷阱等实战注意事项,可直接用于专利分析、文献计量与演化网络研究。

CD index 是什么:从专利引用网络说起

CD index(Consolidating-Destabilizing index)由 Funk 与 Owen-Smith 于 2017 年提出(A dynamic network measure of technological change, Management Science 63(3): 791–817),用于回答一个经典问题:一项新技术(专利)的出现,是让既有技术领域趋于"整合",还是走向"失稳"?

在 NetworkX 中,该算法以节点表示专利、有向边表示引用关系,通过观察聚焦专利(focal patent)的前驱(引用者)是否同时引用聚焦专利的后继(被聚焦专利引用的专利),来刻画该专利在知识网络中的位置动态。整个算法被封装在 time_dependent.py 模块中,并通过 time_dependent.rst 收录于官方 API 参考文档,同时在 算法索引 中注册。

使用前提:图类型与节点属性要求

cd_index对输入图有严格约束,全部体现在函数顶部的装饰器中(源码 L9-L11):

@not_implemented_for("undirected") @not_implemented_for("multigraph") @nx._dispatchable(node_attrs={"time": None, "weight": 1}) def cd_index(G, node, time_delta, *, time="time", weight=None):
  • 必须是有向图(DiGraph):无向图会抛出NetworkXNotImplemented,因为"引用/前驱/后继"语义依赖方向;
  • 不能是多重图(MultiGraph):同样抛出NetworkXNotImplemented
  • 每个节点都必须携带time属性:否则抛出NetworkXError("Not all nodes have a 'time' attribute.")(见 源码 L113-L114);
  • 节点可选携带weight属性用于加权计算,缺失权重的节点按 1 处理。

@nx._dispatchable(node_attrs={"time": None, "weight": 1})是 NetworkX 的后端(backend)调度装饰器,声明time为节点属性、weight默认值为 1,允许第三方后端(如 graphblas 等)接管计算。

cd_index 函数签名与参数详解

函数签名(源码 L12):

cd_index(G, node, time_delta, *, time="time", weight=None)

各参数语义如下表:

参数类型默认值说明
G有向 NetworkX 图必填节点需含time属性,可含weight属性
node节点必填计算 CD index 的目标(聚焦)节点
time_deltanumeric 或timedelta必填以目标节点time为起点的向后时间窗口;必须能与time属性进行加法和比较运算
timestr"time"用作时间计算的节点属性名,可替换为自定义属性名
weightstrNone用作权重的节点属性名;为None时所有前驱权重视为 1

返回值是float,即目标节点的 CD index。值得注意的是,time_delta被加在目标节点的时间上构成一个"截止时间"(target date),只有time属性不大于该截止时间的前驱才会参与计算——这与许多固定窗口算法不同,窗口是"目标节点时间之后向后延伸time_delta"。

快速上手:完整可运行示例

使用 datetime 与 timedelta

以下示例直接取自函数 docstring(源码 L52-L70),构建了一个 5 个节点的引用网络,计算节点 3 的 CD index:

from datetime import datetime, timedelta import networkx as nx G = nx.DiGraph() nodes = { 1: {"time": datetime(2015, 1, 1)}, 2: {"time": datetime(2012, 1, 1), "weight": 4}, 3: {"time": datetime(2010, 1, 1)}, 4: {"time": datetime(2008, 1, 1)}, 5: {"time": datetime(2014, 1, 1)}, } G.add_nodes_from([(n, nodes[n]) for n in nodes]) edges = [(1, 3), (1, 4), (2, 3), (3, 4), (3, 5)] G.add_edges_from(edges) delta = timedelta(days=5 * 365) # 未加权:前驱为 1 和 2 nx.cd_index(G, 3, time_delta=delta, time="time") # 输出: 0.5 # 加权:节点 2 的 weight 属性参与计算 nx.cd_index(G, 3, time_delta=delta, time="time", weight="weight") # 输出: 0.12

使用整数时间戳

time属性不必是日期对象,任何支持加法和比较运算的类型都可以,整数年份是最推荐的用法(原因见下文闰年陷阱):

node_times = {1: 2015, 2: 2012, 3: 2010, 4: 2008, 5: 2014} nx.set_node_attributes(G, node_times, "new_time") nx.cd_index(G, 3, time_delta=4, time="new_time") # 输出: 0.5 nx.cd_index(G, 3, time_delta=4, time="new_time", weight="weight") # 输出: 0.12

算法原理:公式与源码逐行剖析

数学定义

CD index 的数学定义(见 docstring Notes):

$$ CD_{t}=\frac{1}{n_{t}}\sum_{i=1}^{n}\frac{-2f_{it}b_{it}+f_{it}}{w_{it}} $$

其中:

  • $f_{it}$ = 1 表示专利 $i$ 引用了聚焦专利,否则为 0;
  • $b_{it}$ = 1 表示专利 $i$ 同时引用了聚焦专利的任一后继,否则为 0;
  • $n_t$ 是聚焦专利的"前驱与前驱的后继前驱"并集大小;
  • $w_{it}$ 是专利 $i$ 在时刻 $t$ 的权重矩阵。

直观理解:当前驱(引用者)指向聚焦专利的后继时,贡献正值(失稳方向);当前驱同时引用后继、把技术路径"串"起来时,贡献负值(整合方向)。

前驱筛选(源码 L116-L125)

try: target_date = G.nodes[node][time] + time_delta pred = {i for i in G.pred[node] if G.nodes[i][time] <= target_date} except: raise nx.NetworkXError( "Addition and comparison are not supported between 'time_delta' " "and 'time' types." )

关键点:G.pred[node]给出目标节点的所有前驱;只有time <= 目标节点时间 + time_delta的前驱被保留。try/except把类型不兼容(例如datetime时间配上数值型time_delta)统一转换为NetworkXError

b 值与 n 的计算(源码 L128-L133)

# -1 if any edge between node's predecessors and node's successors, else 1 b = [-1 if any(j in G[i] for j in G[node]) else 1 for i in pred] # n is size of the union of the focal node's predecessors and its successors' predecessors n = len(pred.union(*(G.pred[s].keys() - {node} for s in G[node]))) if n == 0: raise nx.NetworkXError("The cd index cannot be defined.")
  • b 值:对每个前驱 $i$,若它指向目标节点的任意后继($i \to s$ 存在,$s \in G[node]$),则 $b=-1$(整合信号),否则 $b=1$(失稳信号);
  • n 值:目标节点前驱集合与其所有后继的前驱集合(排除目标节点自身)的并集大小,作为归一化分母;若为 0(孤立的目标节点场景),CD index 无法定义,抛出NetworkXError

加权与未加权计算(源码 L136-L142)

if weight is None: return round(sum(bi for bi in b) / n, 2) else: weights = [G.nodes[i].get(weight, 1) for i in pred] return round(sum(bi / wt for bi, wt in zip(b, weights)) / n, 2)
  • 未加权:直接对 b 求和除以 n,结果四舍五入保留两位小数round(..., 2));
  • 加权:每个前驱的权重从节点属性读取,缺失权重的节点默认按 1 计G.nodes[i].get(weight, 1)),然后逐项计算 $b_i / w_i$ 再求和归一化。

时间类型兼容性与闰年陷阱

docstring 中有一条非常重要的实战警告(源码 L97-L103):datetime.timedelta存在闰年导致的 off-by-one 问题

timedelta(days=5 * 365) # 看似 5 年,实际因闰年不足 5 年

在示例中,timedelta(days=5 * 365)timedelta(days=4 * 365)得到相同结果,因为 2010 → 2015 之间存在闰日导致天数不足;而timedelta(days=5 * 365 + 1)这组特定年份恰好构成 5 年窗口,但如果 5 年跨度内存在多于 1 个闰年则仍会偏差。官方建议:

  • 优先使用整数表示年份(如time=2010, time_delta=4),完全规避闰年问题;
  • 若必须使用datetime/timedelta,转换时间单位时要格外小心。

异常处理:什么时候会报错

综合 函数 docstring 与源码,cd_index可能抛出以下异常:

异常触发条件
NetworkXError并非所有节点都含time属性
NetworkXErrortime_deltatime属性类型不兼容(加法或比较失败)
NetworkXError归一化分母 n 等于 0,CD index 无法定义
NetworkXNotImplemented输入为无向图或多重图

边界情形与测试验证

test_time_dependent.py 中的TestCdIndex类为上述行为提供了 11 个回归测试用例,覆盖:

  • 常规图(未加权/自定义属性名/整数/浮点时间):节点 4 的 CD index 均为0.17(L15-L153);
  • 加权图:同一拓扑下weight="weight"时结果为0.04(L155-L187);
  • 无前驱:返回0.0(L189-L220);
  • 无后继:返回1.0(L222-L253),此时所有前驱 b 值均为 1,达到失稳上限;
  • n 等于 0:断言抛出NetworkXError("The cd index cannot be defined.")(L255-L287);
  • 时间类型不兼容:整数time配上timedelta断言抛出类型错误(L289-L322);
  • 节点缺失 time 属性:断言抛出NetworkXError(L324-L358);
  • 极限语义test_maximally_consolidating构造全整合拓扑断言结果为-1test_maximally_destabilizing构造全失稳拓扑断言结果为1(L360-L431),验证了 CD index 的取值范围边界 $[-1, 1]$。

在 NetworkX 中的注册与后端调度

cd_index通过 algorithms/init.py L56 的from networkx.algorithms.time_dependent import *暴露为顶层 API,因此可以直接以nx.cd_index(...)调用;__all__ = ["cd_index"](源码 L6)限定该模块只导出这一个函数。文档层面,time_dependent.rst 通过automodule+autosummary自动生成 API 参考页,并在 doc/reference/algorithms/index.rst 的算法目录中占据独立条目。

总结

cd_index是 NetworkX 时间依赖算法族中目前唯一的成员,它把 Funk–Owen-Smith 的 CD index 数学模型封装为一行nx.cd_index(G, node, time_delta)调用,适用于专利引用、论文引用等带时间戳的有向引用网络。使用时牢记三条铁律:有向图且非多重图、所有节点具备time属性、time_deltatime类型必须可加可比;涉及年份换算时优先使用整数时间以规避闰年偏差。结合 源码 与 测试 阅读,即可将其无缝集成到你的时序网络分析流程中。

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

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

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

SysML接口块实战:住宅安防系统建模与EA落地指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/21 7:08:27

RV1126平台JD9366 MIPI屏驱动移植与触摸调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/21 6:58:31

STM32CubeMX+HAL库实战:串口printf重定向与ESP8266 AT指令调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/21 5:26:04

BrewUI:给Homebrew穿上图形界面外套,让macOS包管理更直观

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

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

STM32实现4-20mA压控恒流源电路设计与调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/21 5:01:05

DSP28335 SCI-B串口通信与Vofa+上位机调试避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华