
如果你和我一样每天都要面对成堆的表格、CSV、数据库导出的杂乱数据那你大概率绕不开 Pandas。最近我在 HoRain云 上整理了一批数据处理脚本把日常项目里最常用的一批核心函数系统过了一遍越用越觉得“核心函数”这四个字比想象中值钱很多你以为很复杂的操作其实都是几个基础函数组合出来的。这篇文章不搞花哨炫技只讲实际项目里真正高频、能帮你把数据快速整理干净的 Pandas 核心函数按“读数据、洗数据、变数据、聚数据”这条主流程来拆适合刚从 Excel 迈向 Python 的初级选手也适合经常写数据处理脚本但总在一些小地方卡壳的进阶用户。1. 内容整体设计与思路拆解1.1 为什么每个数据处理项目都离不开这批函数我在不同环境里写过数据处理脚本最大的感受是Pandas 的 API 极其庞大但真正每天都被反复调用的也就是那二三十个函数。很多人在学习时喜欢把 DataFrame 里所有方法都翻一遍结果记得多、用得少等到真正面对脏数据时反而不知道从哪下手。核心函数之所以“核心”是因为它们正好对应了数据处理的三类刚需读取解析、清洗修正、重塑聚合。只要把这三类吃透大部分 Excel 手工操作都能被脚本取代。比如merge对应 VLOOKUPgroupby对应数据透视表apply对应单元格级自定义公式fillna对应查找替换空值。用代码做数据处理的本质就是把你在表格界面里的一次次鼠标点击翻译成一行行可复用、可追溯、可回放的逻辑。1.2 数据处理的标准流水线我在处理一个模拟项目时无论数据来自日志、业务导出还是公开数据集主线流程基本都是固定的数据读取把外部数据装入 DataFrame初步探查理解数据规模、类型、缺失情况数据清洗处理缺失值、重复值、类型问题、异常值数据变换筛选、排序、新增列、长宽表转换分组聚合按某种维度汇总、合并多张表、处理时间序列结果输出导出为 CSV、Excel或进入可视化环节这套流水线在云上跑也一样适用。比如我在 HoRain云 的 Notebook 环境里读取的是对象存储上的数据文件处理完再写回。核心函数本身不挑环境但云上有个额外好处内存、CPU 资源容易弹性调整适合处理单机跑不动的大文件。1.3 环境准备与版本选择做任何数据处理前先把环境定下来。我一般用 Python 3.9 搭配 pandas 1.5.x 或 2.x外加 NumPy。安装很简单pip install pandas numpy openpyxl这里必须提醒一句openpyxl是读写 Excel 的后端引擎很多人在read_excel时报错就是因为没装这个包。云上的基础镜像通常预装了 pandas 和 numpy但 Excel 相关依赖不一定有第一次用前先跑一下import openpyxl确认。另一个容易被忽略的是版本差异。pandas 2.x 在有些 API 上调用了新的数据类型比如read_csv默认的字符串类型更规范某些操作对空字符串的处理也更严格。所以团队协作时最好把环境版本固定写进requirements.txt避免“本地跑得好好的部署到云上就报错”的尴尬。2. 数据读取与初步探查2.1 read_csv 和 read_excel 的高频参数我碰到过最离谱的需求是一个业务方给的 CSV 文件分隔符不是逗号而是分号第一行还不是表头。如果不懂参数光这两点就能卡住。read_csv的常用参数务必熟练import pandas as pd df pd.read_csv( data.csv, sep;, # 分隔符默认逗号 encodingutf-8, # 编码中文数据常用 utf-8 / gbk header0, # 指定表头行号None 表示无表头 names[id, name], # 无表头时手动指定列名 dtype{id: str}, # 指定列类型防止ID被读成数字 parse_dates[date], # 自动解析日期列 nrows10000, # 只读前1万行快速预览 )读 Excel 时路径层次很容易混read_excel的sheet_name可以直接填工作表名称也可以填索引从 0 开始。我习惯用名称因为 Excel 里工作表顺序常常被人挪动。注意数据量大时nrows参数只适合探查真正全量读取后要先确认文件行数与预期一致。我曾经碰到过文件里有大量空行read_csv默认会把空行当成 NaN 行读进来后续统计时莫名多了很多条记录。2.2 用 info、describe、shape 快速摸清数据底细读完数据先不要急着清洗一定要先“望闻问切”。我的固定组合是print(df.shape) # 行数、列数 print(df.info()) # 每列的非空计数和数据类型 print(df.describe()) # 数值列的统计描述 print(df.head(10)) # 头部样例info()看的是数据集的“体检报告”哪列有缺失、哪列类型不对、总共多少内存占用。describe()能暴露很多玄机比如某列最大值离谱、某列均值明显偏离中位数都说明可能存在异常值。至于head()和tail()则是用肉眼确认清洗方向的前提。2.3 索引处理数据的第一根拐杖很多初学者吃亏在索引上。df.loc[2]取的是“索引标签为 2”的行而df.iloc[2]取的是“第 3 行”。如果索引经过筛选后不再连续这两个的差别会直接造成取数错误。df_sorted df.sort_values(salary, ascendingFalse) # 排序后索引还是原来的顺序如果想变成 0,1,2... df_sorted df_sorted.reset_index(dropTrue)set_index则可以把业务主键设置成索引比如订单号、用户ID后续用df.loc[order_1001]取数就很快也方便与其他表做索引对齐。3. 数据清洗核心函数3.1 缺失值处理先摸清缺失模式再决定删还是填处理缺失值最怕一刀切。我见过有人看到有 NaN 就直接dropna()结果把整表数据删得只剩一半。正确的姿势是先搞清楚缺失分布print(df.isna().sum()) print(df.isna().mean() * 100) # 缺失占比isna()返回同形状的布尔 DataFramesum()之后就能看到每列缺失数量想看百分比就除以行数。接下来按列性质决定策略关键业务字段缺失较多且无法推断时删除整行数值型字段可用均值、中位数填充时间序列数据建议用前向或后向填充分类字段可填充“未知”或该列众数df[age] df[age].fillna(df[age].median()) df[join_date] df[join_date].fillna(methodffill) # 向前填充 df.dropna(subset[order_id], inplaceTrue) # 删除关键字段缺失dropna的subset参数很多人不知道它能指定只在某些列做缺失判断而不是整行全空才删。thresh参数则是“非空值数量阈值”保留有效信息较多的行比如df.dropna(thresh5)表示一行至少要有 5 个非空值才保留。3.2 重复值处理先确认重复范围再决定保留策略重复值不只是行完全一样才算重复。业务上一个用户可能有多条记录但其中某几个关键字段重复就足以判断为异常。duplicated和drop_duplicates都支持subset参数df.duplicated(subset[user_id, order_id], keepFalse) df.drop_duplicates(subset[user_id], keeplast, inplaceTrue)keep参数有三个值first保留第一条last保留最后一条False删除所有重复行。我最常踩的坑是想找出重复项时用了keepFalse结果把只出现一次的也标成 True因为keepFalse会把所有重复组内成员都标记出来而单条记录不属于重复。3.3 类型转换与异常值过滤SQL 里有个说法垃圾进、垃圾出。Pandas 里类型混乱就是典型的垃圾入口。最常见的情况是ID 列明明是字符串却被读成了数字前导零全没了金额列里混入了逗号或人民币符号。解决办法是astype和to_numericdf[id] df[id].astype(str) df[amount] pd.to_numeric(df[amount].str.replace(¥, ).str.replace(,, ), errorscoerce)errorscoerce会把无法转换的值变成 NaN这一步再配合缺失值处理就能定位脏数据。我一般会在转换后立刻看一下bad df[df[amount].isna()]用clip限制数值范围也很实用尤其处理明显的录入错误时df[age] df[age].clip(lower0, upper120)3.4 文本清洗str 访问器是文本列的万能工具Pandas 里文本列通过.str访问器暴露字符串方法几乎把 Python 原生字符串方法都搬了过来。我做用户信息清洗时最常见的就是空格、大小写、前后缀问题df[name] df[name].str.strip() # 去首尾空格 df[city] df[city].str.upper() # 统一大写 df[phone] df[phone].str.replace(-, , regexFalse) df[category] df[category].str.split(|).str[0]regexFalse这个参数是血泪教训。默认情况下replace把模式当正则表达式如果你只想替换普通字符串必须显式关闭正则否则括号、点号这类字符会跑出奇怪结果。4. 数据变换核心函数4.1 排序、改名与排名排序是高频刚需。sort_values支持多列、升降序组合df.sort_values(by[group, score], ascending[True, False], inplaceTrue)rename改名时列名映射字典的 key 是“旧列名”value 是“新列名”这个方向很多人第一次写反df.rename(columns{buy_cnt: purchase_count, amt: total_amount}, inplaceTrue)rank在竞赛式排名场景里非常好用method参数控制并列处理average取平均名次min取最小名次dense是连续排名。比如销量榜相同销量希望名次不跳号就选methoddense。4.2 条件筛选query 让代码像 SQL用df[df[age] 18]这种写法看久了眼睛累字符串式的query可读性高很多adults df.query(age 18 and city in [北京, 上海]) top df.query(0 score 100)query对列名里的空格和特殊字符处理有门槛变量引用需要加前缀min_age 18 adults df.query(age min_age)loc和iloc是精确取数的底牌。df.loc[df[a] 0, [col1, col2]]一次完成“条件筛选 列选择”。between在区间筛选时更直观df[score].between(60, 90)。4.3 apply 家族别把所有活都丢给 applyapply是万能工具但也是性能陷阱。很多新手遇到不规则需求就 apply实际上map、applymap、向量化操作各有适用场景df[gender_label] df[gender].map({M: 男, F: 女}) df[[height_cm, weight_kg]] df[[height_m, weight_kg]].apply(pd.to_numeric)规则单列值映射是标量字典用map整行或整列做函数操作用applyDataFrame 每个元素逐一操作用applymap现在很多场景可以另用map能直接用算术运算的绝不 apply比如df[bmi] df[weight] / (df[height] ** 2)比 apply 快一个量级assign是链式写法里新增列的利器它不会修改原 DataFrame非常适合一步步组装新表df df.assign( amount_yuanlambda x: x[amount] / 100, category_upperlambda x: x[category].str.upper(), )4.4 长宽表转换melt 和 pivot 是透视表的灵魂Excel 里的透视表在 Pandas 里就是pivot_table。它的参数对应关系非常直观index是透视表的行区域columns是列区域values是数值区域aggfunc是汇总方式。pivot pd.pivot_table( df, indexcategory, columnsregion, valuessales, aggfuncsum, fill_value0, )反过来宽表变长表用melt。我从业务系统导出的宽表经常是“每个月份一列”合计 12 列做趋势分析前必须先变形long_df df.melt( id_vars[product_id], value_vars[2024-01, 2024-02, 2024-03], var_namemonth, value_namesales, )stack和unstack处理多层索引时很好用unstack可以把行索引转到列stack反之。业务上不常用但一旦遇到多层索引的聚合结果这两个是救命级函数。5. 分组聚合核心函数5.1 groupby先拆分再应用最后合并groupby是 Pandas 的灵魂也是从“会处理表”到“会分析数据”的分水岭。它的执行逻辑可以拆成三步按指定列分组、对每组应用聚合逻辑、把结果拼接起来。我经常在脑子里把它想成“SQL 的 GROUP BY 各类聚合函数”。常用写法# 单列聚合 df.groupby(category)[sales].sum() # 多列多聚合 result df.groupby([category, region]).agg( total_sales(sales, sum), avg_price(price, mean), order_count(order_id, count), )agg中传入元组列表非常灵活左边是新列名右边是聚合方式。比起groupby([category]).agg({sales: [sum, mean]})生成多层列名这种方式更适合直接导出报表。transform能在原 DataFrame 保持行数不变的前提下把聚合结果广播回去。比如计算“每个人销售额占所属组总销售额的比例”df[group_total] df.groupby(category)[sales].transform(sum) df[pct] df[sales] / df[group_total]filter则可以对分组做整体筛选比如只保留订单数大于 100 的分组df.groupby(category).filter(lambda x: x[order_id].count() 100)5.2 窗口函数rolling、expanding、shift做时间序列或者滑动统计时rolling是默认选择。滚动平均是消除波动的第一招df[sales_ma7] df[sales].rolling(window7).mean()expanding算累计值比如累计销售额、从首日到当日的最大值df[cum_sales] df[sales].expanding().sum()shift是构造“上一行 / 下一行”数据的利器尤其在算环比、同比时df[previous_sales] df[sales].shift(1) df[mom_growth] (df[sales] - df[previous_sales]) / df[previous_sales] * 100遇到“每天只取最近 N 条”这种需求可以用分组内 rolling 固定窗口或者用sort_valuesgroupby().head(n)。很多人不知道分组里也能直接head(3)它比循环拼接高效得多。5.3 merge、concat、join多表组合的三种姿势多表数据合并是核心函数里最像“正规军”的部分。merge对应 SQL 的 JOIN核心参数是how和ondf_orders.merge(df_users, howleft, onuser_id) df_orders.merge(df_users, howinner, left_onuid, right_onid)how的可选值我总结成一句话inner是取交集left、right分别是保留左表或右表全部行outer是并集。on用在两边列名相同的情况不同就用left_onright_on。concat更多用于纵向拼接比如把几个月的数据拼到一张表all_df pd.concat([df_month1, df_month2], axis0, ignore_indexTrue)axis0是上下拼axis1是左右拼。业务报表中常常需要把多张结构相同的表上下拼接ignore_indexTrue会在拼接后重新生成连续索引避免索引重复造成的取数错乱。join是基于索引的合并适合两个表都有相同索引但列不同时。它没有merge用的多但是写完set_index之后直接join会产生非常简洁的效果。5.4 时间序列to_datetime、resample、dt 访问器时间列在业务数据里几乎无孔不入。我拿到任何表第一件事是把时间列转成datetime64类型df[date] pd.to_datetime(df[date], format%Y/%m/%d)format参数是一个大救星。读取时如果日期格式混杂不指定 format 会非常慢pandas 会自动推断但在大数据量场景下推断耗时长且容易误判。指定 format 后速度能提升数倍。resample是时间序列的 groupby。它要求索引是时间类型df.set_index(date, inplaceTrue) daily df[sales].resample(D).sum() monthly df[sales].resample(M).mean() weekly df[sales].resample(W-MON).sum()频率代码是硬记的知识点D日、W周、M月末、MS月初、Q季度、H小时。我用M和MS时经常搞混后来记住一句话大写的M是“月末”带S的MS是“月初Start”。dt访问器用于从时间列里提取月份、星期、小时等成分df[month] df[date].dt.month df[weekday] df[date].dt.dayofweek # 0周一 df[hour] df[date].dt.hour6. 常见问题与排查技巧实录6.1 内存告警读大文件时怎么省内存在云上处理几百 MB 甚至上 GB 的数据时最烦的莫过于MemoryError。我实际项目里总结了几招。第一招只读需要的列和行df pd.read_csv(big.csv, usecols[id, date, sales], nrows500000)usecols能极大减少内存占用因为 pandas 不会把没选中的列解析进内存。第二招对能确定类型但默认推断不合理的列指定dtype比如布尔列、短文本列。第三招分批读取后合并结果chunks pd.read_csv(big.csv, chunksize100000) result [] for chunk in chunks: result.append(chunk.groupby(category)[sales].sum()) final pd.concat(result).groupby(level0).sum()6.2 链式赋值的坑为什么设置值没生效新手最困惑的问题之一是df[df[a] 0][b] 1之后发现原表没变。这不是 bug而是 pandas 的链式赋值语义。修改数据时优先统一用locdf.loc[df[a] 0, b] 1用loc能确保修改的是原 DataFrame 的视图而不是一个副本。排查此类问题时可以把pd.set_option(mode.chained_assignment, raise)打开它会在链式赋值时抛出异常帮你快速定位位置。6.3 聚合结果列名过长或混乱groupby().agg({sales: [sum, mean]})之后列名会变成多层索引导出 Excel 时不直观。两个解决办法直接在agg里用元组命名或者事后展平列名df.columns [_.join(col).strip() for col in df.columns.values]如果是元组命名方式这一步基本可以省略。我在做报表输出时习惯最后统一重命名一遍确保交付的表头是中文或业务术语。6.4 云上环境运行 Pandas 的实操建议在云上环境比如我常用的 HoRain云 Notebook跑数据有几个和本机不一样的地方。一是数据文件通常要先下载到本地工作目录或者直接挂在对象存储路径下我用read_csv时习惯先用!ls -lh确认文件大小。二是云上环境可能会限制当前工作目录的写权限导出结果时先确认路径能写import os print(os.getcwd())三是多云环境经常启用多个 CPUmultiprocessing配合简单任务并不直观我一般优先用 pandas 本身的向量化能力而不是盲目并行。四是注意运行时长和空闲超时策略长时间任务尽量拆成小批次跑或者把中间结果落盘防止一次会话断掉前功尽弃。6.5 核心函数速查场景对照表场景核心函数一句话要点读 CSVread_csv记得处理分隔符、编码、表头预览数据head/info/describe先看规模、类型、缺失再动手缺失值统计isna().sum()缺失占比决定删还是填重复值删除drop_duplicatessubset指定判断列类型转换astype/to_numeric转换失败用errorscoerce文本清洗str.strip()/str.replace()regexFalse防正则误伤条件筛选query/loc变量引用加新增列assign/map向量化优于逐行 apply宽表转长表meltid_vars固定分类列透视表pivot_tableaggfunc写清汇总方式分组聚合groupby().agg()元组命名新列滚动均值rolling().mean()固定窗口滑过时间轴多表合并mergehow决定保留哪些行纵向拼接concatignore_indexTrue避免索引重复时间处理to_datetime/resampleformat指定格式提速6.6 我的实操经验补充最后分享几个只有自己亲手敲过代码才会注意的细节。第一凡是赋值操作养成指定inplaceTrue或重新赋值的好习惯避免混淆。现在很多项目越来越强调“无 inplace 写法”我也慢慢转向返回新对象的风格因为它在链式操作里更安全。第二排错时善用单行小 sample。我会把业务关键数据切成几十行的小表所有操作先在小表上跑通再去全量跑。小表打印出来肉眼可读效率远高于盯着大报错信息猜。第三脚本里保留关键节点的形状输出。跑一步打印一次(行数, 列数)能很快发现哪一步多出了数据或者弄丢了数据。这招在长时间数据任务里尤其管用。第四Excel 导入的日期列经常是真正的日期格式但 CSV 里可能是字符串。统一用pd.to_datetime处理时遇到无法解析的格式errorscoerce会让它变成 NaN然后你再回头去查原始数据比一次次报错来得舒服。我在实际项目里把上面这些函数串起来通常一个数据处理脚本不会超过一百行却能把几十个 Excel 手工操作替换成分钟级完成的任务。Pandas 的函数虽多核心思路始终是“读、洗、变、聚”四步。把这四步里的高频函数用熟遇到再复杂的业务表也能拆成这几个动作的组合来解决。有一段时间我还特地把每个新需求对应到速查表上发现绝大多数场景都能落到我列出的那些函数里。希望你也能在 HoRain云 或者自己本机环境里把这几个组合跑一遍踩坑之后才是自己的东西。