这个月初翻 Futility Closet,翻到一张叫「拓扑学家世界地图」的东西,归在 Science & Math 分类底下,日期标的是 9 月 1 日。图是 Reddit 用户 xilefakamot 在 2020 年做的,后来通过 MapPorn 传开。它只画国家之间的国际边界,形状、面积、相互距离全部丢掉,为的是用尽量省的方式把各国边界的连接模式摆出来。
这张图我前几年应该见过一次,当时扫一眼就划过去了,只留下"挺怪"这个印象。这次重看,注意力全落在它的做法上。这篇记两件事:它到底抽象掉了什么,以及如果我自己复现一遍这个思路,会按什么顺序走。
拆开看看,它其实不太配叫「地图」。叫地图会让人默认它能找位置、量距离,而这两件事它一件都不做。
普通地图:节点是国家占据的那块形状,附带面积、坐标、彼此的距离
这张图: 节点是国家,边是「接壤」这一件事,其余属性一个都不带
所以它的本质是一份邻接关系网络,只是最后被画成了地图的样子。节点是国家,边表示两个国家是否接壤,写成邻接表大致是这么个结构:
莱索托: [南非]
梵蒂冈: [意大利]
意大利: [法国, 瑞士, 奥地利, 斯洛文尼亚, 圣马力诺, 梵蒂冈]
看第一行就够了。莱索托在这张图上的全部信息量就是「它挨着南非」,至于它被包在南非的哪一侧、面积多大、是个什么形状,图上一个字都不说。梵蒂冈同理。
真正让我想记一笔的是作者讲的做法:他没有做任何正式计算,而是先勾勒出一张显示国家相邻关系的网络,再一点点把这张网络平滑、调整成最终的画面,整个过程手工完成。也就是说,顺序是先有关系,再有形状。这一点我以前老是搞反。
我自己做图的时候习惯是反的——先想这块摆左边还是右边、配色怎么调,关系对不对是最后才回头看的。结果往往是线条一改,整个布局跟着重排,改到第三版干脆推倒重来。这张图提醒我的地方在于,邻接关系一旦固定,后面那点平滑工作就只是让线好看一点,它没办法反过来改变结构。结构先定死,表现形式是后面可以慢慢磨的。
如果要复现这个思路,我大概会按这个顺序走:
- 找一份国家之间的接壤数据,只留两个字段——哪两个国家挨着,别的一律不取。
- 转成邻接表,一个国家一行,后面跟一串邻居。
- 先检查被单个国家完全包住的那种,比如莱索托、梵蒂冈,它们的邻居列表应该只有一项。
- 再检查对称性:A 的邻居里出现 B,B 的邻居里就得有 A,不对称说明数据源本身有问题。
- 走完上面四步,手上只有关系,一个坐标都没有。摆位置、平滑线条,都是这一步之后的事。
第 3 步是最省事的自检,一分钟都用不到,但它能顺带告诉你数据源是不是只录了"大边"、把小国漏掉了。第 4 步我会顺手补一句检查:
for a, neighbors in adj.items():
for b in neighbors:
if a not in adj[b]:
print(f"{a} -> {b} 不对称")
这段特别土,但省事。数据源里漏一条、方向写反一条,跑一遍就露出来了,比画完图再拿眼睛找快得多。这一步我以前是跳过的,直到有一次顺着一条边往回查,才发现两个明显不挨着的国家被连在了一起,源头就是某个邻居只写了一半。
那这张图本身怎么验?我的办法很笨:随机挑十条边,逐条核对这两个国家是不是真的接壤。它是手工画的,边又这么多,零误差基本不可能;但随手抽的十条都对上,这份连接模式大体就是可信的,够用了。💡 小技巧是专挑邻接关系特别少的国家先看,只有一个邻居的那种最容易核对,也最容易一眼看出画错。
Futility Closet 把这个条目归到 Science & Math,而不是 Maps 或者 Art,我觉得归得挺准。它传递的不是地理信息,是结构信息——能拿来讨论的是连接模式,不是某个地方在哪儿。分类本身就是一种判断。
我一直觉得,做可视化的时候,丢掉信息比加上信息难得多。加东西谁都做得到,加地形、加经纬网、加人口密度,往上堆就是了;难的是判断哪一整块信息可以不要,去掉之后剩下的东西还能不能自洽。这张图把几乎全部几何信息都扔了,剩下的网络照样立得住——拿它查"这两个国家接不接壤",比任何一张普通地图都直接,虽然它压根不能拿来导航。顺带说一句,这也是我看不太上那种什么都想画上去的图的原因,不过这个话题可以另开一篇再记。
划重点:第一,先勾关系网络再想怎么摆好看,顺序反了后面一定要返工;第二,判断一张图有没有想清楚,先看它丢掉了什么,能整块丢掉还站得住的才算想清楚;第三,手工做的东西不用追求零误差,抓几条边自己核对一遍,比站在原地争论准不准快得多。
你可以拿手上正在处理的那份数据试一遍——一份服务之间的调用关系、一张表的外键依赖都行——先把关系列成邻接表再想画法,看看是不是比之前少返工几次。踩到别的坑,欢迎回来留言,我一起补一条笔记。
