【计算机毕业设计单片机案例】基于 STC 单片机的双模式智能窗帘家居调控装置研发 基于 51 单片机的光照窗帘与温湿度风扇联动系统设计(011501)
2026/7/29 18:08:52
做文献计量的小伙伴几乎都遇到过这样的尴尬:把 Web of Science 的纯文本往 CiteSpace 里一丢,结果跑出来的"突现关键词"要么全是常见词,要么干脆空白。问题往往出在两点:
传统 TF-IDF 只能告诉你"哪些词重要",却无法回答"哪段时间突然爆发"。而 CiteSpace 的 Keywords Burst 功能,本质上是把 Kleinberg 的 burst detection 嫁接到文献时间序列上,用自动化方式找"短期激增"。理解它到底怎么切时间、怎么算突现,是避免"红条乱飘"的唯一办法。
Kleinberg 把词频序列看成状态机:状态 0 表示"平稳",状态 1 表示"爆发"。算法用指数衰减代价函数,衡量"从平稳跳到爆发"所需额外编码长度。代价越小,越值得报警。公式核心:
TF-IDF 只看"全局权重",burst 只看"局部增速"。二者互补:先用 TF-IDF 筛掉低频词,再用 burst 找增速拐点,才能既省算力又保精度。
输入:词 w 在年份 t 的计数序列 C[t] 输出:burst 区间列表 1. 对 C[t] 做滑动平均平滑 2. 估计 λ0 = mean(C) 3. 遍历候选区间 [i,j] 3.1 估计 λ1 = mean(C[i:j]) 3.2 计算转移代价 cost 3.3 若 cost < γ 且 λ1 > λ0,记录 burst 4. 合并重叠区间下面代码依赖pandas+numpy,在 Jupyter 里可直接跑。重点看 γ 调优注释。
import pandas as pd import numpy as np from collections import defaultdict def kleinberg_burst(series, gamma=0.3, s=2): """ series: 年份-计数 Series,index 为 int 年份 gamma: 状态转移惩罚,对应 CiteSpace 的 γ s: 平滑窗口,年份窗口通常取 1-3 """ # 1. 数据标准化:补全缺失年份并滑动平均 full_range = np.arange(series.index.min(), series.index.max()+1) counts = series.reindex(full_range, fill_value=0) smoothed = counts.rolling(window=s, center=True).mean().fillna(0) # 2. 全局强度 λ0 lambda_0 = smoothed.mean() or 1e-6 # 避免除 0 bursts = [] n = len(smoothed) # 3. 暴力扫描所有区间 [i, j] for i in range(n): for j in range(i+1, n): seg = smoothed.iloc[i:j] lambda_1 = seg.mean() if lambda_1 <= lambda_0: continue t0, t1 = seg.index[0], seg.index[-1] cost = (t1-t0)*np.log(lambda_0/lambda_1) + \ (seg.sum()-lambda_0*(t1-t0))*np.log(lambda_1/lambda_0) if cost < gamma: bursts.append((t0, t1, float(lambda_1))) # 4. 合并重叠 bursts = merge_intervals(bursts) return bursts def merge_intervals(bursts): if not bursts: return [] bursts = sorted(bursts, key=lambda x: x[0]) merged = [bursts[0]] for b in bursts[1:]: if b[0] <= merged[-1][1]: merged[-1] = (merged[-1][0], max(merged[-1][1], b[1]), max(merged[-1][2], b[2])) else: merged.append(b) return merged # ---------- 演示 ---------- if __name__ == "__main__": # 假设已读取 WOS 数据,生成关键词-年份计数表 df = pd.read_csv("keyword_year_count.csv") # cols: keyword,year,count res = defaultdict(list) for kw, sub in df.groupby("keyword"): bursts = kleinberg_burst(sub.set_index("year")["count"], gamma=0.25) res[kw] = bursts # 保存结果 pd.to_pickle(res, "keyword_burst.pkl")γ 调优经验:
joblib.Parallel;内存管理:
int16存计数,年份用int16|uint16足够;del中间变量,防止 pandas 隐式拷贝。df = df[df.year >= 实际起始年]。import seaborn as sns import matplotlib.pyplot as plt def gantt(burst_dict, topk=20): rows = [] for kw, li in burst_dict.items(): for s, e, _ in li: rows.append({"Keyword":kw, "Start":s, "End":e}) g = pd.DataFrame(rows).sort_values("Start") g = g.head(topk) g["Keyword"] = pd.Categorical(g["Keyword"]) sns.barplot(x="Start", y="Keyword", data=g, color="firebrick") plt.title("Top Keyword Bursts Timeline") plt.tight_layout(); plt.show()burst.detector包接口,可用 Java 继承AbstractBurstDetector,把 γ 做成滑动条,实时预览。把 CiteSpace 的 Keywords Burst 搬到 Python 并没有想象中神秘:核心就是 Kleinberg 的代价函数 + 一个 γ 门槛。只要先把时间切片对齐、同义词合并,再用滑动平均去噪,基本就能复现官方结果。真正难的是"调 γ"——这更像一门手艺,需要结合领域知识反复比对。希望这篇笔记能帮你少踩几次"红条乱飘"的坑,也欢迎你把 LDA、跨语言等新玩法试出来,一起把突现检测做得更准、更快、更懂行。