資料地圖該用哪種色階?viridis、YlGnBu 與 ColorBrewer 實戰指南
本篇是「打造 TaxMap-TW:用 Astro 做台灣所得地圖」系列的第 1 / 10 篇。你可以從系列總覽開始閱讀,也可以直接接著看本文。
最近在做 TaxMap-TW:一張顯示全台灣 7,747 個村里所得稅統計的互動地圖。要為每個 polygon 上色時,第一個問題就讓我卡了一小時:
「Choropleth 地圖該用哪種色階?」
我以為這是 30 秒就能決定的設計問題。結果發現裡面有色盲議題、ColorBrewer 業界標準、長尾分布陷阱、以及跟基底圖的 opacity 互動,每一個都是踩過才知道的坑。
這篇整理那一小時學到的東西。
資料地圖的色階到底在解什麼問題
Choropleth(音「克羅羅普列斯」,沒人念得對)就是「用顏色表達數值」的地圖:每個區域填一個顏色,深淺對應到該區的某個統計量。
聽起來很簡單,但你的每一個選擇都在影響讀者:
- 色相(hue):用紅、藍、綠表達不同的情緒
- 明度漸層:表達數值大小
- 分級方式:把連續數值切成幾個顏色區間
- 透明度:跟下層地圖怎麼疊
- 色階方向:低→高還是高→低
每個選擇都可能讓讀者看到「不存在的趨勢」或「忽略真實的差異」。這不是美感問題,是會不會誤導讀者的問題。
七種主流色階快速比較
直接拿我整理出的比較表:
| 色階 | 視覺風格 | 適合場景 | 陷阱 |
|---|---|---|---|
| Viridis | 紫→藍→綠→黃 | 學術、中性嚴肅 | 不夠吸睛 |
| YlGnBu | 淺黃→綠→深藍 | 公部門報告、所得 / 教育 | 深色端容易跟水體混 |
| YlOrRd | 黃→橘→紅 | 媒體、社群、視覺衝擊 | 紅色易被解讀為「壞」 |
| Plasma / Magma | 黑→紫→紅→黃 | 深色模式、有質感 | 需要深底搭配 |
| Greys | 淺灰→深灰 | 極簡、副地圖 | 訊息密度低 |
| Red-Green | 紅→黃→綠 | ❌ 不推薦 | 8% 男性紅綠色盲無法區分 |
| Diverging (RdBu) | 紅←白→藍 | 雙向偏離(如「相對於平均」) | 不適合單向資料 |
前五個是 sequential(單向)色階,適合「越多越深」這種單方向遞增的資料;最後一個是 diverging(雙向)色階,適合表達相對於某個中心點的偏離(例如選舉得票率相對於 50%)。
選錯類型會給錯誤的暗示——例如用 diverging 的紅藍色階畫所得,會讓讀者以為「藍色那邊比紅色那邊好或壞」,但其實只有「多」跟「少」。
Viridis:學術派的最愛
Viridis 是 Python matplotlib 在 2015 年導入的色階,後來變成科學論文的視覺標配。它的三個特性:
- 感知均勻(perceptually uniform):你眼睛看到的「色差」跟資料本身的「數值差」是線性對應的
- 色盲友善:deuteranopia(綠色盲)跟 protanopia(紅色盲)讀者也能正確分辨
- 黑白列印也能用:因為亮度本身就是漸層
姊妹色階 plasma、inferno、magma 也都有這三個特性,只是色相不同。如果你不知道該選什麼,預設用 viridis 不會錯。
但 viridis 不是沒有代價。對一般民眾來說它看起來有點「太學術」,而且整條色帶偏暗——低值端是深紫,疊到淺色底圖上反而比高值端的黃還搶眼,跟「越多越深」的直覺剛好相反,第一次看的人容易把低值區誤判成重點。如果你的網站受眾是公務員、媒體、社群讀者,YlGnBu 或 YlOrRd 這種低值夠淺、明度從淺到深單調遞增的色階,會更符合他們的視覺習慣。
YlGnBu:ColorBrewer 的經典
ColorBrewer 是 Penn State 的 Cynthia Brewer 教授在 2002 年釋出的色階庫,原本是給地圖設計師用的,現在幾乎所有 GIS / 視覺化工具(D3.js、Leaflet、Tableau、QGIS)都內建了它。
ColorBrewer 提供三類色階:
- Sequential:YlGn、YlOrRd、Blues、Reds、Greens 等,適合單向遞增
- Diverging:RdBu、PiYG、BrBG 等,適合雙向偏離
- Qualitative:Set1、Pastel1 等,適合類別資料(無序)
YlGnBu(黃→綠→藍)是 sequential 類別裡最常被選用的之一,理由是:
- 黃色端夠亮、藍色端夠深,對比範圍大
- 沒有用紅色,避免「紅 = 壞」的情緒誤導
- 公部門報告書的視覺習慣,給人「冷靜、客觀、財經」的感覺
我最後選 YlGnBu 的原因,就是「所得稅地圖」這個題材本身就需要冷靜感。如果是傳染病熱點圖,我會改用 YlOrRd 來強調「警示」。
為什麼紅綠色階是地雷
#ff0000 紅 → #00ff00 綠,這是直覺裡「壞→好」的對比。很多人做地圖第一個會想到的就是這個配色。
但根據統計,全球約 8% 的男性是紅綠色盲(女性約 0.5%)。他們看到的紅色跟綠色都會變成偏黃褐色,幾乎無法區分。
這代表如果你的地圖讀者裡每 12 個男生就有 1 個看不出來,你做的所有視覺化都失效了。
更糟的是:紅綠色階的「紅 = 壞」是西方文化習慣。在台灣,紅色常代表喜慶;中國股市則「紅漲綠跌」。同樣顏色在不同文化代表相反意義,這也是要避開的坑。
說到底,紅綠最大的問題不是「紅綠」這兩個顏色本身,而是它只靠色相去編碼數值、明度卻幾乎沒變——色盲讀者一旦分不出色相,就什麼資訊都拿不到。真正該守的原則是:不要只靠色相編碼,要保證明度單調遞增,這樣就算把地圖印成黑白、或讀者是色盲,深淺順序還是讀得出來。如果你真的需要雙向表達(高於平均 / 低於平均),也不必硬用紅綠——ColorBrewer 的 RdYlBu、BrBG 這類 diverging 色階就是設計成色盲安全的,兩端明度也夠分。
簡單原則:除非你有非常強的理由(例如就是要表達 +/− 雙向),否則 sequential 色階不要用紅綠對比;要雙向就挑色盲安全的 diverging。
分級方法:等距分級會殺死你的資料
選好色階後還有第二個決定:怎麼把連續數值切成 5 個顏色區間?常見方法:
| 方法 | 切法 | 適用 |
|---|---|---|
| 等距(Equal Interval) | 從 min 到 max 平均切 | 均勻分布的資料 |
| 分位數(Quantile) | 每級剛好佔 20% | 想讓地圖顏色均勻 |
| 自然斷點(Jenks) | 演算法找資料的群聚邊界 | 學術正統、保留分布 |
| 標準差 | ±1σ、±2σ 切 | 突顯異常值 |
| 手動斷點 | 自訂門檻 | 有編輯觀點 |
我原本想用等距分級,「最公平、最直覺」嘛。但攤開台灣所得稅資料一看:
- 台北市松山區中華里中位數:98.4 萬元(全國前段)
- 偏鄉村里中位數:30-40 萬元
2025 年公布的 112 年度統計裡,台北松山中華里以平均所得 526.6 萬元登頂全台最富里,擠下蟬聯 5 年榜首的新竹關新里。但要注意 526.6 萬是「平均數」,會被里內極少數高所得家戶整個拉上去——同一個中華里的「中位數」其實只有 98.4 萬。一個里內 mean 比 median 高出五倍,本身就是長尾的訊號。
這就是經典的長尾分布。如果你用等距分級,把上限拉到那種平均破 500 萬的村里,那 90% 的村里就會全部擠在最低色階——整張地圖看起來就是「一片米黃 + 一個刺眼藍點」,幾乎沒有區分度。
這是台灣公開資料視覺化的經典踩坑。
解法是 Jenks 自然斷點:演算法會找出資料的「天然群聚邊界」,讓組內變異最小、組間變異最大。視覺上既不會被尾部極端值搞爛、也不會被分位數的均勻切法掩蓋分布本身的特徵。
但 Jenks 不是無代價的勝利。它的斷點是「跟著這份資料算出來的」,所以換一份資料就會跳——我做單一年度的靜態地圖沒事,但如果你要做跨年比較、或資料會定期更新,每次重算斷點會讓同一個值的顏色一直變,讀者沒辦法把兩張地圖疊著看;斷點也常落在像 47.3 萬這種難讀的非整數上,圖例不好寫;資料量大時演算法本身也吃計算資源。需要跨年比較或動態更新時,我反而會改用一組固定的手動斷點,犧牲一點貼合度換「同一個值永遠同一個顏色」。
順帶一提,被我跳過的分位數也不是只有缺點。它保證每一級剛好塞進 1/5 的村里,每個顏色都有足夠樣本、不會出現某一級全空,而且因為切法跟絕對數值脫鉤,反而最適合講「相對排名」(你家這個里贏過全台幾成)。它的代價是會把分布的形狀抹平——明明差很多的兩個里可能同色、差一點點的卻被切到不同級。所以這比較像「想突顯分布形狀就 Jenks、想講相對位置就分位數」的取捨,不是誰絕對贏。
ColorBrewer + Jenks 是我這次的選擇,QGIS 內建分級和 Axis Maps 的 cheatsheet 也都把這組當預設起點。
真實踩到的坑:opacity × 基底圖
色階跟分級都選定了,本以為大功告成。結果 MapLibre 跑起來——polygons 看不見。
我用的是 OpenFreeMap 的 positron 主題:一張極淺的灰底地圖。fill-opacity 設 0.7(直覺值),結果 YlGnBu 的淺色端(#ffffcc 黃白)幾乎跟灰底融成一體,深色端(#253494 海軍藍)又跟海面顏色撞色——這正是前面比較表裡標的「YlGnBu 深色端容易跟水體混」那個陷阱,被我親身撞上了。
我沒有因為這個陷阱就放棄 YlGnBu,因為「冷靜客觀」對所得題材還是太合適。比較划算的是直接緩解撞色:把 positron 底圖的水體圖層換成更淺或偏灰的顏色、跟深藍拉開明度差,再幫每個 polygon 加一條極細的白色邊框,深色里之間就不會糊在一起、也不會跟海連成一片。fill-opacity 最後拉到 0.85 才整體看得清楚——但又會壓掉一些路名標籤。這就是真實場景才會踩到的互動:色階 × 基底圖 × opacity 是綁在一起的,不能分開選。
經驗法則:
- 淺底圖(positron / light):fill-opacity 0.8–0.9
- 深底圖(dark matter):用 plasma / magma + opacity 0.7
- 衛星圖底:避免低明度色階,用 viridis 反而清楚
反思
弄了一小時下來,最大的轉變是我終於不再把色階當「挑哪個比較好看」的事。實際做一張會被別人看的地圖才懂,每個選擇都在無聲地對讀者說「這裡多、那裡少、這裡異常、那裡正常」,選錯就是在誤導——它是「不要誤導」的問題,不是美感問題。
至於怎麼選,我的順序是先抄 ColorBrewer 再依場景判斷例外。Cynthia Brewer 花了二十年讓那些色階通過色盲、印刷、感知測試,沒道理自己重調一遍;但「直接抄」也有抄不動的時候——要做連續漸層而不是分級、要配深色模式、或品牌色有指定,這幾種情況就得自己再調,ColorBrewer 只是個夠好的起點,不是終點。
另一個只有真的跑過才知道的事,是「色階 × 基底圖 × opacity」綁在一起。設計稿上看好好的色階,疊到真實地圖才發現淺色端融進底圖、深色端撞水面,一定要在實際的 MapLibre / Leaflet 環境跑一次,光看 ColorBrewer 預覽不準。長尾資料則別用等距分級——台灣的所得、房價、人口、營收幾乎都是長尾,等距會把它們壓成「一片米色 + 幾個亮點」,用 Jenks 看分布形狀、用分位數看相對排名都比等距好。
後記:這篇最後選的 YlGnBu,後來我又換掉了。做到比較後期、開始認真面對「一般民眾看得懂嗎」這件事,我把色階整個改成 OrRd(暖色、7 級、用對數絕對門檻分桶),原因和我怎麼把「選色階」這一個決策拆成兩個獨立的軸,寫在 為什麼我把所得地圖色階從 viridis 換成 OrRd。所以這篇的結論請當成「當時的我」的選擇,不是定案。
完整的 TaxMap-TW 程式碼會放在 GitHub repo。
接下來其實還沒輪到 PMTiles。底圖本身要選哪家服務,是上色之前就得先決定的事——下一篇會先比 OpenFreeMap、MapTiler、Mapbox 這幾個 Web 地圖底圖服務怎麼選。至於怎麼把這 7,747 個村里 polygon(簡化後 5.6 MB 的 GeoJSON)封裝成單檔 PMTiles、再用 HTTP Range 按需讀取,留到 PMTiles 那篇再細講。