戰(zhàn))
做數(shù)據(jù)分析這一行箱線圖是逃不掉的基礎(chǔ)圖。我最早用它的時(shí)候matplotlib 默認(rèn)畫出來(lái)的箱體就是個(gè)白色帶黑邊的矩形放在報(bào)告里總覺得單調(diào)尤其是要對(duì)比三五個(gè)業(yè)務(wù)分組時(shí)光靠位置和形狀去區(qū)分眼睛看久了容易花。后來(lái)開始研究怎么給箱線圖自定義填充顏色才發(fā)現(xiàn)這里面的門道比我預(yù)想的多默認(rèn)情況下 matplotlib 根本沒打算讓你直接填色需要先打開一個(gè)叫patch_artist的開關(guān)再配合boxprops參數(shù)才能控制箱體的facecolor。如果你已經(jīng)在用 Python 做數(shù)據(jù)分析和可視化想畫一張既專業(yè)又有辨識(shí)度的彩色箱線圖這篇文章就是圍繞這個(gè)目標(biāo)展開的。我會(huì)從箱線圖的基本原理講起再用可復(fù)現(xiàn)的代碼一步步演示如何實(shí)現(xiàn)自定義填充顏色、按分組上色、結(jié)合 seaborn 快速配色最后把我在實(shí)際項(xiàng)目中踩過(guò)的坑和排查方法一并整理出來(lái)。1. 箱線圖到底在展示什么想給箱線圖換顏色先得搞清楚你畫的這個(gè)圖是什么。箱線圖不是簡(jiǎn)單的“一個(gè)盒子加兩根須”它把一組連續(xù)型數(shù)值數(shù)據(jù)的分布情況濃縮成了幾個(gè)關(guān)鍵統(tǒng)計(jì)量。理解這些統(tǒng)計(jì)量你才能在上色之后依然讀出圖表里的信息。1.1 五個(gè)統(tǒng)計(jì)量構(gòu)成一份標(biāo)準(zhǔn)的箱線圖會(huì)呈現(xiàn)五個(gè)關(guān)鍵位置通常稱為五數(shù)概括。箱體上邊緣和下邊緣分別是上四分位數(shù)Q3和下四分位數(shù)Q1箱體中間那條線是中位數(shù)Q2從箱體延伸出去的兩條須線則指向非異常范圍內(nèi)的最大值和最小值須線之外單獨(dú)畫的點(diǎn)就是被識(shí)別為離群值的數(shù)據(jù)點(diǎn)。我用一個(gè)具體數(shù)字舉例。假設(shè)某門店一周的日銷售額是 80、85、88、90、92、95、200 這 7 個(gè)數(shù)據(jù)排序后中位數(shù)是 90Q1 是 85Q3 是 95IQR 等于 10。按照常見的 1.5 倍 IQR 規(guī)則下邊界是 85 - 15 70上邊界是 95 15 110那么 200 就會(huì)被標(biāo)成異常點(diǎn)須線最右端只畫到 95。箱體的高度也就是 Q1 到 Q3 這段區(qū)間集中了中間 50% 的數(shù)據(jù)這比單純報(bào)一個(gè)平均值更能反映數(shù)據(jù)是否穩(wěn)定。實(shí)際工作中我經(jīng)常用箱線圖做三件事第一快速判斷一組數(shù)據(jù)是左偏還是右偏第二比較不同分組之間的中位數(shù)和離散程度第三找出異常值并跟進(jìn)原因。顏色本身不改變這些統(tǒng)計(jì)結(jié)果但合理的配色能讓圖表更快被讀懂尤其是當(dāng)你要在匯報(bào)時(shí)同時(shí)展示七八個(gè)分組時(shí)。1.2 為什么需要給箱線上色很多人一開始只畫單組箱線圖用默認(rèn)顏色沒問(wèn)題。但一旦涉及多組對(duì)比比如不同地區(qū)、不同產(chǎn)品線、不同月份光靠黑白線條就很難一眼看出哪個(gè)組的波動(dòng)更大。給箱體填充不同的顏色本質(zhì)上是把“分組”這個(gè)額外維度用視覺通道表達(dá)出來(lái)觀眾不用花時(shí)間對(duì)比每根須線的位置直接看顏色就能鎖定關(guān)注對(duì)象。還有個(gè)容易被忽視的使用場(chǎng)景品牌一致性。給外部客戶做交付報(bào)告時(shí)如果整份報(bào)告的主色是某一種品牌藍(lán)那么圖表卻還是 matplotlib 默認(rèn)的白底黑邊視覺上會(huì)非常割裂。自定義填充顏色后箱體顏色能和報(bào)告主視覺保持一致整體專業(yè)感立刻提升。另外顏色還能用于突出“異常組”。我在一次質(zhì)量分析中把合格率偏低的產(chǎn)線標(biāo)成紅色其余產(chǎn)線用灰色匯報(bào)時(shí)所有人的注意力第一時(shí)間就集中到了紅色箱體上。當(dāng)然使用顏色也要克制如果每組的顏色都特別鮮艷圖就會(huì)變得刺眼信息層次反而被削弱。后面我會(huì)專門講配色經(jīng)驗(yàn)這里先明確一個(gè)結(jié)論上色不是隨便挑幾個(gè)好看的顏色而是為了讓數(shù)據(jù)對(duì)比更清晰、報(bào)告更統(tǒng)一、重點(diǎn)更突出。2. 環(huán)境準(zhǔn)備與數(shù)據(jù)樣例開始畫圖之前先把運(yùn)行環(huán)境準(zhǔn)備好。這里我用的是最常見的 Python 數(shù)據(jù)科學(xué)組合matplotlib 負(fù)責(zé)繪圖numpy 負(fù)責(zé)生成模擬數(shù)據(jù)seaborn 作為可選的美化工具。如果你用的是 Anaconda 或者 VS Code 配好了 Python 環(huán)境下面這幾行命令基本不會(huì)遇到問(wèn)題。2.1 環(huán)境準(zhǔn)備推薦使用 Python 3.8 以上版本matplotlib 用 3.5 以上版本因?yàn)榈桶姹驹赽oxplot的參數(shù)細(xì)節(jié)上有些小差異比如某些返回對(duì)象結(jié)構(gòu)不一樣。安裝很簡(jiǎn)單pip install matplotlib numpy seaborn如果網(wǎng)絡(luò)環(huán)境比較慢也可以指定國(guó)內(nèi)鏡像源安裝。裝好之后在代碼開頭統(tǒng)一導(dǎo)入import matplotlib.pyplot as plt import numpy as np import seaborn as snsseaborn 這個(gè)庫(kù)其實(shí)不是必須的它是在 matplotlib 之上做了一層封裝讓我能用更少的代碼畫出樣式更精致的圖。但你如果只為了實(shí)現(xiàn)“自定義填充顏色”只用 matplotlib 也可以后面我會(huì)兩種方式都演示。2.2 構(gòu)造演示數(shù)據(jù)直接拿真實(shí)數(shù)據(jù)不方便復(fù)現(xiàn)所以我用 numpy 生成三組模擬數(shù)據(jù)模擬三個(gè)門店的日銷售額情況。為了每次運(yùn)行結(jié)果一致我設(shè)置了隨機(jī)種子。rng np.random.default_rng(42) store_a rng.normal(loc90, scale8, size120) store_b rng.normal(loc85, scale15, size120) store_c rng.normal(loc95, scale5, size120) # 手動(dòng)加入幾個(gè)離群點(diǎn)模擬真實(shí)業(yè)務(wù)中的突發(fā)情況 store_b np.append(store_b, [150, 160]) store_c np.append(store_c, [60]) data [store_a, store_b, store_c] labels [門店A, 門店B, 門店C]這三組數(shù)據(jù)的設(shè)計(jì)思路是門店A 平均水平中等、波動(dòng)適中門店B 平均水平略低但波動(dòng)很大還帶兩個(gè)高值異常點(diǎn)門店C 平均水平最高、最穩(wěn)定但我也加了一個(gè)偏低異常值。這樣畫出來(lái)之后每組箱體之間能明顯看出差異顏色對(duì)比的效果也更直觀。3. 用 matplotlib 實(shí)現(xiàn)自定義填充顏色matplotlib 的boxplot函數(shù)是繪制箱線圖的核心入口。這一節(jié)我會(huì)從最樸素的畫法開始一步步把顏色加進(jìn)去同時(shí)解釋每個(gè)參數(shù)背后的原理。3.1 最基礎(chǔ)的箱線圖先畫一個(gè)完全默認(rèn)的箱線圖看看 matplotlib 默認(rèn)給了我們什么。plt.figure(figsize(8, 6)) bp plt.boxplot(data, tick_labelslabels, patch_artistFalse) plt.title(默認(rèn)樣式的箱線圖) plt.ylabel(銷售額) plt.grid(axisy, linestyle--, alpha0.6) plt.show()運(yùn)行這段代碼你會(huì)看到三個(gè)白底黑邊的箱體。很多人第一次看到patch_artistFalse會(huì)很疑惑為什么默認(rèn)是 False這就涉及 matplotlib 的內(nèi)部繪圖機(jī)制了。在默認(rèn)情況下boxplot里的箱體是用線條對(duì)象Line2D畫出來(lái)的它只有邊框顏色沒有真正意義上的填充區(qū)域所以無(wú)論你怎么設(shè)置facecolor都不會(huì)生效。只有把patch_artist改成True箱體才會(huì)被創(chuàng)建成可填充的Patch對(duì)象這時(shí)候你才有資格去設(shè)置填充顏色。我最初就是沒搞懂這一點(diǎn)直接傳了boxprops{facecolor: red}結(jié)果運(yùn)行完什么變化都沒有還以為是自己參數(shù)寫錯(cuò)了。后來(lái)翻源碼才發(fā)現(xiàn)patch_artist這個(gè)開關(guān)是前提條件。你記住一句話就行不給patch_artistTrue一切facecolor都是白給。3.2 用 patch_artist 打開填充開關(guān)把patch_artist打開之后箱體從線條對(duì)象變成了可填充圖形對(duì)象boxprops里的facecolor就能真正控制箱體內(nèi)部的顏色了。最簡(jiǎn)單的寫法如下plt.figure(figsize(8, 6)) bp plt.boxplot( data, tick_labelslabels, patch_artistTrue, # 關(guān)鍵開關(guān) boxpropsdict( facecolor#4C72B0, # 箱體填充顏色 edgecolorblack, # 邊框顏色 linewidth1.5 ) ) plt.grid(axisy, linestyle--, alpha0.6) plt.show()這次三個(gè)箱體都變成了藍(lán)色邊框是黑色。這里顏色我用了十六進(jìn)制字符串#4C72B0它是 seaborn 常用配色中的一種藍(lán)色偏沉穩(wěn)適合商業(yè)場(chǎng)景。你也可以用內(nèi)置顏色名稱比如red、steelblue、skyblue或者 RGB 三元組(0.3, 0.45, 0.69)。有一點(diǎn)要提醒如果你在同一個(gè)boxprops里既設(shè)置了facecolor又設(shè)置了colorcolor會(huì)覆蓋邊框、須線等多個(gè)元素的顏色有時(shí)候會(huì)出現(xiàn)邊框也跟著變藍(lán)的情況。所以更穩(wěn)妥的做法是分開控制facecolor管填充edgecolor管邊框。3.3 boxprops 參數(shù)詳解想要畫出好看且信息完整的箱線圖只調(diào)facecolor是不夠的。boxprops作為一個(gè)字典可以接收大量線條和填充相關(guān)屬性我按實(shí)際使用頻率整理了一張表格參數(shù)作用常用取值示例facecolor箱體內(nèi)部填充色#4C72B0,(0.2, 0.4, 0.6)edgecolor箱體邊框顏色black,#333333linewidth邊框線粗細(xì)1.2,2linestyle邊框線型-,--alpha整體透明度0 到 10.6,0.85hatch填充紋理打印時(shí)防顏色丟失/,o,xxzorder圖層層級(jí)控制誰(shuí)蓋住誰(shuí)2,3hatch這個(gè)參數(shù)很多人不知道但我強(qiáng)烈建議你做打印材料時(shí)考慮它。因?yàn)橛行┐蛴C(jī)默認(rèn)是黑白模式或者對(duì)方會(huì)把報(bào)告打印出來(lái)看這時(shí)候僅靠顏色區(qū)分分組就沒用了。加上hatch/后箱體內(nèi)部會(huì)有斜線紋理黑白打印也能清晰看出是不同分組。透明度alpha也是一個(gè)需要謹(jǐn)慎設(shè)置的參數(shù)。理論上透明色能減輕視覺壓迫感但如果alpha太低比如 0.2箱體顏色會(huì)變得很淺疊加在格子背景上可能看不太清。我自己的習(xí)慣是商業(yè)正式圖表降到 0.7 左右既保留顏色質(zhì)感又不至于太刺眼。4. 按分組給不同箱體上色上面演示的是所有箱體統(tǒng)一用一種顏色。但實(shí)際分析中更多情況是每個(gè)分組需要各自的顏色這樣可以在同一張圖里清晰地對(duì)照多個(gè)門店、多個(gè)時(shí)段。這一節(jié)我會(huì)介紹兩種方式手動(dòng)指定顏色列表以及用 colormap 自動(dòng)映射。4.1 手動(dòng)指定多組顏色當(dāng)分組數(shù)量不多時(shí)3 到 6 組手動(dòng)指定顏色是最直接可控的。核心思路是先把boxplot返回的結(jié)果保存下來(lái)然后遍歷每個(gè)箱體逐個(gè)設(shè)置set_facecolor。boxplot的返回值是一個(gè)字典里面的boxes鍵對(duì)應(yīng)所有箱體對(duì)象的列表。colors [#4C72B0, #DD8452, #55A868] plt.figure(figsize(8, 6)) bp plt.boxplot( data, tick_labelslabels, patch_artistTrue, medianpropsdict(colorblack, linewidth2) ) for patch, color in zip(bp[boxes], colors): patch.set_facecolor(color) patch.set_edgecolor(#333333) patch.set_linewidth(1.2) plt.title(每個(gè)分組使用不同填充顏色) plt.ylabel(銷售額) plt.grid(axisy, linestyle--, alpha0.6) plt.show()這里我用zip把箱體對(duì)象和顏色列表一一對(duì)應(yīng)順序不會(huì)錯(cuò)。為什么不用索引for i in range(len(bp[boxes]))因?yàn)橐坏┠愫竺嬖鰟h了分組數(shù)據(jù)索引寫法很容易越界或者錯(cuò)位而zip會(huì)安全地按最短列表停止至少不會(huì)報(bào)錯(cuò)這個(gè)習(xí)慣我在很多循環(huán)場(chǎng)景里都保持下來(lái)了。顏色列表的選擇上要注意組與組之間的亮度差。比如三組分別用深藍(lán)、橙、綠對(duì)比度足夠但如果你選了淺藍(lán)、淺灰、淺粉它們之間的界限會(huì)比較模糊圖例一多就分不清了。4.2 使用 colormap 自動(dòng)映射如果分組數(shù)量很多比如 8 個(gè)、10 個(gè)甚至更多手動(dòng)寫顏色列表就很痛苦也容易讓顏色之間缺乏遞進(jìn)感。這時(shí)候可以用 matplotlib 自帶的 colormap 來(lái)生成一組顏色。colormap 是一系列連續(xù)變化的顏色常見的有viridis、plasma、coolwarm等。import matplotlib.colors as mcolors n_groups len(data) cmap plt.get_cmap(viridis) norm mcolors.Normalize(vmin0, vmaxn_groups - 1) colors [cmap(norm(i)) for i in range(n_groups)] plt.figure(figsize(8, 6)) bp plt.boxplot( data, tick_labelslabels, patch_artistTrue, medianpropsdict(colorwhite, linewidth2) ) for patch, color in zip(bp[boxes], colors): patch.set_facecolor(color) patch.set_edgecolor(#333333) plt.title(使用 viridis 自動(dòng)映射顏色) plt.ylabel(銷售額) plt.grid(axisy, linestyle--, alpha0.6) plt.show()這段代碼的思路是先用Normalize把組序號(hào) 0 到 2 映射到 0 到 1 的范圍再用cmap(norm(i))取出對(duì)應(yīng)顏色。這樣就算你有 20 組數(shù)據(jù)也不需要手寫 20 個(gè)顏色值。用 colormap 時(shí)有個(gè)細(xì)節(jié)viridis這類感知均勻的色圖顏色從深紫到明黃過(guò)渡雖然好看但它偏向表達(dá)“從小到大”的數(shù)值等級(jí)。如果你的分組本身沒有大小順序只是并列關(guān)系用連續(xù)的 colormap 反而可能讓讀者誤以為組 A 比組 C 數(shù)值更大。遇到這種并列分組我建議還是回到 4.1 的離散顏色方案或者選擇色相跨度大一些的定性色圖比如tab10。4.3 同時(shí)自定義中位數(shù)線、異常點(diǎn)顏色箱線圖的信息不只是箱體中位數(shù)線、異常點(diǎn)、須線都在傳遞信息。給箱體上色以后千萬(wàn)注意線條顏色和箱體填充色之間的關(guān)系。如果中位數(shù)線和箱體顏色同為深色中位數(shù)可能就看不清了整個(gè)箱體的高度信息會(huì)丟失。我常用的做法是把中位數(shù)線設(shè)置成與箱體對(duì)比明顯的顏色。顏色較淺的箱體配黑色中位數(shù)線顏色較深的箱體配白色中位數(shù)線。同時(shí)異常點(diǎn)一般用醒目的顏色標(biāo)出因?yàn)楫惓V低菢I(yè)務(wù)上需要重點(diǎn)關(guān)注的數(shù)據(jù)。下面這段代碼是一次性調(diào)整所有元素的示例plt.figure(figsize(9, 6)) bp plt.boxplot( data, tick_labelslabels, patch_artistTrue, boxpropsdict(facecolor#4C72B0, edgecolor#2A3F5F, linewidth1.2), medianpropsdict(colorwhite, linewidth2.5), whiskerpropsdict(color#2A3F5F, linewidth1.5, linestyle-), cappropsdict(color#2A3F5F, linewidth1.5), flierpropsdict(markero, markerfacecolor#C44E52, markeredgecolor#C44E52, markersize6) ) plt.grid(axisy, linestyle--, alpha0.6) plt.show()各參數(shù)的作用如下medianprops中位數(shù)線的樣式我建議加大線寬讓中位數(shù)位置更突出。whiskerprops須線的樣式通常和箱體邊框保持一致顏色。capprops須線末端的橫線用來(lái)標(biāo)記非異常范圍內(nèi)的最小值與最大值。flierprops異常點(diǎn)的樣式包括點(diǎn)形狀、填充色、邊框色、尺寸。這一套設(shè)置下來(lái)圖表的層次就非常清楚了白色中位數(shù)線在藍(lán)色箱體上很明顯紅色異常點(diǎn)不會(huì)被誤認(rèn)為正常數(shù)據(jù)。顏色之間的對(duì)比度不僅僅是審美問(wèn)題更是信息傳遞問(wèn)題。5. 用 seaborn 一行實(shí)現(xiàn)帶填充色的箱線圖如果你不想花太多時(shí)間調(diào) matplotlib 的參數(shù)seaborn 是一個(gè)很好的替代方案。它上層封裝了boxplot對(duì)輸入數(shù)據(jù)的格式更友好尤其是當(dāng)你手上已經(jīng)是一個(gè) DataFrame 時(shí)一行代碼就能畫出帶填充顏色的箱線圖。5.1 palette 參數(shù)介紹假設(shè)數(shù)據(jù)已經(jīng)整理成了長(zhǎng)格式的 DataFrame包含一個(gè)分組列和一個(gè)數(shù)值列。下面構(gòu)造一份import pandas as pd df pd.DataFrame({ 門店: np.repeat(labels, 120), 銷售額: np.concatenate([store_a[:120], store_b[:120], store_c[:120]]) }) sns.boxplot(datadf, x門店, y銷售額, paletteSet2) plt.title(seaborn 默認(rèn) palette 配色箱線圖) plt.show()palette參數(shù)可以直接傳字符串比如Set2、husl、pastel也可以傳一個(gè)顏色列表順序?qū)?yīng)分組順序最靈活的是傳一個(gè)字典鍵是分組名稱值是顏色。color_map { 門店A: #4C72B0, 門店B: #DD8452, 門店C: #55A868 } sns.boxplot(datadf, x門店, y銷售額, palettecolor_map) plt.title(seaborn 使用顏色字典精確控制每個(gè)分組) plt.show()用字典最大的好處是即使 DataFrame 里分組的順序發(fā)生變化顏色和分組之間的綁定關(guān)系也不會(huì)錯(cuò)亂。這在數(shù)據(jù)源字段順序不穩(wěn)定時(shí)非常有用。seaborn 默認(rèn)的箱體邊框是白色這在深色背景圖或者白色背景上都很干凈但如果你覺得白色邊框太輕可以在boxplot中加上linewidth2讓邊框更明顯。5.2 與 matplotlib 混排時(shí)的回填seaborn 雖然方便但它返回的是一個(gè) matplotlib 的Axes對(duì)象所以你在它之后依然可以用 plt 系列函數(shù)去微調(diào)標(biāo)題、坐標(biāo)軸、網(wǎng)格線。也可以利用返回的Axes拿到花了幾分鐘才調(diào)好的圖形細(xì)節(jié)。有一個(gè)常見需求是先用 seaborn 畫箱線圖再往同一張圖上疊加其他統(tǒng)計(jì)信息比如把每個(gè)組的均值點(diǎn)畫上去。代碼可以這樣寫ax sns.boxplot(datadf, x門店, y銷售額, paletteSet2) means df.groupby(門店)[銷售額].mean() for idx, (store, mean_val) in enumerate(means.items()): ax.scatter(idx, mean_val, markerD, s60, colorblack, zorder3) plt.title(箱線圖上疊加均值點(diǎn)) plt.show()這里的核心思路是seaborn 和 matplotlib 本來(lái)就共用底層對(duì)象不需要在兩種繪圖風(fēng)格之間做切換你可以把a(bǔ)x.scatter看成是對(duì)同一個(gè)坐標(biāo)畫布追加元素。我個(gè)人偏愛這種“先用 seaborn 快速出圖再用 matplotlib 精細(xì)微調(diào)”的組合方式效率高也保留了足夠的靈活性。6. 常見問(wèn)題與排查實(shí)錄這部分是實(shí)打?qū)嵉慕?jīng)驗(yàn)復(fù)盤。我見過(guò)身邊不少同事在給箱線圖上色時(shí)踩坑有些問(wèn)題排查起來(lái)很費(fèi)時(shí)間但其實(shí)原因就那么幾個(gè)。我按常見程度從高到低列出來(lái)并附上排查思路。6.1 設(shè)置了 facecolor 卻不變色這是最經(jīng)典的問(wèn)題我在前面已經(jīng)提到過(guò)?,F(xiàn)象是boxprops{facecolor: red}寫進(jìn)去了圖還是白底的。原因幾乎都是沒有設(shè)置patch_artistTrue。排查步驟很簡(jiǎn)單第一確認(rèn)boxplot調(diào)用里有沒有patch_artistTrue第二確認(rèn)顏色是寫在boxprops里而不是直接寫在boxplot的最外層第三檢查顏色值本身是否合法比如十六進(jìn)制字符串有沒有寫錯(cuò)RGB 三元組是否在 0 到 1 范圍內(nèi)。如果你用bp[boxes][0].get_facecolor()拿到的是白色或透明色的 RGBA 值那說(shuō)明問(wèn)題還是出在patch_artist上。這個(gè)函數(shù)是我調(diào)試時(shí)很常用的“體檢工具”。6.2 填充色蓋住了中位數(shù)線設(shè)置成深色填充后中位數(shù)線也設(shè)置成深色比如黑色結(jié)果中位數(shù)線和箱體邊緣融為一體看不出中位數(shù)在哪。嚴(yán)格說(shuō)不是蓋住而是對(duì)比度不夠。解決方式有兩種一是把中位數(shù)線改成白色或淺色線寬加大到 2.5這適合深色箱體二是降低箱體填充色的透明度比如facecolor(0.2, 0.4, 0.6, 0.5)讓深色背景上的深色線條透出來(lái)。前一種方式更干凈我推薦優(yōu)先調(diào)整medianprops。還有一個(gè)容易被忽略的地方當(dāng)你用patch.set_facecolor(color)循環(huán)上色時(shí)medianprops必須在boxplot初始化時(shí)就傳好因?yàn)橹形粩?shù)線的對(duì)象已經(jīng)生成之后你依然可以通過(guò)bp[medians]來(lái)改但直接在初始化時(shí)設(shè)置最省事。6.3 顏色列表與分組數(shù)量不匹配手動(dòng)傳顏色列表時(shí)最常見的問(wèn)題是顏色數(shù)量少于分組數(shù)量。用zip(bp[boxes], colors)時(shí)不會(huì)報(bào)錯(cuò)但最后幾組箱體會(huì)保持默認(rèn)顏色或上次循環(huán)留下的顏色很容易看不出問(wèn)題。如果顏色數(shù)量多于分組數(shù)量多出來(lái)的顏色靜靜躺在列表里沒人用同樣不報(bào)錯(cuò)。這兩種情況都屬于“災(zāi)難性失敗的很安靜”的類型讓人防不勝防。我的排查習(xí)慣是在對(duì)每個(gè)箱體上色后馬上用print(len(bp[boxes]), len(colors))確認(rèn)兩邊數(shù)量一致。另外盡量不要在圖例里手寫顏色而是從修改后的圖形對(duì)象中提取真實(shí)的顏色來(lái)生成圖例句柄這樣圖例和實(shí)際圖形永遠(yuǎn)對(duì)得上。6.4 保存圖片后顏色發(fā)灰有時(shí)候屏幕顯示顏色很鮮艷保存成 PNG 后卻發(fā)灰變暗問(wèn)題通常出在配色方案上。某些顏色在純白背景下顯示正常但系統(tǒng)會(huì)自動(dòng)進(jìn)行色彩管理加上字體渲染寬度不同整體觀感會(huì)變化。這類問(wèn)題我一般從兩個(gè)角度解決。第一保存時(shí)提高分辨率用dpi200或 300能讓顏色邊緣更平滑整體畫面更細(xì)膩。第二如果想保持透明背景用plt.savefig(boxplot.png, transparentTrue, dpi300)但要注意透明背景經(jīng)過(guò)某些看圖軟件展示時(shí)會(huì)反白慎用于正式交付場(chǎng)景。保存矢量圖用plt.savefig(boxplot.svg)顏色精度極高放到 PPT 或設(shè)計(jì)軟件里也很穩(wěn)。還有一個(gè)容易被忽略的問(wèn)題是屏幕色域差異。同一份紅色在你的顯示器上看著發(fā)暗在別人筆記本上可能偏亮。所以正式報(bào)告里我通常會(huì)在顏色旁邊加文本標(biāo)簽或圖例文字保證即使色彩有輕微偏差分組信息也不會(huì)丟。7. 幾個(gè)實(shí)際項(xiàng)目里的配色經(jīng)驗(yàn)技術(shù)細(xì)節(jié)聊完了最后分享一些我真實(shí)項(xiàng)目里積累的配色原則。這些經(jīng)驗(yàn)不一定出現(xiàn)在官方文檔里但對(duì)最終成圖質(zhì)量影響很大。先看“品牌色優(yōu)先”。如果這份圖要進(jìn)公司匯報(bào) PPT我建議直接從公司 VI 規(guī)范里取主色和輔助色而不是臨時(shí)去調(diào)色板里選。這樣整份報(bào)告的顏色系統(tǒng)是統(tǒng)一的領(lǐng)導(dǎo)看完不會(huì)覺得圖表是“外包風(fēng)格”。具體操作時(shí)我會(huì)把品牌色整理成顏色列表專門放在一個(gè)配置模塊里所有項(xiàng)目統(tǒng)一調(diào)用。再說(shuō)“顏色數(shù)量控制”。一張箱線圖里顏色數(shù)量我盡量控制在 3 到 6 個(gè)。超過(guò) 7 個(gè)時(shí)即使顏色有差異人眼也很難快速區(qū)分不如改用其他視覺通道比如箱體填充紋理、邊框線型。分組特別多時(shí)我更傾向于拆成小圖或者先用序號(hào)排序再用顏色強(qiáng)調(diào)其中一組?!邦伾姓Z(yǔ)義”。如果你在畫的是業(yè)務(wù)監(jiān)控?cái)?shù)據(jù)盡量讓顏色本身包含含義紅色代表異常綠色代表正常藍(lán)色代表中性。這樣讀者不需要看圖例單憑生活常識(shí)就能快速判斷。比如我在分析門店銷售額時(shí)把異常波動(dòng)的門店用紅色箱體標(biāo)出其余用灰色信息傳達(dá)效率非常高。還要記得“打印與色盲友好”。前面提到的hatch參數(shù)我在做紙質(zhì)匯報(bào)材料時(shí)一定會(huì)加因?yàn)楹诎状蛴o(wú)法顯示色彩區(qū)別。對(duì)于色盲人群紅綠搭配是最容易被混淆的所以我會(huì)改用藍(lán)橙搭配或者同時(shí)用紋理區(qū)分。一次給客戶做交付對(duì)方明確指出小組內(nèi)部有紅綠色盲同事從那以后我的默認(rèn)配色方案就從紅綠改成了藍(lán)橙。最后一個(gè)建議給箱線圖加圖例時(shí)不要直接拿顏色名稱當(dāng)分組名稱。我通常會(huì)把圖例和顏色關(guān)系用一個(gè)小表格放在圖表下方或者直接在箱體上方標(biāo)注文字。顏色作為視覺輔助非常好用但只有配合清晰的文字說(shuō)明圖表才能真正自解釋。我個(gè)人在實(shí)際操作中的體會(huì)是自定義填充顏色的價(jià)值不在于炫技而在于讓數(shù)據(jù)圖表更快地說(shuō)明問(wèn)題。當(dāng)你面對(duì)一張多組對(duì)比的箱線圖顏色能讓觀眾在 10 秒內(nèi)鎖定哪組波動(dòng)最大、哪組中位數(shù)最高這就是這項(xiàng)技能最大的回報(bào)。下次你再畫箱線圖不妨先把默認(rèn)的白色箱體換成有語(yǔ)義的顏色再看一眼圖表是否立刻變得更容易讀了。