用 Vue.js 与 D3.js 构建自定义社交网络可视化:Data-Science-For-Beginners 第 13 课作业实战
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
导读
本文面向正在学习《数据科学入门(Data-Science-For-Beginners)》第 13 课「创建有意义的可视化」的开发者,围绕课程作业「构建你自己的自定义可视化」展开。你将学会如何基于仓库中现成的 Vue 3 + vue-d3-network 起步项目,把小说《危险关系》中 175 封书信的收发关系转换成一张可交互、可拖拽的社交网络图,理解力导向图(force-directed graph)的数据建模、嵌套循环建链与节点样式配置原理,并掌握把同类技术迁移到「自己社交数据」项目上的完整方法。
作业背景:从图表选择到创造性可视化
本作业位于课程《Making Meaningful Visualizations》单元中。在开始动手前,课程正文已经建立了一套核心方法论(见 英文课程 README),作业正是这些知识的实践检验:
- 选对图表类型:趋势用折线图、类别比较用柱状/饼图、总量比较用饼图/堆叠柱状图、关系用散点/折线/分面/双线图、分布用散点/直方图/箱线图、占比用饼图/环形图/华夫图;
- 避免欺骗性图表:课程用翻转 X 轴、重排日期、翻转 Y 轴叠加误导性配色等反面案例,提醒读者视觉极易被操纵;
- 谨慎使用颜色:颜色会携带含义(如红色代表力量、蓝色代表信任忠诚),并要考虑色盲用户的可用性;
- 注重可读性:合理设置画布宽高、标注坐标轴、提供图例与 tooltip;
- 尝试动画与 3D 呈现,最终完成一个创造性的可视化作品。
作业选择的载体是《危险关系》(Les Liaisons Dangereuses)——1782 年肖代洛·德拉克洛创作的书信体小说,情节由 175 封往来书信推进。课程给出的思路是:把每封信抽象成「发信人 → 收信人」的一条边,把每个角色抽象成一个节点,用力导向图在视觉上「查出」谁是这桩贵族社交阴谋的核心人物。
项目结构与运行环境
作业要求使用仓库中 starter 起步项目 作为代码基础,仓库同时提供了 solution 完成版 供参考对照。起步项目的核心结构如下:
starter/ ├── public/ # 静态入口(index.html、favicon.ico) ├── src/ │ ├── assets/ │ │ └── letters.json # 全部书信数据(含 from/to) │ ├── components/ │ │ └── Nodes.vue # 本作业主要修改点:构建节点与连线 │ ├── App.vue # 根组件,渲染 <Nodes /> │ └── main.js # Vue 应用入口 ├── babel.config.js ├── package.json └── package-lock.json从 starter/package.json 可以看出项目依赖与脚本约定:
{ "name": "liaisons", "version": "0.1.0", "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "core-js": "^3.6.5", "vue": "^3.5.16", "vue-d3-network": "0.1.28" }, "engines": { "node": ">=20.0.0" } }关键技术依赖是vue-d3-network@0.1.28——一个基于 Vue 与 D3 构建的网络可视化组件库。前提条件:根据 starter/README.md,本机需要安装 NPM 和 Node.js>=20.0.0。启动流程为:
npm install # 安装依赖 npm run serve # 开发模式,支持热重载,默认监听本地开发服务器 npm run build # 生产构建,产出可部署的静态资源(dist/) npm run lint # ESLint 代码检查与自动修复运行npm run serve后浏览器会自动打开页面,你会看到一张社交网络图,并且可以直接用鼠标拖拽节点,把数据在屏幕上「摇匀」后重新观察网络的疏密结构。
数据模型:书信 JSON 与角色清单
可视化的事实来源是 starter/src/assets/letters.json,共 1261 行,每封信是一个对象。以文件开头几条为例:
[ { "letter_num": 1, "from": "Cécile de Volanges", "to": "Sophie Carnay", "date": "8/3/17**", "location": "Ursulines" }, { "letter_num": 2, "from": "Marquise de Merteuil", "to": "Vicomte de Valmont", "date": "8/4/17**", "location": "Chateau de…" } ]字段含义:
| 字段 | 含义 | 在本项目中的用途 |
|---|---|---|
letter_num | 书信编号 | 作为数据索引,便于追踪每一封信 |
from | 发信人姓名 | 连线的起点(source) |
to | 收信人姓名 | 连线的终点(target) |
date | 信件日期(原文中部分为17**模糊年份) | 可视化中未使用,可作扩展维度 |
location | 写信地点 | 同样未用于建图,属可挖掘的附加信息 |
而角色清单由 starter/src/components/Nodes.vue 中created()钩子里的characters数组硬编码给出,共 15 个节点:
let characters = [ "Chevalier Danceny", "Marquise de Merteuil", "Cécile Volanges", "Présidente de Tourvel", "Azolan, chasseur", "Madame de Rosemonde", "Madame de Volanges", "Vicomte de Valmont", "Père Anselme", "...", "M. Bertrand", "Anonyme", "Sophie Carnay", "Maréchale de ***", "Le Comte de Gercourt", ];从数据模型角度理解:letters.json描述的是「边」集合,characters描述的是「节点」集合。作业的核心就是把「边集合」中的人名与「节点集合」的下标对应起来,生成{ sid, tid }形式的边对象——这正是vue-d3-network组件消费的数据格式(sid为源节点 id,tid为目标节点 id)。
核心实现:补齐 createLinks 建链逻辑
作业的动手部分在Nodes.vue中完成,关键方法是createLinks()。起步代码里该方法体只有一行注释//complete this code,作业要求把英文 README 给出的嵌套循环补进去。完成后的逻辑如下(与 solution/src/components/Nodes.vue 完全一致):
methods: { createLinks(characters) { //loop through letters let f = 0; let t = 0; for (var i = 0; i < letters.length; i++) { for (var j = 0; j < characters.length; j++) { if (characters[j] == letters[i].from) { f = j; } if (characters[j] == letters[i].to) { t = j; } } this.links.push({ sid: f, tid: t }); } }, // ... }这段代码的执行逻辑分三层:
- 外层循环遍历
letters(信件数组),letters[i].from与letters[i].to分别是这封信的发信人与收信人姓名; - 内层循环在
characters中线性查找这两个姓名,找到后把对应下标赋给f(from)与t(to); - 每处理完一封信,向
this.links推入{ sid: f, tid: t },即一条「发信人下标 → 收信人下标」的有向边。
值得说明的细节:f、t声明在循环外部,内层循环会持续覆盖它们,因此对每一封信都能拿到正确的两个下标。这是从源码结构可推断的实现约定,说明letters.json中出现的发收信人姓名必须都能在characters数组中精确匹配到(含大小写与标点),否则下标会停留在上一次的残留值上,产生错误的边。这正是自定义数据集时最容易踩的坑。
节点样式与力导向配置解析
createLinks()只负责建边;节点本身在created()钩子中构建:
created() { // ... characters 数组定义 this.createLinks(characters); for (var j = 0; j < characters.length; j++) { this.nodes.push({ id: j, name: characters[j], _size: this.getCount(characters[j]) + 20, _color: "#" + Math.floor(Math.random() * 16777215).toString(16), }); } }每个节点对象携带四个字段:
| 字段 | 取值 | 含义 |
|---|---|---|
id | 角色下标j | 与sid/tid对应的节点标识 |
name | 角色姓名 | 图上的节点标签 |
_size | 收到信件数 + 20 | 节点半径与「热度」挂钩:被写信越多节点越大 |
_color | 随机十六进制颜色 | 每个节点随机配色(#+ 0xFFFFFF 内的随机值) |
其中_size依赖同文件里的getCount(name)方法——它遍历全部信件,统计某人作为to(收信人)出现的次数:
getCount(name) { var count = 0; for (var i = 0; i < letters.length; i++) { if (letters[i].to === name) { count++; } } return count; }「节点大小 = 收信热度」这一设计,正是课程强调的让数据决定视觉编码的体现:图表的每个视觉属性(位置、大小、颜色)都应当承载数据含义,而不是随意堆砌。
画布与力导向参数集中在computed的options中:
computed: { options() { return { force: 3000, // 节点间斥力强度,数值越大节点越分散 size: { w: 600, h: 600 }, // 画布尺寸 offset: { x: 0, y: 0 }, // 画布偏移 nodeLabels: true, // 显示节点标签 linkLabels: true, // 显示连线标签 canvas: this.canvas, // 是否使用 canvas 渲染(默认 false,走 SVG) }; }, }force是力导向布局的关键旋钮:它控制节点之间的排斥力大小,直接影响网络的「摊开」程度——值越大节点间距越开,值越小节点越容易聚拢成团,适合在观察社区结构时调节。canvas字段则对应data()中的canvvas: false开关,可切换到 canvas 渲染以获得更好性能。模板侧通过以下方式把三者接入组件:
<d3-network :net-nodes="nodes" :net-links="links" :options="options" />Nodes.vue底部还有一套 CSS 视觉规范:.node默认填充#dcfaf3并在:hover时描边加粗为金色高亮;选中节点描边#caa455,固定(pinned)节点描边紫色;连线默认半透明绿、悬停金色加粗——这些都属于课程「为可读性而设计样式」的实践:让交互状态清晰可辨,而不是仅凭默认样式敷衍了事。
完成效果与运行验证
补全代码后再次运行npm run serve,即可看到《危险关系》的书信网络图。课程 README 给出的运行效果截图如下:
运行中可以观察到:收到信件越多的角色节点越大(例如 Marquise de Merteuil 与 Vicomte de Valmont),连线的疏密暗示角色间的通信强度,拖拽节点则能帮助读者从不同角度辨认网络中的「枢纽人物」——这正是「用可视化回答问题」的完整闭环。如果对结果有疑问,可直接对比 solution 完成版 的同一文件。
作业评分标准
德语版 作业说明 给出的评估表格如下,作为交付质量的验收清单:
| 优秀(Vorbildlich) | 合格(Angemessen) | 待改进(Verbesserungswürdig) |
|---|---|---|
| 提交一个能正确运行的 GitHub 仓库(尽量作为静态 Web 应用部署),并附带解释项目的带注释 README | 仓库无法正常运行,或文档不完善 | 仓库无法正常运行且文档不完善 |
也就是说,仅代码跑通还不够,交付物还要求:部署为静态 Web 应用(npm run build产出的dist/目录即为静态资源,可直接托管到任意静态站点服务)、README 要完整说明项目(数据来源、构建方式、运行步骤、可视化设计思路)。这两条与课程反复强调的「可读性」与「可复现性」一脉相承。
迁移到自定义社交数据:把《危险关系》换成你自己的网络
作业的另一半要求是设计自己的社交交互数据——例如绘制自己的社交媒体使用情况,或画一张家庭关系图谱。结合上面分析的源码,迁移步骤非常清晰:
- 替换数据文件:新建与 letters.json 同构的 JSON,保持
from/to(可加letter_num等扩展字段)字段名不变,例如:
[ { "letter_num": 1, "from": "Alice", "to": "Bob", "date": "2026-01-05" }, { "letter_num": 2, "from": "Bob", "to": "Carol", "date": "2026-01-06" } ]- 同步角色清单:把
Nodes.vue中characters数组替换为你的节点姓名集合,务必保证 JSON 中的from/to与数组元素完全精确匹配(这是createLinks能正确建边的唯一前提); - 调整视觉编码:
_size的getCount + 20计算公式、随机_color、options.force与画布尺寸可按数据量调节——比如家庭图谱节点少,可适当调低force让网络更紧凑; - 重新运行与部署:
npm run serve本地验证后执行npm run build生成静态产物,按评分标准部署并撰写带注释的 README。
小结
本作业是第 13 课全部知识点的综合演练:从「为问题选对图表」到「让颜色与大小承载语义」再到「动画化、可交互的创造性可视化」。通过补全createLinks()的嵌套循环、理解_size与force的视觉编码,你已经掌握了一条完整的「数据建模 → Vue 组件集成 → 力导向图渲染 → 静态部署」链路,并能把它复用到任何以人物/实体为节点、以关系为边的数据集上。相关源码均可继续深入研读:起步项目、完成版参考、课程正文。
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考