双轴图不改变相关系数,它改变的是「交叉点」——而读者读的恰恰是交叉点
【锚点】这一篇只钉一个判断:【双轴图(dual-axis)的危险不在它夸大了相关,而在它凭空生产「交叉点」和「谁超过谁」——这两样东西完全由你选的轴窗口决定,数据里没有。】
【显式】先把数学摆上桌。设两条序列 a_t、b_t。双轴图做的事,是给每条序列配一个仿射映射压到同一像素高度:
p_t = α_A·a_t + β_A (左轴) q_t = α_B·b_t + β_B (右轴)
α 由轴窗口的跨度决定,β 由窗口下界决定。
【隐式】一步推理:Pearson r 对正仿射变换不变,所以 |r(p,q)| = |r(a,b)|。也就是说——【双轴图根本没能力伪造相关系数】。你改轴,r 纹丝不动。这是它最容易被冤枉的地方,也是它最容易蒙混过关的地方:因为读者从来不是靠 r 读图的。
【细微】需要比对两处才看得见的东西:读者真正读的是三样—— 1. 【交叉点】{ t : α_A·a_t + β_A = α_B·b_t + β_B }。解出来是 α_A/α_B 与 (β_A−β_B) 的函数。两个自由参数,交叉点可以搬到任意 t 上,也可以彻底消灭(永不相交)。 2. 【视觉差距】两条线的垂直像素距离 = |α_A a_t − α_B b_t + (β_A−β_B)|,同样是自由参数的函数。 3. 【谁在某年超过谁】——就是交叉点的另一种说法。 这三样,没有一样在数据里。它们全在轴窗口里。
【极难发现】再深一层:这意味着【一张图是一个带自由参数的模型,而参数从不写进方法部分】。你的回归写了系数和标准误;你的图什么都没写。审稿人复现得了你的模型,复现不了你的图——因为「右轴取 0–1000 还是 0–50」不在任何代码注释里,它藏在 plt.twinx() 的下一行。
【流程节点链路】起点:数据表 (t, a_t, b_t) → 中间节点1:选定左轴窗口 [L_min, L_max],得到 α_A、β_A → 中间节点2:选定右轴窗口 [R_min, R_max],得到 α_B、β_B → 中间节点3:两条序列经仿射映射压到同一像素高度 → 终点:读者读出的「交叉点 / 视觉差距 / 谁超过谁」。这条链上【只有起点是数据,后三站全是编辑决定】。
【显式】一个可复算的例子。取 a_t = t(t=1..10,气温),b_t = 3t(销量): - 右轴取 0–30:两条线完全重合,读者读作「同步」。 - 右轴取 0–300:b 被压到 a 下面,读者读作「销量远低于气温,且差距在扩大」。 - 右轴取 0–15:b 冲到 a 上面,读者读作「销量领先」。 同一份数据,三种叙事,r 恒等于 1。
【隐式】所以「双轴图能不能用」这个问题问错了。该问的是:【这张图想主张的是「水平」还是「形状」?】 - 主张【水平对比】(谁高谁低)→ 双轴图非法,因为水平由轴决定。用同一根轴,或直接画比值。 - 主张【形状同步】(一起涨一起跌)→ 双轴图仍不是最优,因为交叉点会误导。正确做法是【指数化到共同基期】(t0 都设 100):这杀掉 β 自由度和单位差异,只剩 α 一个自由度,而 α 对「形状」无影响。
【流程节点链路】修正流程:起点:两条量纲不同的序列 → 中间节点1:各自除以 t0 期取值,指数化到 100 → 中间节点2:画在同一根轴上(单位统一为「相对基期的百分比」)→ 终点:读者读到的差距与交叉点都是数据的,不是轴的。若指数化后仍要保留绝对水平,则改画单轴比值图 b_t/a_t。
【细微】还有一个常被漏掉的成本:双轴图的图例通常只标颜色,不标「哪根轴对应哪条线」。读者要来回扫 颜色—轴—线,三次映射。Alberto Cairo 在《How Charts Lie》里把这类图列为最容易被无心误读的类型之一——不是因为作者撒谎,是因为读者要做的映射太多。
【锚点回扣】一句话版本:【双轴图不伪造相关,它伪造的是「相遇」。而人类读两条时间序列,读的就是相遇。】
【值得吵的问题】如果一张图就是一个带自由参数的模型,那「图的方法部分」该长什么样?我的立场:每张发表用图应附一行可执行的【图规格】(轴窗口、变换、聚合、bin 宽、缺失值处理),像 sessionInfo() 一样进补充材料。反对意见我也认:这会杀死 exploratory plotting 的自由。但 exploratory 和 publication 本来就是两件事——前者不需要记录,后者必须。你能接受「图规格」进审稿清单吗?







0 Comments