把裝在電腦裡的 pandas 套件載入記憶體,之後用 pd 這個短名字叫它。
pandas套件 要先安裝才有(pip install pandas;Anaconda、Google Colab 已經裝好)。全部小寫。
as pd名牌 取綽號。記憶體裡只會有名牌 pd,沒有 pandas 這個名字。
執行後 type(pd) 是 <class 'module'>:pd 指的是一個工具箱(模組物件),不是設計圖。四個步驟(查已載入清單 → 執行 pandas 的程式碼 → 登記 → 貼名牌)和常見錯誤,第 41 題的 逐行解剖 講得很完整。
叫 pandas 的 read_csv 機器打開 orders.csv,把每一行切成格子、判斷每一欄的型別,做成一張表格物件,最後把名牌 df 貼上去。
df =名牌+指派 等號不是「等於」,而是「右邊做完以後,把名牌 df 貼上去」。
pd.read_csv函式 工具箱裡讀 CSV 的機器,小寫開頭,不是設計圖。
'orders.csv'位置引數 檔名字串,對應第 1 個參數 filepath_or_buffer。相對路徑:從「執行程式時所在的資料夾」找。
這一行用到的預設值(其他參數見第 41 題)
參數 預設值(兩版相同) 對本題的意思
sep逗號(簽名顯示 <no_default>) 用逗號切欄
header'infer'第 1 行 OrderID,CustomerID,Category,Revenue 當欄名
index_colNone不拿任何一欄當列標籤,自動編 0~19
dtypeNone(自動判斷)Revenue → int64;其他三欄 → 2.2 object/3.0 str
執行後:
df.shape → (20, 4)
df.index → RangeIndex(start=0, stop=20, step=1)
type(df) → pandas 3.0.6:<class 'pandas.DataFrame'>;pandas 2.2.3:<class 'pandas.core.frame.DataFrame'>
CSV 不在「目前的工作資料夾」:實測 FileNotFoundError: [Errno 2] No such file or directory: 'orders.csv'。把 CSV 和程式放同一個資料夾,並在那個資料夾執行。
以為 df 就是 CSV 檔:df 是記憶體裡的表格物件,後面四個選項怎麼排、怎麼分組,都不會改到硬碟上的檔案。
把整張表格照 Revenue 由大到小重新排一次,拿最上面 3 列(3 張金額最大的收據),再從這 3 列只取 CustomerID 那一欄。
① .sort_values(…) 收據照金額排隊 20 列的新表格
→
② .head(3) 拿最前面 3 列 3 列的新表格
→
③ ['CustomerID'] 只看客戶這一欄 3 格的 Series
10 C06
7 C05
4 C02
Name: CustomerID, dtype: object ← pandas 3.0.6 是 dtype: str
左邊的 10、7、4 是這 3 張收據原本的列標籤 (O011、O008、O005),排序不會重新編號。這一行完全沒有「同一位客戶加起來」的步驟,回答的是「金額最大的 3 張收據是誰的」,不是總貢獻。
叫 df 照 Revenue 這一欄重新排列「每一列」,由大到小。整列一起搬(收據的其他欄跟著走),做成一張新表格交出來,df 自己不變。
.sort_values方法 DataFrame 設計圖上的方法(DataFrame 自己寫了一份,蓋過上層 NDFrame 的)。
'Revenue'位置引數 對應第 1 個參數 by:照哪一欄排。
ascending=False關鍵字引數 「名字=值」的寫法。ascending 是「遞增」,設成 False 就是由大到小。
sort_values 完整參數表
簽名(兩版相同):sort_values(by, *, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False, key=None)。* 後面的參數只能用「名字=值」寫。
參數 選項 A 的值 預設值 白話
by'Revenue' (必填) 照哪一欄排。可以給清單排好幾層,例如 ['CustomerID', 'Revenue']:先照客戶、同一客戶再照金額
axis— 00:上下重排「列」(最常用);1:左右重排「欄」
ascendingFalseTrueTrue 由小到大,False 由大到小。排好幾欄時可以給清單,例如 [True, False]
inplace— FalseFalse:做一張新表格交出來,df 不變。True:直接改 df,回傳 None(實測),後面就不能再接 .head(3)
kind— 'quicksort'用哪種排序演算法:'quicksort'、'mergesort'、'heapsort'、'stable'。只有 'stable'、'mergesort' 保證同分時維持原本的先後
na_position— 'last'空格(NaN)排最後;'first' 排最前。本題沒有空格
ignore_index— FalseFalse:保留原本的列標籤(所以結果左邊是 10、7、4);True:重新編成 0、1、2(實測)
key— None排之前先把這一欄丟進一個函式轉換,照轉換後的值排(例如文字不分大小寫)
交出來的新表格(前 6 列,兩版相同)
OrderID CustomerID Category Revenue
10 O011 C06 Electronics 900
7 O008 C05 Electronics 700
4 O005 C02 Electronics 680
2 O003 C01 Electronics 560
14 O015 C01 Home 540
16 O017 C05 Clothing 520
完整 20 列由大到小依序是 900、700、680、560、540、520、350、320、310、290、280、90、70、65、60、60、55、50、45、40。有兩張 60 元(O002 和 O019)同分:預設的 quicksort 實測把 O019 排在 O002 前面;加上 kind='stable' 則照原本順序,O002 在前。同分的先後只有 stable 有保證,好在它不影響前 3 張。
新手常踩的坑
忘了 ascending=False:變成由小到大,前 3 張是 40、45、50 元,全是 C04 的(實測輸出 12、9、6 三列都是 C04)。
把 False 寫成字串:ascending='False',實測 ValueError: For argument "ascending" expected type bool, received type str.
欄名打錯:df.sort_values('revenue'),實測 KeyError: 'revenue'(大小寫要一字不差)。
以為 df 被排好了:實測 df.sort_values(…) is df 是 False,排完 df 的第一列還是 O001。要留著排好的表格,請另外貼名牌,例如 ranked = df.sort_values(…)。
從上一步排好的新表格,拿最上面 3 列,再做成一張新表格。它只看「位置」,不看數字。
.head方法 寫在 NDFrame(Series 和 DataFrame 共同的上層類別),所以表格和一欄都能用。
head 完整參數表
簽名(兩版相同,DataFrame 和 Series 一樣):head(n=5)
參數 選項 A 的值 預設值 白話
n3 5拿前幾列。不寫就是 5 列。寫負數是「除了最後 |n| 列以外全部」:實測 df.head(-3) 交出 17 列
交出來的新表格(兩版相同):
OrderID CustomerID Category Revenue
10 O011 C06 Electronics 900
7 O008 C05 Electronics 700
4 O005 C02 Electronics 680
head(3) 等於「照位置拿第 0、1、2 列」(和 .iloc[:3] 一樣)。它之所以拿到最大的 3 張,完全是因為上一步已經排好了;head 自己不會挑大小。
把數字寫成字串 head('3'):實測 TypeError: cannot do positional indexing on RangeIndex with these indexers [3] of type str。
以為 head 會挑最大的:沒有排序就 head,只是拿表格最上面幾列(例如 df.head(3) 是 O001、O002、O003)。選項 B 就是踩了這個坑。
拿 'CustomerID' 這把鑰匙跟那張 3 列的表格要一欄,交出一個 3 格的 Series:3 張大收據各是誰的。
[取東西 緊貼在 head(3) 後面:跟那張 3 列的表格要東西(呼叫它的 __getitem__)。
'CustomerID'鑰匙 欄名字串,大小寫要一字不差。
]收尾 交出 Series,名字(Name)就是欄名 CustomerID。
最後交出的 Series:
10 C06
7 C05
4 C02
Name: CustomerID, dtype: object ← pandas 3.0.6:dtype: str
.tolist() → ['C06', 'C05', 'C02']
選項 A 的兩個問題
看的是單張收據,不是每人總額 :C06 那張 900 是全場最大,可是他只買過這一次;總額第 1 的 C03 沒有任何一張進前 3。
同一位客戶可能出現兩次 :如果拿 5 張,head(5)['CustomerID'] 實測是 C06、C05、C02、C01、C01,C01 的 560 和 540 都上榜。「前 3 張收據」不等於「前 3 位客戶」。
兩個實測過的等價寫法:['CustomerID'] 和 head(3) 先後對調,結果相同;df.nlargest(3, 'Revenue')['CustomerID'] 也相同(DataFrame 的 nlargest 必須告訴它照哪一欄,只寫 df.nlargest(3) 會出現 TypeError: DataFrame.nlargest() missing 1 required positional argument: 'columns')。
先照客戶把收據分進 6 個籃子,每個籃子只看金額、只留最大的那一張,做成 6 格的 Series;再照目前的順序拿前 3 格。
① .groupby('CustomerID') 分 6 個籃子
→
② ['Revenue'] 只看金額
→
③ .max() 每籃取最大 6 格 Series
→
④ .head(3) 拿前 3 格
①② 和第 6 行(正解 C)一模一樣,詳細拆解在第 6 行的 ①②,這裡先看它們交出什麼:① 一個 DataFrameGroupBy(分好 6 個籃子,還沒算)、② 一個 SeriesGroupBy(同樣的籃子,只看 Revenue)。B 和 C 只差在第 ③④ 步。
CustomerID
C01 560
C02 680
C03 350
Name: Revenue, dtype: int64
這是 C01、C02、C03 三個人「各自最大的一張」,既不是總額,也不是照金額挑出來的前 3 名。
叫 SeriesGroupBy 物件「每個籃子各找最大值」。這一步才真的計算,做出一個新的 Series:標籤是客戶編號,值是那個人最大的一張收據。
.max方法 寫在 GroupBy 這一層,SeriesGroupBy 是繼承來的(§1 觀念 4)。
max 完整參數表(SeriesGroupBy.max)
簽名:pandas 3.0.6 max(numeric_only=False, min_count=-1, skipna=True, engine=None, engine_kwargs=None);pandas 2.2.3 沒有 skipna。
參數 預設值 白話
numeric_onlyFalse只算數字欄。這裡已經只剩 Revenue 一欄,有沒有設都一樣
min_count-1籃子裡至少要有幾個「不是空格」的值才算,不夠就給 NaN。-1 代表不設門檻
skipnaTrue(只有 pandas 3)跳過空格(NaN)。pandas 2.2.3 寫了這個參數,實測 TypeError: GroupBy.max() got an unexpected keyword argument 'skipna'
engineNone用哪個計算引擎:None 就是用 pandas 內建、編譯好的 cython 程式;'numba' 要另外安裝 numba 套件
engine_kwargsNone給 numba 引擎的額外設定
.max() 交出的完整 Series(兩版相同):
CustomerID
C01 560
C02 680
C03 350
C04 70
C05 700
C06 900
Name: Revenue, dtype: int64
以為 max 就是「最有貢獻」:max 只看每人最大的一張 。C03 每張都不大(最大 350),總額卻是第 1。
以為把 head 換成 nlargest 就對了:….max().nlargest(3) 實測是 C06 900、C05 700、C02 680,答案是「單筆最大的 3 位客戶」,還是不是總貢獻。問題出在 max,不只出在 head。
從 6 格的 Series 拿最前面 3 格。因為 groupby 預設 sort=True 把籃子照客戶編號排好了,最前面 3 格永遠是 C01、C02、C03。
.head(3)方法 和選項 A 的 head 是同一個方法(寫在 NDFrame),參數只有 n,預設 5。這次點前面是 Series。
選項 A 的 head 能拿到大的,是因為前面先排序過;選項 B 前面沒有照金額排序,只有照客戶編號排,所以 head(3) 拿到的是「編號最小的 3 位」。§2 實測過:改成 groupby('CustomerID', sort=False),同一行就變成 C03 350、C04 70、C01 560(照第一次出現的順序)。答案會隨「客戶編號怎麼取、誰先來買」而變,代表它根本沒在回答「誰貢獻最多」。
把 head 當成「前幾名」:head 是「前幾個」(位置),nlargest 才是「前幾名」(數值)。
照客戶把收據分進 6 個籃子,每個籃子只看金額、全部加起來,得到每位客戶的總貢獻;再從這 6 個總額挑出最大的 3 個。
① .groupby('CustomerID') DataFrameGroupBy 6 個籃子,還沒算
→
② ['Revenue'] SeriesGroupBy 只看金額
→
③ .sum() Series:6 個總額 這一步才計算
→
④ .nlargest(3) Series:3 個
步驟 交出的物件 內容 有名牌嗎
① DataFrameGroupBy 照 CustomerID 分組的規則+指回 df 的箭頭 沒有
② SeriesGroupBy 同樣的 6 個籃子,只看 Revenue 沒有
③ Series(6 格) C01 1100、C02 830、C03 1550、C04 385、C05 1220、C06 900 沒有
④ Series(3 格) C03 1550、C05 1220、C01 1100 (選項裡沒有等號,所以也沒有)
想看中間每一步,就自己一步一步貼名牌,最後結果和一行寫完一模一樣 (實測):
g = df.groupby('CustomerID') # ①
sg = g['Revenue'] # ②
s = sg.sum() # ③
top = s.nlargest(3) # ④
print(top)
CustomerID
C03 1550
C05 1220
C01 1100
Name: Revenue, dtype: int64
拆開寫好除錯,每一步都能 print 出來檢查(§6 程式 2 就是這樣寫);寫成一行比較精簡。Python 做的事情一樣。
看動畫 2:記住第幾列、還沒加總(0:28)
叫 df「照 CustomerID 這一欄分組」。它交出一個分組物件:記得分組規則、指著原本的 df,第一次需要時再算出每個籃子是哪幾列。加總、最大值這些都還沒算。
.groupby方法 DataFrame 設計圖自己寫的方法。
'CustomerID'位置引數 對應第 1 個參數 by:照哪一欄分。這一欄每一種不同的值,就是一個籃子(一個「鍵」)。
groupby 完整參數表
簽名:pandas 3.0.6 groupby(by=None, level=None, *, as_index=True, sort=True, group_keys=True, observed=True, dropna=True);pandas 2.2.3 groupby(by=None, axis=<no_default>, level=None, as_index=True, sort=True, group_keys=True, observed=<no_default>, dropna=True)。
參數 本題 pandas 2.2.3 預設 pandas 3.0.6 預設 白話
by'CustomerID' NoneNone照什麼分:一個欄名、好幾個欄名的清單(分好幾層)……
axis— <no_default>(=0,已不建議用)已移除 沿哪個方向分。2.2 寫了會跳 FutureWarning;3.0 寫了直接出錯
level— NoneNone改照「列標籤」的某一層分組,而不是照欄位
as_index— TrueTrueTrue:鍵(客戶編號)變成結果的列標籤,所以結果是 Series;False:留成一般欄位,結果是 DataFrame
sort— TrueTrue把籃子照鍵排序(C01…C06);False:照第一次出現的順序。選項 B 的關鍵
group_keys— TrueTrue只影響 .apply():結果要不要多一層「客戶」標籤。本題用不到
observed— <no_default>(目前等於 False)True只在分組欄是 category分類型別:事先列好「可能出現哪些值」的欄位,例如把客戶編號限定為 C01~C07。 型別時有作用:要不要列出「有登記、但沒有任何資料」的類別
dropna— TrueTrue鍵是空的(NaN)的列,要不要丟掉
pandas 3 的簽名多了一個 *:as_index 以後的參數只能用「名字=值」寫。
參數改一下會怎樣(都實測過)
寫法 結果
df.groupby('CustomerID', sort=False)['Revenue'].sum()C03 1550、C04 385、C01 1100、C02 830、C05 1220、C06 900(照第一次出現);後面接 .nlargest(3) 結果不變
df.groupby('CustomerID', as_index=False)['Revenue'].sum()交出 DataFrame:兩欄 CustomerID、Revenue,列標籤 0~5(見下方)
先把 O020 的 CustomerID 清成空的,再 ….sum() C04 變成 320:O020 那張 65 元因為沒有客戶,不進任何籃子。加 dropna=False 就多一列 NaN 65
把 CustomerID 轉成 category,類別多登記一個沒有交易的 C07,再 ….sum() pandas 2.2.3:多一列 C07 0,並跳 FutureWarning;pandas 3.0.6:沒有 C07。寫 observed=False 兩版都列出 C07 0
df.groupby('CustomerID')['Revenue'].apply(lambda x: x.nlargest(1))兩層標籤:C01 2 560、C02 4 680…;加 group_keys=False 只剩原本的列標籤 2、4、0…
df.set_index('CustomerID')['Revenue'].groupby(level='CustomerID').sum().nlargest(3)C03 1550、C05 1220、C01 1100:照列標籤分組,答案一樣
df.groupby('CustomerID', axis=0)pandas 2.2.3:FutureWarning: The 'axis' keyword in DataFrame.groupby is deprecated and will be removed in a future version.;pandas 3.0.6:TypeError: DataFrame.groupby() got an unexpected keyword argument 'axis'
as_index=False 的輸出(兩版相同):
CustomerID Revenue
0 C01 1100
1 C02 830
2 C03 1550
3 C04 385
4 C05 1220
5 C06 900
pandas 2.2.3 的 observed 警告原文:FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.
執行後
type → pandas 3.0.6:pandas.api.typing.DataFrameGroupBy
pandas 2.2.3:pandas.core.groupby.generic.DataFrameGroupBy
print(…) → <pandas.api.typing.DataFrameGroupBy object at 0x…> ← 不是表格
.obj is df → True .ngroups → 6
.groups → {'C01': [2, 14], 'C02': [4, 11, 18], 'C03': [0, 5, 8, 13, 17], …}
新手常踩的坑
欄名打錯:df.groupby('customerid') 在建立這一步就出錯,實測 KeyError: 'customerid'。
以為 groupby 完就有結果:print 出來只有 <… object at 0x…>。一定要接 sum、max、count 這類方法才會算出數字。
直接在分組物件上 nlargest:df.groupby('CustomerID').nlargest(3),實測 AttributeError: 'DataFrameGroupBy' object has no attribute 'nlargest'。
看動畫 1:['Revenue'] 只看金額(0:37)
跟分組物件要 'Revenue' 這一欄:籃子還是同樣 6 個,但每個籃子裡只看金額,不管 OrderID、Category。交出一個新的 SeriesGroupBy 物件,仍然還沒算。
[取東西 緊貼在 groupby(…) 後面:跟 DataFrameGroupBy 物件要東西(它的 __getitem__)。
]收尾 交出 SeriesGroupBy(2.2:pandas.core.groupby.generic.SeriesGroupBy;3.0:pandas.api.typing.SeriesGroupBy)。
SeriesGroupBy 身上也有一個 .obj:裡面就是 Revenue 那 20 個金額(實測和 df['Revenue'] 的值相同)。分組規則沿用上一步,只是換成「只看這一欄」。
忘了 ['Revenue'] 會怎樣:連文字欄也被「加總」
df.groupby('CustomerID').sum() 不會報錯,但它會把每一欄 都加總。文字的「加法」是把字串接起來,所以 OrderID、Category 變成一長串(兩版實測,終端機太窄時中間那欄用 ... 省略):
OrderID ... Revenue
CustomerID ...
C01 O003O015 ... 1100
C02 O005O012O019 ... 830
C03 O001O006O009O014O018 ... 1550
C04 O002O004O007O010O013O016O020 ... 385
C05 O008O017 ... 1220
C06 O011 ... 900
[6 rows x 3 columns]
被省略的 Category 也被接起來了,例如 C01 變成 ElectronicsHome。Revenue 那欄是對的,但得到的是一張 DataFrame,不是 Series,後面直接接 .nlargest(3) 會出錯。只想加數字欄可以寫 sum(numeric_only=True),最清楚的還是先選好 ['Revenue']。
新手常踩的坑
欄名打錯:['revenue'],實測 KeyError: 'Column not found: revenue'(訊息和一般表格的 KeyError 不一樣)。
寫成雙層方括號 [['Revenue']]:交出的是 DataFrameGroupBy,.sum() 變成只有一欄的 DataFrame,再接 .nlargest(3) 實測 TypeError: DataFrame.nlargest() missing 1 required positional argument: 'columns';要寫成 .nlargest(3, 'Revenue') 才行。
寫成 df.groupby('CustomerID').Revenue 也可以跑(實測結果相同),但欄名有空格、或和方法撞名時就不行,方括號最保險。
看動畫 2:sum 真正計算(0:59)
叫 SeriesGroupBy 物件把每個籃子裡的金額加起來。這時候 pandas 才真的去讀那 20 個金額、一籃一籃加總,做出一個新的 Series:標籤是客戶編號,值是總額。
.sum方法 寫在 GroupBy 這一層(§1 觀念 4),SeriesGroupBy 繼承來的。
()呼叫 一定要有括號。沒有括號只拿到方法本身,不會加總。
sum 完整參數表(SeriesGroupBy.sum)
簽名:pandas 3.0.6 sum(numeric_only=False, min_count=0, skipna=True, engine=None, engine_kwargs=None);pandas 2.2.3 沒有 skipna,其他相同。
參數 預設值 白話 實測
numeric_onlyFalse只加數字欄 這裡只剩 Revenue,沒差;對整張表 df.groupby('CustomerID').sum(numeric_only=True) 就只剩 Revenue 一欄
min_count0籃子裡至少要有幾個「不是空格」的值才加,不夠就給 NaN sum(min_count=3):C01、C05、C06 只有 2、2、1 張,變成 NaN,整欄變成小數 float64
skipnaTrue(只有 pandas 3)跳過空格 本題沒有空格,pandas 3 寫 skipna=False 結果不變;pandas 2.2.3 寫了 → TypeError: GroupBy.sum() got an unexpected keyword argument 'skipna'
engineNone計算引擎(預設用 pandas 內建的 cython;'numba' 要另外安裝) —
engine_kwargsNone給 numba 引擎的設定 —
.sum() 交出的新 Series(兩版相同):
CustomerID
C01 1100
C02 830
C03 1550
C04 385
C05 1220
C06 900
Name: Revenue, dtype: int64
type → pandas.Series(2.2:pandas.core.series.Series) index.name → 'CustomerID' name → 'Revenue'
順序是 C01~C06(groupby 預設 sort=True),還沒照金額排。6 個加起來 5985,等於 20 張收據的總和。
忘了括號:….sum.nlargest(3),實測 AttributeError: 'function' object has no attribute 'nlargest':.sum 只是那台機器本身,還沒開動。
以為 sum 會順便排好:sum 只負責加總,順序照客戶編號;要前 3 名還得接 nlargest。直接 .sum().head(3) 會拿到 C01 1100、C02 830、C03 1550(實測),C02 根本不在前 3 名。
看動畫 1:nlargest 挑前 3(1:03)
叫上一步的 Series「把值最大的 3 個挑出來,由大到小排」。交出一個 3 格的新 Series:C03 1550、C05 1220、C01 1100。
.nlargest方法 Series 設計圖自己寫的方法。名字就是 n largest:最大的 n 個。對應的 nsmallest 挑最小的。
nlargest 完整參數表(Series.nlargest)
簽名(兩版相同):nlargest(n=5, keep='first')
參數 本題的值 預設值 白話
n3 5要幾個。不寫就是 5 個:實測 .nlargest() 交出 C03、C05、C01、C06、C02。n 比資料還多時全部交出(nlargest(10) 交出 6 個);nlargest(0) 交出空的 Series
keep— 'first'同分卡在名次邊界時留誰:'first' 留先出現的、'last' 留後出現的、'all' 全部留下(可能超過 n 個)
本題的 6 個總額沒有同分,keep 怎麼設都一樣。假設 C06 的總額也是 1100,和 C01 並列第 3(實測):
keep 結果
'first'(預設)C03 1550、C05 1220、C01 1100(C01 排在前面,先出現)
'last'C03 1550、C05 1220、C06 1100
'all'C03 1550、C05 1220、C01 1100、C06 1100(4 個)
最後交出的 Series(兩版相同):
CustomerID
C03 1550
C05 1220
C01 1100
Name: Revenue, dtype: int64
.index.tolist() → ['C03', 'C05', 'C01'] ← 前 3 名客戶的名單
.equals(s.sort_values(ascending=False).head(3)) → True
它和「整串排好再 head(3)」結果相同,但不必把每個人都排好名次:§2 打開原始碼看過,它先用 kth_smallest 找出第 3 名的門檻,只排過門檻的幾個。這就是題目說的「高效」。
對文字欄用 nlargest:df['CustomerID'].nlargest(3),實測 TypeError: Cannot use method 'nlargest' with dtype object(pandas 3 是 dtype str)。nlargest 只能用在數字上。
把 3 寫成字串:nlargest('3'),實測 TypeError: '<=' not supported between instances of 'str' and 'int'。
在 sum 之前就 nlargest:df.groupby('CustomerID')['Revenue'].nlargest(1) 可以跑,但意思是「每位客戶自己最大的 1 張收據」(§5 常見寫錯有輸出)。
數 CustomerID 這一欄每個值出現幾次(每人有幾張收據),由多到少排好,交出一個 Series;再拿前 3 格。完全沒有看金額。
.value_counts方法 DataFrame 設計圖自己寫的方法:數「每種組合出現幾次」。
'CustomerID'位置引數 對應參數 subset:只看這一欄。
.head(3)方法 和 A、B 的 head 相同:照目前順序拿前 3 格。因為 value_counts 已經照次數排好,這次拿到的是次數最多的 3 個。
value_counts 完整參數表(DataFrame.value_counts)
簽名(兩版相同):value_counts(subset=None, normalize=False, sort=True, ascending=False, dropna=True)
參數 選項 D 的值 預設值 白話+實測
subset'CustomerID' None看哪幾欄。None 是「所有欄一起看」:每列的 4 個值組合都不同,每種都是 1 次。寫成清單 ['CustomerID'] 時,結果的標籤會變成 MultiIndex(多層標籤),印出來的空白比較多
normalize— FalseTrue:改成比例,C04 0.35、C03 0.25、C02 0.15、C01 0.10、C05 0.10、C06 0.05,名字變成 proportion
sort— True照次數排。False 不排:pandas 2.2.3 照客戶編號(C01、C02…),pandas 3.0.6 照第一次出現(C03、C04、C01…)
ascending— FalseFalse 由多到少;True 由少到多,.head(3) 會變成 C06 1、C01 2、C05 2
dropna— True空的(NaN)不數
value_counts('CustomerID') 交出的 Series(6 格):
C04 7、C03 5、C02 3、C01 2、C05 2、C06 1 ← 名字是 count
.head(3) 之後(兩版相同):
CustomerID
C04 7
C03 5
C02 3
Name: count, dtype: int64
§2 看過原始碼:DataFrame 的 value_counts 在裡面呼叫 groupby,數每個籃子有幾列,所以它也是分籃子,只是「套用」的是數張數。Series 也有 value_counts(參數 normalize、sort、ascending、bins、dropna),df['CustomerID'].value_counts() 結果相同。
把「次數」當成「金額」:C04 來了 7 次最多,總額卻只有 385,是 6 個人裡最少的。
以為結果的名字是 CustomerID:pandas 2.0 起叫 count(最後一行 Name: count)。
欄名打錯:df.value_counts('customerid'),實測 KeyError: 'customerid'。