写在前面的话做数据分析这些年我手里最离不开的工具就是Pandas。坦白讲刚学Python的时候我也觉得Pandas只是个读表格的库后来处理的数据量上来了、业务复杂度上来了才发现以前对它的理解有多浅。这篇文章不是讲Pandas入门也不是把官方文档抄一遍而是想把从“会操作”到“会分析”这段路上的关键点、踩过的坑、沉淀下来的方法完整地整理出来。这篇进阶笔记适合谁已经掌握了Python基础语法知道DataFrame和Series是什么但处理数据还是靠for循环一层层写、看文档找函数、对数据清洗和性能优化没什么方向感的同学。换句话说你不需要再学DataFrame是什么但你需要知道在真实项目中怎么把它用好、用快、用出效率。全文会围绕数据分析的完整流程来展开环境配置、数据加载、清洗、类型转换、分组聚合、时间序列、多表整合、性能优化和报错排查。每个环节我都会给出实际项目中能直接套用的写法以及常规文档里不会告诉你的细节。1. 数据加载与初探读文件只是开始数据分析的起点是拿数据。看起来就是pd.read_csv()或pd.read_excel()一句话的事但实际项目里文件来源五花八门编码、分隔符、表头、数据类型都会影响加载结果。这个环节花的时间不长但踩坑率极高。1.1 read_csv和read_excel的高频参数用法先说read_csv。很多初学者只知道pd.read_csv(file.csv)但实际业务中经常碰到这些情况文件是GBK编码、分隔符是分号、首行不是表头、某个列本来就是字符串但要保留前导零。这些都要靠参数解决。import pandas as pd # 实际项目中更完整的读取姿势 df pd.read_csv( data/orders.csv, sep,, # 分隔符可以是正则表达式比如\s encodingutf-8, # 遇到乱码就换成gbk或gb18030 header0, # 第0行作为列名没有表头就写None skiprows[1, 3], # 跳过指定行比如文件里夹了几行说明 usecols[order_id, amount, created_at], # 只读取需要的列 dtype{order_id: str}, # 强行指定列类型订单号绝不能读成数值 parse_dates[created_at], # 把日期字符串直接转成datetime类型 )这里我最想提醒的是dtype参数。订单号、身份证号、电话号码这类字段如果用默认方式读取会被转成int64或float64前面的0全没了甚至变成科学计数法事后想挽救非常麻烦。正确做法是一开始就用dtype指定为字符串。read_excel的情况类似但多一个引擎概念。读取.xlsx文件底层需要openpyxl读取旧版.xls需要xlrd。df_excel pd.read_excel( data/sales_report.xlsx, sheet_name华东区, # 按表名读取也可以传0/1等索引 engineopenpyxl, # 指定引擎版本冲突时用这招 skiprows2, # Excel里前两行可能是合并单元格的标题说明 )有个很实用的排查技巧不确定文件是什么编码时先用open()读前几行试试或者用Python自带的chardet库检测。别猜猜一次错一次。1.2 加载后必做的三件事shape、info、head数据加载完成后不要急着做分析先看三样东西行数列数、每列的类型和非空情况、前几行的数据长什么样。df.shape # 返回(行数, 列数)马上知道数据规模 df.info() # 每列的非空计数、数据类型、内存占用一眼看出有没有缺失 df.head() # 默认前5行检查数据长什么样这三句话能解决80%的“数据到底怎么回事”的疑问。info()尤其有用如果发现数值列变成了object说明里面混进了文本比如有空字符串或者像“1,234”这种带千分位符号的脏数据。如果发现日期列还是object后面做时间序列分析时肯定要转换。我还习惯加一句df.columns.tolist()把列名全部打出来。因为原始数据的列名可能带空格、带换行符、甚至一模一样重复提前看清楚能避免后面写代码时一遍遍回头查。2. 数据清洗实战数据质量决定分析下限数据分析圈有句老话数据清洗占整个分析流程的60%~80%时间。别嫌夸张真实项目就是这样。Pandas进阶的核心能力很大程度上就体现在清洗代码写得干不干净、快不快、稳不稳。2.1 缺失值处理dropna和fillna的取舍缺失值是最常见的问题。处理逻辑其实不复杂要么删要么填。关键在于什么场景下删、什么场景下填、用什么值填。# 删除方向控制 df.dropna(axis0) # 删除含缺失值的行默认howany df.dropna(axis1) # 删除含缺失值的列常用于直接丢弃整列无用数据 df.dropna(subset[email]) # 只在关键列上判断不要求所有列都完整 # 填充方向控制 df[amount].fillna(0) # 数值缺失填0 df[remark].fillna(无备注) # 文本缺失填固定标记 df[score].fillna(df[score].median()) # 用中位数填充抗异常值 df[amount].fillna(methodffill) # 用前一个有效值填充 df[amount].fillna(methodbfill) # 用后一个有效值填充关于删除和填充的取舍我的经验是这样的如果缺失值占比超过30%别盲目填充先搞清楚为什么缺失。可能是业务上本来就没填也可能是采集环节丢了。前者可以用特定标记填充后者可能需要回溯源数据。时序数据适合用ffill或bfill比如传感器每隔几秒上报一次偶尔丢几条用前后值补上很合理。数值列别一律填0如果缺失值代表“没发生交易”填0没问题如果代表“数据没拿到”填0会把平均值、总和全部拉偏。还有inplaceTrue这个老生常谈的坑。Pandas里的很多方法默认返回新对象不改原数据。如果你想在原DataFrame上直接修改可以用inplaceTrue但我的建议是除非在交互式环境里临时用正式脚本里千万别用。因为inplaceTrue会导致无法链式调用也很难跟踪数据在哪个环节被改的。规范写法是df df.dropna(subset[email])可读性更好作用也更清晰。2.2 数据类型转换astype的常见局限astype是核心的类型转换方法但实际用下来有几个坑。字符串转数值。数据里经常出现“1,234.56”这类带千分位符号的文本直接用astype(float)会报错。需要先清洗再转换。# 错误写法会抛出 ValueError # df[salary] df[salary].astype(float) # 正确写法 df[salary] ( df[salary] .str.replace(,, , regexFalse) # 去掉千分位逗号 .astype(float) )数值转字符串。需要注意NaN的存在astype(str)会把NaN变成字符串nan这在后续逻辑中非常危险。建议用fillna先处理。df[order_id] df[order_id].fillna(0).astype(int64).astype(str)字符串转日期。这是最常用的一类转换但日期格式五花八门2018/05/19、2018年5月19日、2018-05-19 10:30:00都可能出现。pd.to_datetime可以处理大部分情况但遇到奇葩格式还是要自己解析。df[created_at] pd.to_datetime(df[created_at], format%Y/%m/%d, errorscoerce) df[date_only] df[created_at].dt.date # 只保留日期部分 df[year_month] df[created_at].dt.to_period(M) # 转成年月errorscoerce非常关键遇到解析不了的字符串不会直接抛错中断而是填成NaTNot a Time之后我们可以单独查看哪些行没解析成功再针对性处理。2.3 重复值、异常值和列操作重复值处理相对简单但有个细节容易被忽略。df.duplicated().sum() # 查看重复行数 df.drop_duplicates() # 默认保留第一条删除其余 df.drop_duplicates(subset[user_id, date]) # 按指定列判断重复 df.drop_duplicates(keepFalse) # 全部删除一个不留异常值处理就需要结合业务。比如订单金额出现负数技术上看不是异常但业务上明显不合理。我的做法是先画箱线图或用describe()看分位数找出极端值再判断是数据错误还是真实情况比如退款。df[amount].describe() # 看min、max、25%、75%、mean数值规模一目了然 # 超过3倍标准差可视为异常值 mean df[amount].mean() std df[amount].std() df_outlier df[(df[amount] mean 3 * std) | (df[amount] mean - 3 * std)]列的重命名和删除在Pandas里脑子要清楚drop是删行或删列rename是改名。df df.rename(columns{旧名: 新名, number: count}) df df.drop(columns[无用列1, 无用列2]) df df[[需要的列1, 需要的列2, 需要的列3]] # 用列名选择来调整顺序drop还有一个冷门但有用的功能删除行时用索引标签。df df.drop(index[2, 5, 9]) # 删除索引为2、5、9的行 df df.drop(axis0, labels[2, 5, 9]) # 等价写法3. 分组聚合与透视从明细数据到业务视角清洗完之后真正开始“分析”往往是从分组开始的。结果要按区域汇总、按月份汇总、按渠道对比这些需求底层都是groupby。3.1 groupby核心玩法分组、聚合、转换、过滤groupby本身做的事情很简单把数据按某些列的值拆成多组然后对每组应用同样的操作。但它的高级之处在于不同操作目的对应不同的方法。df.groupby(region)[amount].sum() # 每个区域的销售额 df.groupby([region, month])[amount].mean() # 多级分组 df.groupby(region)[[amount, quantity]].agg([sum, mean, count])多列多函数聚合时agg方法最灵活。我用得最多的几种写法import numpy as np df.groupby(region).agg( total_amount(amount, sum), avg_amount(amount, mean), order_count(order_id, count), max_qty(quantity, max), ) # 如果想对不同列用不同函数下面的写法可读性更好 df.groupby(region).agg( total_amount(amount, sum), total_qty(quantity, sum), )agg之后结果是个DataFrame列名按新列名(原列名, 函数)的元组映射生成非常直观。transform是很多人没接触过但极其实用的功能。它的特点是返回的DataFrame和原数据形状相同不会减少行数。典型场景是计算每个订单占该用户总订单金额的比例。df[user_total] df.groupby(user_id)[amount].transform(sum) df[order_share] df[amount] / df[user_total]这一步如果用普通思路要先groupby算出用户总额再merge回去而transform一步到位代码量少了一圈。filter用于筛选分组它的作用对象是组而不是行。比如只要订单数超过10个的用户。df.groupby(user_id).filter(lambda g: g[order_id].count() 10)再补充一个性能细节groupby的key列尽量用纯字符串或整数类型如果用object类型匹配耗时明显增加。数据量达到百万行时先df[user_id] df[user_id].astype(category)再做groupby内存占用和耗时都会有明显改善。3.2 pivot_table和crosstab把数据变成报表透视表是Excel用户一定不陌生的概念Pandas里的pivot_table能做同样的事情而且更灵活。# 行是区域列是月份值是销售额 pd.pivot_table( df, valuesamount, indexregion, columnsmonth, aggfuncsum, fill_value0, ) # 多级索引一张表同时看区域×渠道×月份 pd.pivot_table( df, valuesamount, index[region, channel], columnsmonth, aggfuncsum, marginsTrue, # 增加总计行和总计列 margins_name合计, )marginsTrue这个参数我强烈推荐它自动生成行列小计非常适合做报表。crosstab本质上也是透视表但它对输入数据的形式更友好——它接收两个一维Series用来计算频数交叉表非常方便。# 统计各渠道下新老用户的订单占比 ct pd.crosstab(df[channel], df[user_type], normalizeindex)normalizeindex会按行归一化输出变成百分比行这样图表分析时可以直接用省去了手动计算的步骤。如果统计值是金额之类的数值就直接用pivot_tablecrosstab更适合频数统计。3.3 实战把订单明细转成日报表简单演示一个从明细到报表的完整链路这也是日常工作中出现频率极高的需求。# 模拟一个订单表 raw_df pd.read_csv(orders.csv, parse_dates[order_time]) # 生成日期维度 raw_df[order_date] raw_df[order_time].dt.date # 按日期渠道汇总 daily_report ( raw_df .groupby([order_date, channel]) .agg( order_cnt(order_id, count), gmv(amount, sum), refund_cnt(refund_flag, sum), ) .reset_index() ) # 透视成报表行是日期列是渠道值是订单数 order_pivot daily_report.pivot_table( indexorder_date, columnschannel, valuesorder_cnt, aggfuncsum, fill_value0, ) # 增加一列“当日总订单数” order_pivot[总计] order_pivot.sum(axis1) # 保存到Excel order_pivot.to_excel(daily_order_report.xlsx, sheet_name每日订单)这段代码里有一个细节值得展开groupby之后默认会以分组的列作为索引。如果要继续做透视或图表reset_index()把分组列降级成普通列往往更方便否则后面操作经常被多层索引搞晕。这也是很多初学者卡壳的地方——看一眼daily_report就明白。4. 多表数据整合join、merge、concat怎么选真实项目中想分析的字段通常不在同一张表里。订单表里有用户ID但没有用户城市用户表里有城市但没有订单。把多张表整合起来是必修技能。4.1 merge是核心SQL思维映射到Pandasmerge其实就是Pandas里的JOIN理解方式可以完全对标SQL。orders pd.read_csv(orders.csv) users pd.read_csv(users.csv) # 内连接只要两个表匹配上的行 df_inner orders.merge(users, onuser_id, howinner) # 左连接保留订单表全部行用户表匹配不上就填NaN df_left orders.merge(users, onuser_id, howleft) # 右连接和全外连接 df_right orders.merge(users, onuser_id, howright) df_outer orders.merge(users, onuser_id, howouter) # 连接键在两表列名不同时 df orders.merge(users, left_onuid, right_onuser_id, howleft)实际项目中howleft用得最多因为它能保证主要表的完整性——比如分析订单时即使某些用户信息缺失订单也不能丢。连接键是多个时传入一个列表df orders.merge(users, on[user_id, date], howleft)还有一类高频场景需要根据某个范围去关联比如判断订单日期是否落在某次活动的起止日期内。merge默认是“相等匹配”不支持范围匹配这时可以用pd.merge_asof它专门处理时间或数值上的“最近匹配”。# 合并订单表和价格表取小于等于订单时间的最近一条价格 prices pd.read_csv(prices.csv, parse_dates[price_date]) df pd.merge_asof( orders.sort_values(order_time), prices.sort_values(price_date), left_onorder_time, right_onprice_date, directionbackward, )4.2 concat纵向堆叠比横向连接更常用concat的典型场景是多个月的数据文件结构相同需要纵向堆叠成一个全量DataFrame。import glob all_files glob.glob(data/orders_*.csv) df_list [pd.read_csv(f) for f in all_files] df_all pd.concat(df_list, ignore_indexTrue)注意ignore_indexTrue因为每个文件的行索引都是从0开始的直接拼接会出现重复索引后续按索引取数会有大麻烦。concat也可以横向拼接但横向拼接时建议优先用merge因为concat是按位置对齐的一旦两张表的行顺序不一致就会出错。merge则是按指定的唯一键对齐语义更清晰。# 横向拼接这种场景下merge更靠谱 df_merged df_a.merge(df_b, left_indexTrue, right_indexTrue, howouter)4.3 连接后的字段冲突破解两张表有同名但含义不同的列时merge会自动生成列名_x和列名_y。比如订单表有amount表示订单金额用户表也有amount表示累计消费金额合并后会出现两个amount。这时候我一般会在合并前先重命名users users.rename(columns{amount: user_total_amount}) df orders.merge(users, onuser_id, howleft)提前把列名规划好比合并后再去清理省心太多。5. 时间序列处理按时间维度做分析的核心能力电商看流量趋势、金融看收益率曲线、运营看用户活跃度几乎所有行业的数据分析都离不开时间维度。Pandas在时间序列处理上提供了相当完整的方法集。5.1 把字符串列转成时间类型to_datetime全面解析第一步永远是转换类型。上面提过pd.to_datetime这里再补充几个高频场景。df[date] pd.to_datetime(df[date], format%Y-%m-%d, errorscoerce) df[month] df[date].dt.to_period(M) # 聚合到月 df[week] df[date].dt.to_period(W) # 聚合到周 df[dayofweek] df[date].dt.dayofweek # 周几从0开始 df[hour] df[date].dt.hour # 提取小时dt这个访问器在时间序列操作中很重要它允许我们批量提取年、月、日、小时、星期等属性。还有一个容易忽略但好用的功能dt.diff()可以直接计算相邻时间差。如果要把日期列设为索引便于用时间索引切片df df.set_index(date).sort_index() df.loc[2023-06] # 直接取出6月全部数据 df.loc[2023-06-01:2023-06-15] # 切片取前半个月5.2 resample重采样按时间颗粒度汇总利器resample是Pandas时间序列功能的精华对标的是SQL里date_trunc后再group by的操作。# 按日汇总销售额 df.resample(D).agg({amount: sum}) # 按周汇总W-MON表示周一开始算一周 df.resample(W-MON).agg({amount: sum}) # 按月汇总 df.resample(MS).agg({amount: sum, order_cnt: sum}) # 按季度 df.resample(Q).agg({amount: sum})resample要求索引是时间类型否则会报TypeError: Only valid with DatetimeIndex。如果你手上的DataFrame还没设置时间索引resample之前记得set_index。时间序列里还有一个非常实用的rolling窗口函数。移动平均能平滑噪音展示趋势。# 7日移动平均销售额 df[amount_7d_avg] df[amount].rolling(window7, min_periods3).mean()min_periods3表示至少要有3个有效数据才计算这样前几天的窗口没有足够数据时不会变成NaN。做趋势图时这个参数特别重要否则图上前面几天全是空值。6. 性能优化处理大数据量时的实用技巧数据量一上来比如几百万行Pandas如果用得不好速度慢到怀疑人生。这一节聊聊我的实战优化思路。6.1 向量化操作别在DataFrame里写for循环最常见的问题就是遍历行。很多人习惯这样写# 反例逐行遍历效率极低 df[category_label] for idx, row in df.iterrows(): if row[amount] 1000: df.loc[idx, category_label] 大额 else: df.loc[idx, category_label] 普通Pandas的设计初衷就是向量化计算。上面的逻辑一行搞定df[category_label] np.where(df[amount] 1000, 大额, 普通)更复杂的多条件逻辑用np.selectconditions [ df[amount] 10000, df[amount] 1000, df[amount] 0, ] choices [超大额, 大额, 小额] df[amount_level] np.select(conditions, choices, default非正数)apply比iterrows快不少但本质上仍然不是向量化它的底层还是按行调用Python函数。数据量几万行时无所谓几百万行时差别就很明显了。# apply写法能用但仍有性能损耗 df[amount_level] df[amount].apply( lambda x: 超大额 if x 10000 else (大额 if x 1000 else 小额) )最理想的是利用Pandas的str、dt、astype等原生向量化方法。实在需要逐行计算时优化顺序是向量化 apply itertuples iterrows。6.2 dtypes优化与分块读取Pandas在加载数据时默认会为每列分配一个合适但不一定最优的数据类型。比如int64在只存0~100的数值时就显得浪费占8字节而int8只占1字节。# 查看每列内存占用 df.memory_usage(deepTrue) # 按量级压缩数值列 df[int_col] df[int_col].astype(int32) df[small_int] df[small_int].astype(int8) # 对重复度高的文本列用category类型 df[region] df[region].astype(category)category类型在处理像城市、地区、渠道这种取值有限的文本列时非常有效。它底层用整数编码存储内存可以压缩掉一大半。做groupby时因为匹配的是整数编码速度也更快。文件特别大的时候比如几个GB的CSVpd.read_csv会直接把内存吃满更好的方案是分块读取。chunk_iter pd.read_csv(huge_data.csv, chunksize100000) result [] for chunk in chunk_iter: # 对每个分块做必要清洗和加工 chunk chunk[chunk[amount] 0] result.append(chunk.groupby(region)[amount].sum()) # 最后汇总所有分块的结果 total pd.concat(result).groupby(level0).sum()用chunksize返回一个迭代器每次只处理10万行内存占用保持平稳。虽然代码稍复杂但至少程序不会崩。另一个减少内存的好习惯是读数据时用usecols只保留需要的列。SQL是先select再处理数据分析也一样别把用不到的字段全拉进来。6.3 小技巧把中间结果做瘦身真实项目中数据会经过多步处理每步都可能产生新列。很多人的中间DataFrame越滚越大列数从10列变成30列虚拟内存都快撑不住。我的建议是每完成一个分析阶段就把不需要的列drop掉或者用需要保留的列重新取一遍。df_clean df[[user_id, order_date, amount, channel]].copy()用.copy()也很重要。Pandas里很多操作返回的是原数据的视图view而不是副本copy后续如果对这个视图做修改很容易触发SettingWithCopyWarning。新手常被这个警告搞懵本质就是分不清哪些是引用、哪些是新对象。凡是基于DataFrame切片出来的数据还要再改值的先.copy()准没错。7. 实战案例从两张表到一份完整分析报告理论说多了容易飘还是落到一个完整的小项目上。需求分析某电商平台6月份订单情况按用户地区统计销售额和订单量找出贡献前10%的高价值用户并输出一份Excel报告。7.1 模拟数据和核心代码我先模拟两个文件orders.csv和users.csv然后跑完整分析。import pandas as pd import numpy as np # 读取数据 orders pd.read_csv(orders.csv, parse_dates[order_time]) users pd.read_csv(users.csv) # 1. 数据清洗 orders orders.dropna(subset[order_id, amount]) orders[amount] pd.to_numeric(orders[amount], errorscoerce) orders orders[orders[amount] 0] # 2. 生成报表地区维度 orders_users orders.merge(users[[user_id, region]], onuser_id, howleft) region_report ( orders_users .groupby(region) .agg(order_cnt(order_id, count), gmv(amount, sum)) .reset_index() .sort_values(gmv, ascendingFalse) ) # 3. 高价值用户识别 user_value ( orders .groupby(user_id) .agg(user_gmv(amount, sum), order_cnt(order_id, count)) .reset_index() ) top_threshold user_value[user_gmv].quantile(0.9) top_users user_value[user_value[user_gmv] top_threshold] # 4. 写入Excel多个sheet with pd.ExcelWriter(report_6月.xlsx, engineopenpyxl) as writer: region_report.to_excel(writer, sheet_name地区报表, indexFalse) top_users.to_excel(writer, sheet_name高价值用户, indexFalse)这段代码看起来不长但代表了数据清洗、数据整合、分组聚合、分位数筛选、报表导出这几个完整环节。实际写代码时每一步的中间结果我都习惯打印一眼比如region_report.head()、top_users.shape边跑边确认避免最后一步才发现前面处理有误。7.2 细节复盘这段代码的四个关键设计第一个关键设计合并前只取需要的列。users[[user_id, region]]因为users表可能还有几十个无关字段提前瘦身既减少内存又防止字段冲突。第二个关键设计pd.to_numeric(errorscoerce)把可能的脏值转成NaN再配合后面的dropna清掉。清洗顺序是先处理关键字段的错误值再删脏数据最后做业务过滤。第三个关键设计用quantile(0.9)代替写死一个金额阈值。这样“前10%高价值用户”的判定是相对全局的即使数据整体水平变化逻辑仍然成立不会出现某个月阈值明显偏高或偏低的情况。第四个关键设计用ExcelWriter上下文管理器一次性写入多个sheet。to_excel单独调用时每次会覆盖整个文件写入有了ExcelWriter才能真正实现多表分sheet导出。8. 常见报错与排查技巧速查整理几个出现频率高的报错附带排查思路方便大家直接对照。8.1 高频报错对照表报错信息出现场景排查方向KeyError: xxx按列名取数据不存在先用df.columns.tolist()确认列名拼写注意列名内可能有空格SettingWithCopyWarning对切片后的DataFrame赋值切片后加.copy()或者改用df.loc直接操作原DataFrameValueError: cannot convert float NaN to integer含NaN的列转int时报错先fillna或将NaN所在行剔除再转MemoryError数据量超过内存用chunksize分块读取或只读需要的列或压缩数据类型InvalidFileError/ModuleNotFoundErrorread_excel找不到对应引擎安装openpyxl或xlrd或者指定engine参数ParserError: Error tokenizing data读CSV时分隔符或引号问题检查sep参数查看原始文件的前几行Only valid with DatetimeIndex用时间索引操作但索引不是时间类型set_index并确保转换to_datetime成功ValueError: cannot reindex from a duplicate axis尝试沿索引对齐时索引重复先用df.index.is_unique检查再用reset_index或drop_duplicates处理8.2 排查报错的通用思路遇到报错不要急着一行行看代码。我的固定套路是三条先看报错类型和最后一行错误信息。Pandas报错信息往往在最后一行才说明真正原因前面堆的错误栈只是调用过程。再看涉及的数据类型。info()和dtypes能快速确认列是不是object、有没有NaN。很多报错的根源就是类型不匹配尤其是字符串和数值混合的列。最后做最小化测试。挑三五行数据只保留出问题的那个操作在交互环境里逐步执行。这种方式比反复运行完整脚本效率高得多。还有一个容易被忽略的点如果你用的是较新版本的Pandas某些方法可能已经弃用或行为变化。比如append方法在Pandas 2.0后就不能用了要改用concat。所以遇到奇怪报错时先看一下pd.__version__确认版本再去对应版本的文档查一遍能省不少时间。8.3 数据处理阶段的防呆建议在清洗阶段多花点心思做防护能避免后面一堆问题。我通常在脚本里加两行确认assert df[order_id].notna().all(), 订单号仍有缺失 assert (df[amount] 0).all(), 存在负数金额assert的好处是数据不符合预期时立即中断并且给出明确提示。数据量再大也不带怕的程序自动帮你揪出问题数据。9. 我对Pandas进阶学习的三个建议学习Pandas最容易掉进去的坑是背函数。今天记一个groupby明天记一个pivot_table学完就忘用的时候还是查文档。我个人的体会是更好的方式是把学习和真实需求绑定在一起。第一个建议给自己找一个能坚持做一周的真实数据集。比如公开的电商订单数据、本地的公交刷卡数据、甚至是自己的银行卡账单。真实数据的好处是它足够脏——有缺失、有异常、有重复这些才是练习数据处理的最佳素材。第二个建议先提高“读文档”的能力。Pandas官方文档写得非常详细每个函数都有参数说明和示例。遇到问题先去读官方文档比在搜索引擎里大海捞针效率高得多。特别是需要搞清楚某个参数的作用时直接看df.method?就能调出帮助文档。第三个建议定期用小项目巩固知识。不需要多复杂今天就做一个“按周汇总销售并输出Excel报表”明天做一个“找出连续三天有成交的用户”。每次做完一个完整闭环你对Pandas操作的理解就会加深一层。最后分享一个小技巧我用Pandas几年下来最深的体会是“一切皆Series一切皆向量化”。每当你发现自己想写for循环遍历DataFrame时停下来逼自己想想有没有用列运算直接解决的问题。有的话你的Pandas水平就已经超过了一大半人。