跳到正文
册页

图表

WaterfallChart - 瀑布图

一个总数是怎么从一个数走到另一个数的。

用法

什么时候用它

「为什么变了」,而且贡献项可以是负的。饼图放不下负的扇形;各部分不必刚好补齐两个总数之间的差额时,用 BarChart。

来自@misoto22/folio/charts

TSX
import { WaterfallChart } from '@misoto22/folio/charts'

说明

一个总量是怎么从一个数走到另一个数的——期初的余额、把它推动过去的那些带正负号的变动,以及最后收在哪里。

这是回答「为什么会变」的那种形式:两根柱子答不了,饼图答得是错的——因为变动可以是负的,而扇区不能。当各部分并不需要正好凑成两个总量之间的差额时,用 <BarChart>;当这个量只会越来越小时,用 <FunnelChart>。

真正要当心的断言,是那些连接线。它们把各步画成了一个序列——先发生这个,再发生那个——而大多数拆解压根没有先后:同一个月里的流失和扩张是同时发生的,它们被列出来的顺序不过是一次编辑取舍。算术怎么排都成立,故事不成立,而读者一定会把最左边那根柱子当成第一个原因。还有两个连带的坑:中间那些柱子是悬空的长度,没有共同的基线可对,所以瀑布高处的一小步很难和接近零处的一大步比大小;而任何一步如果本身是两笔更大的反向变动净出来的,从图上根本看不出它是个净额。顺序是随手排的,就在 description 里说明白。

Recharts 在这里挣得的位置是坐标轴、网格和 tooltip。每根柱子都是一段悬空的区间——从当前累计值到新的累计值——上面再盖一个自定义形状,因为 Recharts 没有瀑布图这种标记,而连接线必须和它所连接的柱子出自同一套几何。

结构

WaterfallChart anatomy
部件说明
图框必填ChartFigure 的那个 <figure>,零个步骤时给出空状态。关于顺序的那句告诫该写进 description,而它在设置 showTitle 之前是 sr-only 的。
步骤必填<WaterfallChart.Bars>:一个步骤一根悬空的区间条,从上一个合计走到新的合计,上面盖着一个自定义 shape。增量和合计取实心的序列填充,减量取 45 度的斜纹,所以方向在灰度和强制颜色下仍然成立。
连接线connectors,默认打开,把每根柱子的收尾边和下一根的起点连起来。没有它们,瀑布图就是一排悬在互不相干的高度上的柱子,读者得自己把这道级联重建出来。
零基线必填一条零处的参考线,由 <WaterfallChart.Bars> 自己画。合计柱就站在它上面,而一张看不见零的瀑布图,是在要求读者对每一根悬空的柱子照单全信。
步骤标签showValues,而且它默认是关的。它把每一步带正负号的变化量印在柱子旁边——这在别处都不如在这里值钱,因为中间的柱子底下没有基线,而它的长度恰恰是坐标轴还不回来的那一样东西。
隐藏数据表那张 sr-only 表格带着每一步的变化量和累计值,正好就是图里编码成一个长度和一个位置的那一对。

实践建议

推荐

  • 把 showValues 打开。它默认是关的,而它正是这种图形核心弱点的解药:中间的柱子是悬空的,所以读者看得出某一步很小,却看不出小到什么程度。
  • 收尾的合计那一步不要写 value。省略掉,它就由上面各个增减量算出来;手写上去,它就可能和那些量对不上,而图会一声不吭地把这个矛盾画出来。
  • 步骤顺序是人为编排的时候,在 description 里说出来,并且传 showTitle,让这句话真的被印出来。连接线把各步画成一个序列,可绝大多数拆解并不是序列——同一个月里的流失和扩张是同时发生的——而读者会把最左边那根柱子当成第一个原因。

