據(jù)分析Pandas全攻略)
前言Pandas 是 Python 生態(tài)里做表格數(shù)據(jù)處理最常用的第三方庫(kù)third-party library 它建立在 NumPy 之上提供了兩個(gè)核心數(shù)據(jù)結(jié)構(gòu)一維的Series和二維的DataFrame。 日常說(shuō)的用 Python 做數(shù)據(jù)分析十有八九指的就是 pandas。需要先說(shuō)清楚一件事pandas 不是標(biāo)準(zhǔn)庫(kù)必須單獨(dú)安裝pip install pandas 它也不在本文的驗(yàn)證環(huán)境里——本機(jī)沒(méi)有 Python 解釋器、沒(méi)有 pandas所以下文的示例只能逐行人工推演無(wú)法運(yùn)行驗(yàn)證。凡是涉及具體參數(shù)的地方都以 Pandas 官方文檔為準(zhǔn)本文只保證概念和調(diào)用形式準(zhǔn)確。另一個(gè)常見(jiàn)誤解是把 pandas 當(dāng)成Excel 的替代品。pandas 的數(shù)據(jù)模型是列式 索引 它擅長(zhǎng)的是批量向量化運(yùn)算、分組聚合和連接而不是像 Excel 那樣逐個(gè)單元格編輯。 理解這一點(diǎn)后面的loc/iloc、鏈?zhǔn)劫x值、apply與向量化的取舍就都順了。一、兩個(gè)核心數(shù)據(jù)結(jié)構(gòu)Series是帶標(biāo)簽的一維數(shù)組DataFrame是帶行標(biāo)簽和列標(biāo)簽的二維表。 可以把DataFrame理解成共享同一個(gè)行索引index的多個(gè)Series。# pandas 1.0 均可使用示例需先 pip install pandasimport pandas as pds pd.Series([10, 20, 30], index[a, b, c])df pd.DataFrame({name: [Alice, Bob, Cara],age: [25, 30, 28],city: [Beijing, Shanghai, Beijing],})print(df.index) # RangeIndex(start0, stop3, step1)print(df.columns) # Index([name, age, city], dtypeobject)print(df.dtypes) # 每列各自的數(shù)據(jù)類(lèi)型索引index是 pandas 區(qū)別于純 NumPy 數(shù)組的關(guān)鍵它讓按標(biāo)簽取值和 按位置取值成為兩套不同的語(yǔ)義也正是loc與iloc分野的來(lái)源。dtypes尤其要看。常見(jiàn)類(lèi)型有int64、float64、object、bool 較新的 pandas 還支持可空類(lèi)型Int64大寫(xiě) I、boolean、string。object通常是字符串或混合類(lèi)型運(yùn)算性能一般能轉(zhuǎn)成專(zhuān)門(mén)的類(lèi)型就轉(zhuǎn)。二、數(shù)據(jù)讀取與寫(xiě)出pandas 的 I/O 是它最受歡迎的部分常見(jiàn)的讀取函數(shù)命名很規(guī)律。# pandas 1.0df pd.read_csv(data.csv, encodingutf-8)df pd.read_excel(data.xlsx, sheet_name0) # sheet_name 默認(rèn) 0即第一個(gè)表# df.to_csv(out.csv, indexFalse, encodingutf-8)要點(diǎn)read_csv的encoding參數(shù)在 Windows 上尤其重要中文文件常見(jiàn)utf-8與gbk兩種情況讀出來(lái)亂碼先懷疑編碼。read_excel的參數(shù)叫sheet_name不是舊版的sheetname可傳表名、表序號(hào)或傳None一次性讀全部表并返回字典。to_csv/to_excel的indexFalse能避免把行號(hào)也寫(xiě)進(jìn)文件。場(chǎng)景常用函數(shù)關(guān)鍵參數(shù)CSV 文本read_csvsep、encoding、usecolsExcelread_excelsheet_name、header、usecolsSQL 數(shù)據(jù)庫(kù)read_sqlsql、con、params寫(xiě)出 CSVto_csvindex、encoding寫(xiě)出 Excelto_excelsheet_name、index注意read_sql的簽名是read_sql(sql, con, ...)sql 在前、連接在后 順序記反是最常見(jiàn)的低級(jí)錯(cuò)誤。參數(shù)化查詢請(qǐng)用params不要自己拼字符串。三、選擇、過(guò)濾與賦值取值有三套入口必須分清# pandas 1.0df[age] # 取一列返回 Seriesdf[[name, age]] # 取多列返回 DataFrame注意雙重方括號(hào)df.loc[0, name] # 按標(biāo)簽取df.iloc[0, 0] # 按位置取df[df[age] 26] # 布爾過(guò)濾df[age]返回Seriesdf[[age]]返回DataFrame。 方括號(hào)里給字符串是選列給布爾序列是過(guò)濾行——同一套語(yǔ)法兩種含義 這是初學(xué)者最容易混淆的地方。賦值優(yōu)先用loc避免鏈?zhǔn)劫x值# pandas 3.0 起默認(rèn)啟用寫(xiě)時(shí)復(fù)制Copy-on-Writedf.loc[df[age] 26, group] senior從 pandas 3.0 開(kāi)始寫(xiě)時(shí)復(fù)制Copy-on-Write簡(jiǎn)稱 CoW成為默認(rèn)行為 過(guò)去那種改了卻報(bào) SettingWithCopyWarning、或者改了個(gè)寂寞的情況有了統(tǒng)一語(yǔ)義 任何切片得到的都是邏輯副本修改它不會(huì)影響原對(duì)象除非你顯式用.loc在原對(duì)象上改。 新版里SettingWithCopyWarning這個(gè)警告本身已經(jīng)被移除。四、分組聚合與合并分組groupby的語(yǔ)義是拆分—應(yīng)用—合并split-apply-combine# pandas 1.0result df.groupby(city, as_indexFalse)[age].mean()# 多列多函數(shù)聚合agg df.groupby(city).agg({age: [mean, max]})合并merge對(duì)應(yīng) SQL 的 JOIN# pandas 1.0merged pd.merge(orders, users, howleft, onuser_id)how取值inner內(nèi)連接默認(rèn)、left左連接、right右連接、outer外連接、cross笛卡爾積。左右兩側(cè)列名不同時(shí)用left_on/right_on 同名但需要區(qū)分時(shí)用suffixes控制后綴。五、一個(gè)完整的小例子下面把讀取、清洗、分組、寫(xiě)出串起來(lái)。代碼結(jié)構(gòu)完整但請(qǐng)以官方文檔核對(duì)參數(shù)。# pandas 1.0import pandas as pddef summarize(path: str) - pd.DataFrame:df pd.read_csv(path, encodingutf-8)# 1) 列名去空格避免 name 與 name 被當(dāng)成兩列df.columns df.columns.str.strip()# 2) 數(shù)值列缺失按 0 填充字符串列缺失留空df[amount] df[amount].fillna(0)# 3) 按城市分組求和out (df.groupby(city, as_indexFalse).agg(total(amount, sum), orders(amount, size)).sort_values(total, ascendingFalse))return outif __name__ __main__:summary summarize(orders.csv)print(summary.head())注意agg(total(amount, sum))是命名聚合寫(xiě)法關(guān)鍵字是結(jié)果列名 值是「源列名, 聚合函數(shù)」的元組。這是 pandas 0.25 起提供的語(yǔ)法比agg({amount: sum})再改名更省事。拿不準(zhǔn)時(shí)退回到較老的寫(xiě)法更保險(xiǎn)。常見(jiàn)坑點(diǎn)把df[age]和df[[age]]當(dāng)成一樣?type(df[age])以為得到表格實(shí)際是Series后面df[age][new_col]直接報(bào)錯(cuò) ? 需要保留二維結(jié)構(gòu)就用雙括號(hào)df[[age]]得到DataFrame用鏈?zhǔn)劫x值改數(shù)據(jù)?df[df[age] 26][group] senior在舊版報(bào)SettingWithCopyWarning在新版默默無(wú)效 ?df.loc[df[age] 26, group] senior一次定位、一次賦值read_excel記憶成舊參數(shù)名?pd.read_excel(a.xlsx, sheetnameSheet1)?pd.read_excel(a.xlsx, sheet_nameSheet1)新版本用sheet_nameread_sql把連接和 SQL 寫(xiě)反?pd.read_sql(conn, SELECT * FROM t)?pd.read_sql(SELECT * FROM t, conn)sql在前、con在后拼 SQL 字符串傳參數(shù)?pd.read_sql(fSELECT * FROM t WHERE id{uid}, conn)有注入風(fēng)險(xiǎn) ?pd.read_sql(SELECT * FROM t WHERE id%(uid)s, conn, params{uid: uid})沒(méi)看dtypes就算數(shù)? 直接把object列拿去做減法報(bào)TypeError? 先df.dtypes檢查用pd.to_numeric或astype轉(zhuǎn)成數(shù)值類(lèi)型再算groupby默認(rèn)把分組鍵變成索引? 分組后out[city]報(bào) KeyError因?yàn)?city 跑到索引里去了 ? 加as_indexFalse讓分組鍵保持為普通列總結(jié)主題關(guān)鍵點(diǎn)數(shù)據(jù)結(jié)構(gòu)Series一維、DataFrame二維都帶索引讀取read_csv看encodingread_excel用sheet_name取值方括號(hào)選列/過(guò)濾loc按標(biāo)簽iloc按位置賦值用loc別鏈?zhǔn)劫x值3.0 起 CoW 為默認(rèn)分組groupby是拆分—應(yīng)用—合并注意as_index合并merge(how...)對(duì)齊 SQL JOIN參數(shù)化查詢防注入pandas 的學(xué)習(xí)曲線主要卡在索引語(yǔ)義上先把loc/iloc和布爾過(guò)濾練熟 再去理解分組與合并最后才是性能優(yōu)化。記住它是第三方庫(kù)、要按官方文檔核對(duì)參數(shù) 多寫(xiě)多查比死記 API 更靠譜。