每月多筆週紀錄的寬表,兩行程式變成一張分通路長條圖。本站實跑:寬表 (12, 3) 經 melt 融成長表 (24, 3),barplot 的六根條高度與 groupby(['month','channel']).sum() 一字不差(讀 ax.patches 逐根對帳)——web 450 → 600 → 780、store 780 → 620 → 450。而如果照選項 A 把兩通路相加:月合計 1230/1220/1230,幾乎一條水平線——通路大翻轉被整個藏住。這頁還埋一個版本考點:題目標「seaborn >= 0.12」,考的就是 errorbar=None 這個新 API。
df 每月可能有多列紀錄,欄位為 month、web、store。使用 seaborn 0.12 以上版本時,下列程式產生的圖最合理的描述為何?
long = df.melt(id_vars='month', # month 當「身分欄」原地保留 value_vars=['web', 'store'], # 這兩欄要「融」進同一欄 var_name='channel', value_name='sales') # 舊欄名進 channel、數值進 sales sns.barplot(data=long, x='month', y='sales', # 長條圖:x 軸月份、y 軸銷售 hue='channel', estimator=sum, errorbar=None) # 顏色分通路;同組加總;不畫誤差線
同一堆銷售紀錄有兩種記法。寬表像月報表:每列一筆紀錄、web 與 store 各佔一欄——人看很舒服。長表像收銀機流水帳:每列只記一件事——「幾月、哪個通路、多少錢」,通路變成欄位裡的值而不是欄位的名字。
seaborn 這類繪圖工具偏愛流水帳,理由很實際:你想「顏色照通路分」,長表只要一句 hue='channel'——因為「通路」就躺在一個欄位裡可以指名;寬表的通路藏在欄名裡,工具沒辦法指認。所以這題的兩行程式是視覺化的標準流程:先 melt 融寬成長,再把欄位名交給 x/y/hue。
melt 把 web、store 兩欄融成「channel+sales」兩欄——每列原紀錄拆成兩列(實跑 12 列 → 24 列)。② barplot 是聚合圖:同一「月份 × 通路」組的很多筆資料,會被 estimator 揉成一根條——本題 estimator=sum = 組內加總。③ 題目標「seaborn >= 0.12」不是裝飾——errorbar=None 是 0.12 起的新寫法(舊版寫 ci=None)。點擊後出現漸進式說明:白話說明 → 說清楚一點 → 常見錯誤與考點。
點擊後出現漸進式說明。
五行,一行一行走完。右上角的「看位置」可以把這一行放回完整程式裡看。
long = df.melt(id_vars='month', # 開始融表:month 原地保留、不被融
melt(融化)是寬表變長表的工具。id_vars='month' 指定身分欄:這欄是每筆紀錄的「身分證」,融表過程原地保留、跟著每一列走。結果存進變數 long——待會 barplot 吃的就是它。
value_vars=['web', 'store'], # 這兩欄要被「融」進同一欄
用清單點名 web 與 store——它們的欄名會變成新欄位裡的值、數值會集中到另一欄。這就是融表的本體:原本一列同時記兩個通路,融完每列原紀錄拆成兩列(web 一列、store 一列)——本站實跑:寬表 (12, 3) 融完變長表 (24, 3)。
var_name='channel', value_name='sales') # 舊欄名進 channel、數值進 sales
var_name 幫「裝舊欄名的欄」取名(不寫預設叫 variable)、value_name 幫「裝數值的欄」取名(預設叫 value)。取好名字的長表三欄:month / channel / sales——實跑前四列:(1, web, 120)、(1, web, 95)、(1, web, 130)、(1, web, 105)。取名不是裝飾:下一行 barplot 要用這些名字指認欄位——hue='channel' 能寫,是因為這裡取了 channel。
sns.barplot(data=long, x='month', y='sales', # 資料吃長表;x 軸月份、y 軸銷售
sns.barplot 是聚合長條圖——它不是把 24 筆資料畫 24 個東西,而是把「同一組」的資料揉成一根條。data=long 指定吃剛融好的長表;x='month' 讓月份當類別軸(實跑 x 軸刻度 1、2、3);y='sales' 是要被聚合的數值。這也是選項 B 陣亡的地方:畫每一筆原始資料的是 scatterplot/stripplot,不是 barplot。
hue='channel', estimator=sum, errorbar=None) # 顏色分通路;同組全部加總;不畫誤差線
三個參數三件事。① hue='channel':每個月份底下分兩根不同顏色的條(實跑圖例:web、store)——「分通路」的來源。② estimator=sum:同一「月份 × 通路」組內的多筆紀錄全部加總成條高——不寫的話預設取平均(實跑:450 會變 112.5)。③ errorbar=None:關掉預設的誤差線——這是 seaborn 0.12 起的新寫法,舊版寫 ci=None,題目特別標版本就是在考這個。
本站實跑資料:3 個月、每月 4 筆週紀錄——正是題目說的「每月可能有多列紀錄」。按兩顆按鈕,看同一批資料的兩種長相:
(12, 3) → df.melt(...) → 長表 (24, 3),欄位 month / channel / sales。第一筆紀錄(1 月、web 120、store 200)被拆成兩列:(1, web, 120) 與 (1, store, 200)——通路從「欄名」搬進「欄值」,這正是 seaborn 能用 hue='channel' 指認它的原因。barplot 拿到長表後做的事,等價於一句 pandas:long.groupby(['month','channel'])['sales'].sum()。本站直接讀圖上六根條的高度(ax.patches)跟 groupby 對帳:
estimator=sum 拿掉:六根高度變成組內平均 [112.5, 112.5, 150, 155, 195, 195],而且多出 6 條誤差線(errorbar 預設開啟)——「加總」與「不畫誤差線」都是題目親手指定的。加碼:estimator 寫 sum、'sum'、np.sum 三種形式實跑同值。選項 A 說「把 web 與 store 相加成單一折線,不區分通路」。先不論折線與長條之別——把通路加起來,這份資料會發生一件慘案:
題目特別寫「使用 seaborn 0.12 以上版本」——不是廢話,是API 分水嶺。三件展品(本站 seaborn 0.13.2 實跑):
ci=None;0.12 起改成 errorbar=None(errorbar 參數還能指定 'ci'、'sd'、'se' 等種類)。本站 0.13.2 實跑舊寫法:圖照畫,但噴 FutureWarning:The `ci` parameter is deprecated. Use `errorbar=None` for the same effect.——「還能跑但已被判死緩」。題目把版本寫進題幹,就是提示你 errorbar=None 是合法的新 API、不是錯字。剩下兩個選項用實跑數字補刀,順便學一招對帳技巧:
| 選項的主張 | 那其實是誰的工作 | 本站實跑補刀 |
|---|---|---|
| B:每筆原始資料的散佈圖 | scatterplot/stripplot | barplot 是聚合圖:24 筆進去、只出 6 根條;且 hue='channel' 顏色代表通路,不是月份 |
| D:相關係數矩陣+熱圖 | df[['web','store']].corr() + sns.heatmap | 實跑 r = -0.8752(web 升 store 跌,強負相關)——有趣歸有趣,但那是另一段程式 |
對帳技巧(值得帶走):不確定 barplot 在畫什麼時,自己先算一次等價的 pandas——long.groupby(['month','channel'])['sales'].sum()——再跟圖對。本站更進一步用 ax.patches 把六根條的高度直接讀出來比對:逐根一字不差。圖不再是黑盒子,每根條都有一句 groupby 當它的存在證明。
兩刀斃命:① barplot 畫長條不是折線(折線是 lineplot);② hue='channel' 明明白白區分通路——每月兩根不同色的條(實跑圖例:web、store)。加映慘案:真把兩通路相加,月合計 1230/1220/1230 幾乎持平——web 翻 1.73 倍、store 掉 42% 的大翻轉整個被藏住——這張圖存在的意義就是不讓你這樣看。
barplot 是聚合圖:實跑 24 筆原始資料進去,圖上只有 6 根條——每根是一組「月份 × 通路」被 estimator 揉出來的總和;想看每一筆原始資料,那是 scatterplot/stripplot 的工作。第二刀:顏色由 hue='channel' 決定——代表通路,不是月份;月份在 x 軸。
每個字都對得上實跑:「先轉成長表」= melt 把 (12, 3) 融成 (24, 3)、通路從欄名搬進 channel 欄;「以月份與通路分組加總」= estimator=sum——六根條高度與 groupby(['month','channel']).sum() 一字不差(450/780、600/620、780/450);「分通路長條」= hue='channel' 每月兩根分色條。melt 融寬成長、estimator 分組加總、hue 分通路——一句話就是這一題。
相關係數矩陣要寫 df[['web','store']].corr()、熱圖要 sns.heatmap——題目程式裡一個都沒出現。本站順手實跑給你看:r = -0.8752(web 節節上升、store 節節下滑,強負相關)——這個數字很有戲,但它不是這兩行程式的產出。考場判法:選項描述的函式根本沒被呼叫,直接出局。
再看一次同兩行程式。這次你知道每個參數各管哪件事了:melt 融寬成長、hue 分通路、estimator=sum 分組加總、errorbar=None 是 0.12 的新 API。
long = df.melt(id_vars='month', value_vars=['web', 'store'], ...) # (12,3) → (24,3) sns.barplot(..., hue='channel', estimator=sum, errorbar=None) # 六根條 = groupby sum
八題,全部都是本題的延伸。答錯會直接告訴你錯在哪。
melt 把寬表融成長表(month 身分欄、web/store 融進 channel/sales);後兩行 barplot 用長表畫分通路長條。groupby(['month','channel'])['sales'].sum() 一字不差——web 450→600→780、store 780→620→450。ci=None、0.12 起改 errorbar=None——本站 0.13.2 實跑舊寫法仍可跑但噴 FutureWarning;題幹標「>= 0.12」就是在提示新 API。df[['web','store']].corr()(實跑 r=-0.8752)+sns.heatmap——題目程式一個都沒呼叫。「melt 長表 + seaborn 分組圖」是中級科目二大數據處理分析與應用的高頻考點,常跟 groupby、樞紐分析(pivot)互相搭配出題。最常見的六種問法:① 給 melt+barplot 程式問畫出什麼(本題——認出融表、分組、聚合三步);② 問 melt 各參數的角色(id_vars 身分欄/value_vars 融誰/var_name、value_name 取名);③ 問 melt 前後的形狀(每列拆成 value_vars 個數的列:12 × 2 → 24);④ 問 barplot 的 estimator 預設與替代(預設 mean;sum、median、np.max 都能換);⑤ 問 hue 的作用(同一 x 底下按類別分色分條);⑥ 問新舊版誤差線寫法(ci=None → errorbar=None,0.12 分水嶺)。一句口訣:melt 融寬成長、hue 分通路、estimator=sum 分組加總。
melt 把「多個數值欄」融成「一欄類別+一欄數值」。本題 value_vars 點名 web 與 store 兩欄:每一列原紀錄裡,web 的值搬出來配上標籤 'web' 成一列、store 的值配上 'store' 再成一列——所以列數 × 2(value_vars 有幾欄就 × 幾)。實跑:(12, 3) → (24, 3),長表欄位 month/channel/sales,前四列 (1, web, 120)、(1, web, 95)、(1, web, 130)、(1, web, 105)。id_vars 指定的 month 是身分欄,原地保留跟著每列走。記法:寬表把類別藏在欄名裡、長表把類別放進欄值裡——工具要「按類別分色」,只認得欄值。
因為 seaborn 的文法是「欄名 → 圖的角色」:x='month'、y='sales'、hue='channel'——每個視覺角色指名一個欄位。長表裡「通路」是一個真正的欄位,所以能被 hue 指認;寬表的通路藏在欄名(web、store)裡,hue 沒有東西可指。這套「一列一觀測、一欄一變數」的規矩叫 tidy data(整潔資料)——pandas 的 melt/pivot 就是在寬與長之間搬家的工具。考點延伸:seaborn 大多數函式(lineplot、boxplot、violinplot)都吃同一套長表文法——學會一次、全家通用。
本站直接對帳給你看:把題目的圖畫出來、用 ax.patches 讀出六根條的高度,排序後是 [450, 450, 600, 620, 780, 780];再算 long.groupby(['month','channel'])['sales'].sum()——1月 web 450/store 780、2月 600/620、3月 780/450——逐根一字不差。所以 barplot+estimator 可以理解成「幫你 groupby 完直接畫」的縮寫。加碼實跑:estimator 寫 sum(Python 內建函式)、'sum'(字串)、np.sum 三種形式結果同值——0.12 起官方推薦字串寫法。
預設 estimator 是平均(mean):實跑六根高度變 [112.5, 112.5, 150, 155, 195, 195]——「1 月 web 平均每筆 112.5」和「1 月 web 總共 450」是兩張完全不同的圖,讀圖前要先問「這根條是加總還是平均」。誤差線(errorbar)預設也會開:seaborn 會用 bootstrap 估計平均的 95% 信賴區間、在條頂畫一小段線(實跑:預設版多出 6 條誤差線物件)——它表達「這個平均有多不確定」。題目 errorbar=None 把它關掉,因為加總是精確計算、畫不確定性沒有意義。
同一件事的兩個年代。0.12 之前:ci 參數管信賴區間,關掉寫 ci=None。0.12 起:改成統一的 errorbar 參數——None 關閉、'ci' 信賴區間、'sd' 標準差、'se' 標準誤,表達力更完整。本站 seaborn 0.13.2 實跑舊寫法 ci=None:圖照畫、結果相同,但噴 FutureWarning:The `ci` parameter is deprecated. Use `errorbar=None` for the same effect.——「還能跑,但已進淘汰名單」。題目把「seaborn >= 0.12」寫進題幹,就是要你認得 errorbar=None 是合法新寫法、不是錯字——版本敏感的 API 是科目二近年愛考的題型。
兩條路。路一(pandas 先算):df.groupby('month')[['web','store']].sum().sum(axis=1) 或對長表 long.groupby('month')['sales'].sum()——實跑 1230/1220/1230。路二(圖上直接畫):把 hue 拿掉、estimator=sum 保留——每月一根總和條。但要記得本頁 04 節的教訓:這份資料的月合計幾乎持平,總量視角會把「web 翻 1.73 倍、store 掉 42%」的結構翻轉整個藏住——合計圖適合報總量,結構變化要靠 hue 分組(或堆疊圖)才看得見。選 A 的人不只選錯圖形,更選錯了「該問資料什麼問題」。
兩條線。「圖背後是聚合」:第 13 頁(macro/weighted F1)教「一個數字背後是哪種平均」,本頁教「一根長條背後是哪句 groupby」——科目二考視覺化,考的都是「程式 → 數字 → 圖」的對應,不是美感。「被合計遮住的結構」:第 3 頁 accuracy 95% 假象(全猜多數類)、第 13 頁 weighted 被多數類拉高、本頁月合計 1230 持平藏住通路翻轉——同一個病:把不同組揉成一個數,組間的故事就死了;同一帖藥:分組看(hue、macro、逐類報告)。把這條線講給自己聽一遍,三頁一起帶走。