據(jù)分析的實戰(zhàn)入門與性能優(yōu)化)
上周幫一個零售團隊處理他們的銷售流水一份兩百多兆的Excel文件打開就卡等了三分鐘才看到那一百多萬行數(shù)據(jù)。列一多、表一寬Excel直接進入半癱瘓狀態(tài)。我當時的處理方案很簡單把數(shù)據(jù)丟給Pandas用Python腳本批量清洗、聚合、輸出匯總表十分鐘不到跑完還順手給他們做了兩張趨勢圖。今天這篇就是想把這條從Excel到百萬級數(shù)據(jù)分析的路盡量完整地拆開講清楚——Pandas處理入門到底要學(xué)什么哪些操作是高頻剛需數(shù)據(jù)量上來之后又有哪些坑等著你。這不是一篇只講API的入門文而是我實際處理過大幾十個數(shù)據(jù)文件之后沉淀下來的經(jīng)驗。它適合兩類人一種是每天泡在Excel里做報表、但明顯感覺Excel已經(jīng)扛不住數(shù)據(jù)體量的同學(xué)另一種是剛學(xué)完P(guān)ython基礎(chǔ)、想知道怎么用它來正經(jīng)做數(shù)據(jù)分析的初學(xué)者??赐曛竽阒辽倌苊靼滓患聻槭裁碢andas能撐起百萬級數(shù)據(jù)分析這個場景以及你該從哪里下手。1. 為什么不建議繼續(xù)用Excel硬扛大數(shù)據(jù)先聲明一點我不是讓你把Excel丟掉。Excel的交互體驗、可視化、日常表格操作至今仍然是辦公場景里效率最高的工具之一。但當你手里的數(shù)據(jù)開始往幾十萬行、上百萬行走的時候Excel的兩大問題就會暴露得淋漓盡致。1.1 Excel的卡頓源頭在哪里Excel的卡頓核心在于它的內(nèi)存管理方式。一個單元格只要有內(nèi)容哪怕只是一個空字符串它都要為公式計算、格式渲染、條件格式預(yù)留資源。你想想Excel里一個100萬行的表格有多少個單元格光指針和元信息就要吃掉好幾個GB內(nèi)存。更致命的是Excel對大數(shù)據(jù)執(zhí)行篩選、排序、透視表這類操作時是整表重算的。你對一份百萬行的表做個數(shù)據(jù)透視后臺要重新索引全部數(shù)據(jù)CPU和硬盤都在被反復(fù)壓榨表現(xiàn)出來就是轉(zhuǎn)圈、無響應(yīng)。我印象特別深的一次是處理一份將近120萬行的訂單明細列數(shù)不算多大概15列但其中夾雜著日期、時間、文本備注、金額字段。Excel打開花了40多秒之后隨便點擊一下都需要兩三秒才有反應(yīng)想按客戶維度做個匯總直接卡死彈窗Excel無法完成此操作。最后我導(dǎo)出CSV用Pandas讀進來整個數(shù)據(jù)集的讀取、清洗、透視、導(dǎo)出一條管道下來7秒跑完。這不是Pandas特別神奇而是它的設(shè)計本來就是為了處理這種體量。1.2 Pandas處理大數(shù)據(jù)的三點優(yōu)勢Pandas相比之下有幾個天然優(yōu)勢對比維度ExcelPandas內(nèi)存占用方式每個單元格都帶格式元信息開銷巨大按列存儲每列同一類型緊湊得多計算方式界面操作觸發(fā)整表重算向量化運算按列批量計算性能高一個量級可重復(fù)性步驟靠人記改了數(shù)據(jù)要重做腳本寫一次永久復(fù)用怎么清洗的清清楚楚規(guī)模上限百萬行已是極限再往上直接崩處理千萬行以內(nèi)都很常見極限取決于機器內(nèi)存擴展能力缺乏與Python生態(tài)協(xié)作能力機器學(xué)習、可視化、數(shù)據(jù)庫對接隨手就能接其中最核心的一份優(yōu)勢是按列存儲。Pandas的DataFrame本質(zhì)上是個列優(yōu)先的數(shù)據(jù)容器你做計算的時候是整列整列地批量運算底層用的又是C語言實現(xiàn)的numpy數(shù)組所以同樣是對100萬行數(shù)據(jù)求平均Pandas只需要一次性掃過這一列Excel卻要逐單元格去解釋公式。這也是為什么能跑百萬級的關(guān)鍵所在。1.3 什么樣的數(shù)據(jù)量適合用Pandas說句實在話數(shù)據(jù)量在5萬行以內(nèi)我自己也經(jīng)常用Excel處理沒必要非上代碼。但一旦你的數(shù)據(jù)規(guī)模往50萬行靠攏或者你需要每天、每周重復(fù)做同一套匯總邏輯那Pandas的腳本化優(yōu)勢就體現(xiàn)出來了。代碼寫一遍以后的數(shù)據(jù)丟進去跑就行不用再拿鼠標點半天。另外還有一種情況也建議用Pandas——數(shù)據(jù)需要跨表關(guān)聯(lián)。Excel里做多表匹配主要靠VLOOKUP兩張表都很大時VLOOKUP慢得令人絕望而且跨文件引用極易出錯。Pandas里一個merge函數(shù)就解決了一會兒我細說。2. 入門階段必須掌握的五個DataFrame操作很多初學(xué)者學(xué)Pandas最大誤區(qū)是把API當單詞表背??粗臋n里幾百個方法今天學(xué)drop_duplicates明天學(xué)fillna學(xué)完就忘。我的建議很直接你的真實場景只需要從幾個高頻操作開始。2.1 讀入數(shù)據(jù)是第一步也是第一個坑一切從讀數(shù)據(jù)開始。日常最常見的是這兩個函數(shù)import pandas as pd # 讀Excel df pd.read_excel(sales_2024.xlsx, sheet_name訂單明細) # 讀CSV df pd.read_csv(sales_2024.csv, encodingutf-8)如果你是從Excel轉(zhuǎn)過來的注意兩個細節(jié)。第一CSV和Excel文件讀進來之后的DataFrame完全一樣但對百萬級數(shù)據(jù)來說CSV的讀取速度通常比xlsx快很多因為xlsx本質(zhì)是個壓縮的XML文件讀取時要層層解包解析CSV就是純文本逐行掃。第二編碼問題非常容易踩到read_csv的時候如果遇到中文亂碼先試試encodinggbk或者encodingutf-8-sigWindows出品的CSV多半要轉(zhuǎn)一下編碼。還有一個入門階段容易忽略的參數(shù)是usecols。假如原始文件有30列你只需要其中的5列那就別把30列全讀進來占內(nèi)存df pd.read_csv(big_sales.csv, usecols[訂單號, 日期, 金額, 客戶ID, 渠道])這個習慣在數(shù)據(jù)量小的時候看不出來到百萬級以后能少讀一半內(nèi)存。2.2 先摸家底info、head、describe這一套體檢動作拿到數(shù)據(jù)之后不要急著清洗先做體檢。我的固定操作是三連print(df.head()) print(df.info()) print(df.describe())head讓你看前五行長什么樣用來判斷列名是否規(guī)范、數(shù)據(jù)長得是否像你預(yù)期。info輸出的東西更有價值每一列的字段類型、非空數(shù)量、內(nèi)存占用都在里面了。你知道嗎info最后會顯示一行memory usage: 156.8 MB這一行是判斷你后續(xù)需不需要做內(nèi)存優(yōu)化的關(guān)鍵指標。如果你的數(shù)據(jù)讀進來只有十幾萬行卻占了幾百MB那大概率是列的類型沒優(yōu)化后面我專門講。describe適用于數(shù)值列會告訴你每列的均值、標準差、最小值、四分位數(shù)、最大值。這一段跑完哪些列有離群值哪些列明顯異常心里基本就有數(shù)了。2.3 篩選、分組、聚合從自然語言到一行代碼數(shù)據(jù)分析里最高頻的三個動作就是篩選、分組、聚合。舉個例子你腦子里想的是華東區(qū)、渠道為線上的訂單里每個月的總銷售額是多少。這句話翻譯成Pandas就是三行filtered df[(df[區(qū)域] 華東) (df[渠道] 線上)] monthly_sales filtered.groupby(filtered[日期].dt.to_period(M))[金額].sum()第一行篩選用布爾條件組合。注意Pandas里多個條件必須用連接每個條件都要加括號否則報錯。第二行按月份分組這里用到了dt.to_period(M)意思是把日期列轉(zhuǎn)成月份周期相當于把日期按月份歸類。很多人第一步就栽在分組上是因為忘了groupby之后對象的類型會變成DataFrameGroupBy不再是普通的DataFrame。你必須對它再做聚合操作sum、mean、count、agg才能得到結(jié)果。如果你要同時算多個指標比如既要總銷售額又要訂單數(shù)就配合agg使用result filtered.groupby(filtered[日期].dt.to_period(M)).agg( 銷售額(金額, sum), 訂單數(shù)(訂單號, nunique) )這里訂單數(shù)用nunique意思是去重計數(shù)如果你直接count它會數(shù)一下這個組里有幾條記錄而不是多少個不同訂單。這個區(qū)別在電商數(shù)據(jù)里特別容易造成報表失真我在實際交付中反復(fù)跟業(yè)務(wù)團隊確認過這個口徑的差異。2.4 排序和去重永遠記住你處理的是一張表排序用sort_values去重用drop_duplicatestop_customers df.sort_values(銷售額, ascendingFalse).head(10) unique_orders df.drop_duplicates(subset[訂單號])這兩個操作很簡單但實際場景里有個細節(jié)容易出錯drop_duplicates默認保留第一條重復(fù)記錄如果你想保留最后一條要加keeplast參數(shù)。另外如果原始數(shù)據(jù)里訂單號本身存在空值去重之前得先確認一下空值占比避免把大量真實訂單誤刪??樟说臄?shù)據(jù)不算重復(fù)但Pandas默認也會把它當作重復(fù)項之一保留一條處理的時候要留意。2.5 缺失值處理先搞清楚為什么缺再決定怎么填缺失值處理看起來簡單其實最考驗?zāi)銓I(yè)務(wù)的理解。處理手段無非就幾種刪除、填0、填均值、前向填充。但選哪一種取決于這個字段為什么為空。舉個例子訂單金額為空那大概率是異常單需要謹慎。用戶年齡為空你可以考慮用該渠道的平均年齡填充。庫存表里某個商品的補貨日期為空可能只是因為它還沒有排期跟異常沒有關(guān)系。我在實戰(zhàn)中的固定做法是先跑一下缺失值統(tǒng)計missing_ratio df.isnull().sum() / len(df) print(missing_ratio[missing_ratio 0])然后根據(jù)缺失比例和字段含義分列處理絕不無腦fillna一把梭。對數(shù)值型字段最常見的方案是fillna(0)但你要想清楚0是否是一個有業(yè)務(wù)意義的填充值。3. 百萬級數(shù)據(jù)真正要命的三個性能瓶頸到了百萬行這個量級你會發(fā)現(xiàn)普通的Pandas代碼還能跑但就是慢。明明一個很簡單的操作要等好幾分鐘這就是性能瓶頸。我踩過的三個最大的坑今天一次性說清楚。3.1 dtype是內(nèi)存大戶object類型會吞掉你的內(nèi)存很多人讀入數(shù)據(jù)之后從來不關(guān)心列類型。默認情況下Pandas會把文本列都識別為object類型而object類型在內(nèi)存里存的其實是字符串指針比數(shù)值類型昂貴得多。我做過一次實測一個90萬行的數(shù)據(jù)里面有三列是分類文本比如華東/華北/華南這種如果保持object類型光這三列就占了大約120MB內(nèi)存把它們轉(zhuǎn)成category后直接降到5MB左右內(nèi)存減少九成以上。所以大數(shù)據(jù)的第一個優(yōu)化動作永遠是類型壓縮。常用的招數(shù)df[區(qū)域] df[區(qū)域].astype(category) df[金額] df[金額].astype(float32) df[訂單數(shù)] df[訂單數(shù)].astype(int32)category類型適合那種重復(fù)值很多的列比如區(qū)域、渠道、產(chǎn)品大類這幾個列在百萬行數(shù)據(jù)里往往只有幾十種取值用category再合適不過。數(shù)值列則用更小的子類型替代默認的float64和int64哪怕float32在精度上損失一點點做匯總分析完全夠用。每次做完類型轉(zhuǎn)換看一眼df.info()的memory usage你會直觀感受到內(nèi)存從幾百MB掉到幾十MB這會給后續(xù)所有操作騰出大量空間。3.2 循環(huán)是萬惡之源向量化才是Pandas的舒適區(qū)新手最容易寫的慢代碼就是遍歷每一行# 千萬別這么寫100萬行會讓你等到懷疑人生 for i in range(len(df)): if df.loc[i, 金額] 1000: df.loc[i, 等級] 高 else: df.loc[i, 等級] 低這行代碼的邏輯沒錯但對100萬行數(shù)據(jù)來說循環(huán)加loc逐行定位雙重減速跑完起碼要十分鐘起步。Pandas的設(shè)計思想是向量化——對整列一次性操作底層用C語言的循環(huán)替你跑速度是一百倍以上的差距。同樣的邏輯用向量化一行搞定df[等級] np.where(df[金額] 1000, 高, 低)如果條件復(fù)雜多個列參與判斷可以用np.selectconditions [ df[金額] 5000, df[金額] 1000, df[金額] 0 ] choices [高, 中, 低] df[等級] np.select(conditions, choices, default未知)有很多人問我什么時候真的要用循環(huán)我的答案是當你需要逐行依賴前一行計算結(jié)果的時候比如計算累加收益、持倉狀態(tài)機這類有順序依賴的業(yè)務(wù)循環(huán)才不可避免。此時你可以考慮用numba加速或者盡量將這類操作拆解成累計函數(shù)。大多數(shù)想用循環(huán)的場景其實是沒找到對應(yīng)的向量化函數(shù)先找找不到再循環(huán)。3.3 groupby和merge在數(shù)據(jù)量上來之后的正確姿勢groupby在百萬級數(shù)據(jù)上如果直接跑通常問題不大但如果你在這個基礎(chǔ)上又掛了多個agg還要排序再合并回原表那計算量會指數(shù)級上升。我常用的原則是能少合并就少合并groupby的結(jié)果如果只是最終報表就不concat回原表保持分組之后只輸出結(jié)果。merge同理。兩張百萬級表做關(guān)聯(lián)默認用howinner就行不要沒事就搞howouter外連接會在匹配不到時產(chǎn)生大量空行內(nèi)存和行數(shù)都會暴漲。另外merge之前建議先對被關(guān)聯(lián)列做類型統(tǒng)一比如一邊是int64一邊是object即使內(nèi)容一樣merge也會判定無法匹配。我吃過這個虧客戶ID一側(cè)是從Excel讀的數(shù)字被讀成了float64一側(cè)是字符串看起來都是10086merge出來全是NaN排查了很久才發(fā)現(xiàn)是類型不一致。所以merge前的類型檢查要養(yǎng)成習慣用df.dtypes排查一遍再操作。4. 一次完整的百萬行數(shù)據(jù)實操復(fù)盤理論說了不少我們干脆從頭到尾跑一個完整案例。我構(gòu)造一個模擬場景一份某電商平臺的交易流水大約120萬行包含訂單號、用戶ID、區(qū)域、渠道、商品ID、銷售日期、銷售額、成本、數(shù)量、優(yōu)惠金額這10列。目標是算出每個區(qū)域每個渠道的月度銷售額、訂單量和利潤。4.1 先做環(huán)境準備與數(shù)據(jù)體檢我會先準備環(huán)境import pandas as pd import numpy as np df pd.read_csv(sales_120w.csv, encodingutf-8, parse_dates[銷售日期]) df.info()這里parse_dates參數(shù)直接把日期列解析成datetime類型不要等后續(xù)再轉(zhuǎn)換。info出來之后看內(nèi)存占用我模擬的結(jié)果是大概980MB這個體量在普通配置的電腦上已經(jīng)有點吃緊了。于是做類型壓縮df[區(qū)域] df[區(qū)域].astype(category) df[渠道] df[渠道].astype(category) df[銷售額] df[銷售額].astype(float32) df[成本] df[成本].astype(float32) df[優(yōu)惠金額] df[優(yōu)惠金額].astype(float32) df[數(shù)量] df[數(shù)量].astype(int8) df[利潤] (df[銷售額] - df[成本] - df[優(yōu)惠金額]).astype(float32)此時再跑一遍df.info()內(nèi)存占用已經(jīng)掉到差不多150MB這個數(shù)字對后續(xù)計算就很舒服了。4.2 深度清洗去重、缺失值、異常值三件套清洗環(huán)節(jié)是重頭戲先統(tǒng)計缺失再處理重復(fù)和異常print(df.isnull().sum())假設(shè)發(fā)現(xiàn)優(yōu)惠金額有2.3萬行缺失這通常是訂單沒有優(yōu)惠填充0是合理的df[優(yōu)惠金額] df[優(yōu)惠金額].fillna(0)再看訂單號去重跑一個數(shù)量對比before len(df) after len(df.drop_duplicates(subset[訂單號, 用戶ID])) print(f去重前后差異: {before - after})如果差異顯著說明有大量重復(fù)記錄這些通常來自同一個用戶重復(fù)下單產(chǎn)生的多條流水在訂單明細分析里屬于正常情況不需要刪除。但如果同一訂單號出現(xiàn)多次且你統(tǒng)計的是訂單量這種口徑就必須去重后再統(tǒng)計否則訂單量會虛高。異常值這一塊我習慣跑一個describe看分布print(df[[銷售額, 數(shù)量, 利潤]].describe())如果發(fā)現(xiàn)銷售額最小值是負數(shù)或者利潤出現(xiàn)了極度異常的極大值要單獨抽查這些記錄判斷是促銷沖正還是數(shù)據(jù)錄入錯誤。模擬數(shù)據(jù)里我設(shè)定了少量銷售負值退款單這類記錄在匯總?cè)粘dN售額時應(yīng)該剔除但它們對退款分析有價值所以我會單獨建一張退款表而不是簡單刪掉。4.3 管道化處理把清洗邏輯封裝進函數(shù)清洗步驟一多臨時寫在腳本里就很亂。實際操作中我會把整套清洗封裝成一個函數(shù)用pipe一次性串起來def clean_sales_data(df): df df.copy() df[優(yōu)惠金額] df[優(yōu)惠金額].fillna(0) df df[df[銷售額] 0] df[利潤] df[銷售額] - df[成本] - df[優(yōu)惠金額] df[銷售日期] pd.to_datetime(df[銷售日期]) return df df_clean ( pd.read_csv(sales_120w.csv, encodingutf-8) .pipe(clean_sales_data) )這么做的最大好處是以后每周收到新數(shù)據(jù)只需要重復(fù)跑一遍管道不會因為少執(zhí)行某一步清洗而造成結(jié)果前后不一致。數(shù)據(jù)交付里最忌諱的就是這次我手動多過濾了一個條件這類操作結(jié)果下周對不上賬。4.4 按區(qū)域渠道匯聚月度核心指標清洗完之后做匯總這是百萬級數(shù)據(jù)里最典型的導(dǎo)出需求result ( df_clean .groupby([ df_clean[銷售日期].dt.to_period(M), 區(qū)域, 渠道 ]) .agg( 銷售額(銷售額, sum), 訂單量(訂單號, nunique), 利潤(利潤, sum) ) .reset_index() ) result.to_csv(月度區(qū)域渠道匯總.csv, indexFalse, encodingutf-8-sig)這段代碼在120萬行數(shù)據(jù)上跑實測大概用了不到5秒。跑完之后result大約幾百行這已經(jīng)是可以直接交給業(yè)務(wù)方看的報表級輸出了。這里有一個細節(jié)很重要就是groupby里分組鍵用了df[銷售日期].dt.to_period(M)這是一個Series而不是列名字符串這樣寫是允許的而且能把日期直接按月歸類。如果按月分組的場景很常見更推薦提前把月份字段單獨生成一列這樣后續(xù)寫代碼更清晰df_clean[月份] df_clean[銷售日期].dt.to_period(M)4.5 結(jié)果校驗別相信一次跑通的結(jié)果跑完匯總之后千萬不要直接交差一定要做結(jié)果校驗。最穩(wěn)妥的校驗方法是抽一個你熟悉的維度跟原始數(shù)據(jù)對齊。比如拿華東區(qū)域線上渠道某一個月的數(shù)據(jù)手工用Excel篩選原始文件算一下總銷售額跟你代碼輸出的結(jié)果對一下。對不上的情況絕大多數(shù)是過濾條件不一致比如你有沒有把退款單算進去有沒有漏掉空區(qū)域的分組。我另外的習慣是在腳本里做兩層自動校驗# 校驗1匯總后的總銷售額等于過濾后的總銷售額 assert abs(result[銷售額].sum() - df_clean[銷售額].sum()) 0.01 # 校驗2每個分組的行數(shù)占比要合理 group_counts df_clean.groupby([ 區(qū)域, 渠道, 月份 ]).size() assert len(group_counts) len(result)這兩層校驗成本極低但你有沒有做這步?jīng)Q定了交付的質(zhì)量是看起來對還是真對。數(shù)據(jù)量越大的時候越要信不過自己的手要信校驗邏輯。5. 兩年里踩過最深的四個Pandas坑坑這個東西不真正踩一遍光看文檔是記不住的。作為一個被坑過無數(shù)次的人我挑四個最傷的例子按慘痛程度排個序。5.1 inplaceTrue的虛假安全感剛學(xué)Pandas時特喜歡寫df.dropna(inplaceTrue)總覺得加個inplace參數(shù)就萬事大吉不用重新賦值。但實際上inplace在很多方法里是有隱患的。第一Pandas 2.x開始已經(jīng)在官方文檔里不推薦這種用法未來有可能被移除。第二inplace依賴函數(shù)內(nèi)部修改原對象某些方法實際做的是復(fù)制一份再修改再回填根本沒有省內(nèi)存。第三鏈式調(diào)用的場景里inplace經(jīng)常失效——你對一個切片執(zhí)行inplace操作改的只是副本原DataFrame紋絲不動。我現(xiàn)在一律寫成顯式賦值df df.drop_duplicates(subset[訂單號])這樣代碼意圖明確debug時也方便打斷點看中間狀態(tài)。5.2 鏈式賦值引發(fā)的SettingWithCopyWarning鏈式賦值是我見過最隱蔽的Bug來源。和inplace類似問題出在你對切片的理解。下面這段代碼df[df[金額] 100][折扣] 0很多新手以為這是把金額大于100的那幾行折扣改成0實際上Pandas會警告你這可能是在試圖修改一個副本。因為df[df[金額] 100]產(chǎn)生的是一個視圖或副本到底改沒改到原DataFrame取決于內(nèi)部優(yōu)化你不該依賴它。正確的寫法永遠是用.locdf.loc[df[金額] 100, 折扣] 0記住這條規(guī)則就好了在DataFrame里做條件修改一律df.loc[條件, 列名] 新值不要用鏈式括號。養(yǎng)成這個習慣之后我再沒遇到過SettingWithCopyWarning。5.3 索引沒有resetgroupby之后悄悄變了樣groupby之后忘了reset_index是我看別人代碼時最常見的問題。groupby的結(jié)果行數(shù)取決于分組鍵的取值組合數(shù)但索引是這些分組鍵的值你要是還想再繼續(xù)跟其他DataFrame合并或者做進一步篩選就會發(fā)現(xiàn)索引亂七八糟對不上。如果你在groupby后直接執(zhí)行to_excel你會發(fā)現(xiàn)左邊多了一列看不到名字的索引。正確的姿勢是想清楚這個中間結(jié)果是要繼續(xù)參與計算還是作為最終報表。如果是中間結(jié)果分組之后加個reset_index()把分組鍵變成普通列后面怎么操作都不慌。這也是為什么我對agg結(jié)果的固定習慣是馬上reset_index()。5.4 read_excel在大型Excel文件上的隱藏慢速很多人遇到大文件第一步還是pd.read_excel殊不知這可能是最慢的讀法。Excel文件的底層是XML壓縮格式讀取時要實時解壓和解析速度遠比CSV慢。我測過一個約80MB的xlsxread_excel用了30多秒同樣數(shù)據(jù)導(dǎo)出成CSV后read_csv只要5秒。如果是100MB以上的Excel差距還會更離譜。所以我的工作流是如果數(shù)據(jù)源是大型Excel文件第一步先用Excel另存為CSV或者用腳本把xlsx批量轉(zhuǎn)成csv再做Pandas處理。如果實在只能讀Excel可以考慮用openpyxl引擎配合usecols和nrows參數(shù)先只讀一部分看看結(jié)構(gòu)全量讀取留到最后。還有一個相關(guān)的坑read_excel默認會讀取整個sheet的所有行和列如果sheet里有大量空行Pandas也會傻乎乎地讀進來再丟掉白白浪費內(nèi)存和讀取時間。加sheet_name參數(shù)指定要讀的sheet能省則省。寫在最后的一點個人經(jīng)驗我剛開始用Pandas處理百萬級數(shù)據(jù)時心態(tài)是代碼能跑就行后來才意識到干凈、可讀、可復(fù)現(xiàn)的腳本比跑得快重要得多。因為數(shù)據(jù)分析工作真正的交付物不是那幾張報表而是如果有人想復(fù)現(xiàn)你的數(shù)據(jù)結(jié)論能不能照著你的腳本一步步跑出同樣的結(jié)果。哪怕你只是給自己用一周之后回看你也會感謝當初寫了清晰注釋的自己。還有一個容易被低估的習慣每一步處理完都打印一下行數(shù)和shape數(shù)據(jù)從120萬行變成90萬行這一步到底是因為去重還是因為過濾退款心里要有數(shù)。做數(shù)據(jù)分析最怕的不是報錯而是結(jié)果看著挺合理但實際上有偏差那種錯誤藏得最深也最難查。Pandas這條路入門不難難的是數(shù)據(jù)量上來之后那種感覺自己會用、但一跑就慢、一慢就不知道該優(yōu)化哪里的迷茫。我的建議是先跑通再想性能最后再優(yōu)化內(nèi)存。別一上手就背一堆優(yōu)化技巧等你真遇到那個讓你干等10分鐘的百萬行文件時你自然會回來翻這篇。