避免

  • 不要把两个方向相反的变动轧成一个步骤。一根读作负二十、实际上是正一百八十对负两百的柱子,画出来和一个风平浪静的月份一模一样,而把“动了什么”显示出来正是这种图形存在的全部目的。
  • 不要用眼睛去比中间的柱子和合计柱。只有合计坐在零线上;它们之间的一切都是悬在任意高度上的一段长度,所以级联上方一个小的步骤和接近零处一个大的步骤,根本不站在可比的基准上。
  • 贡献项带正负号时,不要去用饼图。这种图形之所以存在,就是因为饼图放不下一块负的扇形——而当各部分不必刚好补齐两个合计之间的差额时,对的那个是 BarChart。

示例

默认

一个期初合计、四笔带正负号的贡献,以及一根本身不带值的收尾柱:它就是那些变动加起来的结果,所以它永远不可能和它上面那些步骤对不上。减少取 45 度斜纹,增加取实心填充——用纹理而不是第二种颜色,于是这个读法在灰度和强制色彩下都还活着——而一个合计是靠几何和一次增加区分开的,因为它是唯一一种站在基线上的柱子。这里的各步是同时发生的,它们的顺序是一次编辑取舍,而描述里把这件事说了出来:算术怎么排都成立,但那些连接线画的是一个序列,而读者一定会把最左边那根柱子当成第一个原因。

ARR bridge, FY24 to FY25Thousands of AUD. The steps are simultaneous; the order is editorial.
ARR bridge, FY24 to FY25
nameChangeRunning total
FY24 ARR4,2004,200
New business1,1805,380
Expansion6406,020
Downgrades-3105,710
Churn-8204,890
FY25 ARR04,890

小计

type 用 total 会在基线上立一根柱子并重设累计值,而这里在中间用了两次——毛利,然后营业利润——于是这条瀑布在读者本来要在脑子里加五段悬空长度的地方,把自己重述了一遍。两个小计都不带值,所以两个都不可能和它们上面那些步骤对不上。把连接线关掉,同样这些柱子就成了一排高度毫不相干的长度,而这既是把它们留着的理由,也是把它们刻意去掉的理由:当各步的顺序是随手排的,那些连接线宣称的是一个数据本身并不具有的序列。

Operating profit bridge
Operating profit bridge
nameChangeRunning total
Revenue8,4008,400
COGS-3,1005,300
Gross profit05,300
Sales-1,4503,850
R&D-1,9001,950
G&A-7201,230
Operating profit01,230

对照计划

那根收尾柱说出总量落在了哪里;它没说那是不是计划所说的地方。一条参考线把那一个数字横着画过整条瀑布,画在柱子之上而不是背后——一个被柱子盖住的目标是一个没人核查得了的目标——而且它会把坐标轴撑开去容纳自己,而不是被丢掉,这正是它在这里看得见的原因,因为这个计划值高于图中每一根柱子。weight 用 firm 是实心墨色,留给那一条图表真正在讲的阈值;安静的默认值是一条虚线细线,留给那些必须在场、又必须明显不是数据的背景。

ARR bridge against plan, H1 to H2Thousands of AUD. The steps are simultaneous; the order is editorial.
ARR bridge against plan, H1 to H2
nameChangeRunning total
H1 actual5,2005,200
New business1,4206,620
Expansion7807,400
Discounts-2607,140
Churn-9406,200
H2 actual06,200

类型

TSX
export type WaterfallStepType = 'delta' | 'total'
export type WaterfallDirection = 'increase' | 'decrease' | 'total'

无障碍

  • 连接线是要小心对待的那个断言:它把各步画成一个序列,可绝大多数拆解根本不是序列——同一个月里的流失和扩张是同时发生的,而读者会把最左边那根柱子当成第一个原因。顺序是任意的时候,在 description 里说出来。
  • 中间那些柱子是悬空的长度,没有基线可读,所以级联上方一个小的变动很难和接近零处一个大的变动作比较。只有 total 柱坐在坐标轴上,也只有它们能被绝对地读出来。
  • 方向由标签的正负号和柱子的纹理承担,不只靠位置——所以在灰度和强制颜色下这个读法仍然成立。
  • 不给 value 的收尾柱由各个增减量算出来,把这道算术留在数据里,而不是留在调用者脑子里。