1. 为什么要学 Pandas:数据分析的起点
在做数据分析之前,很多人的第一反应是打开 Excel。Excel 确实足够直观,但当数据量达到几十万行、几百万行,或者需要重复执行清洗、汇总、关联操作时,Excel 就会变得力不从心。这时候,Pandas 就是 Python 生态里最核心的数据分析利器。
Pandas 是一个基于 Python 的开源数据分析库,名字来源于“Panel Data”(面板数据)。它提供了两类核心数据结构:Series(一维)和DataFrame(二维)。借助这两个结构,你可以完成数据读取、清洗、转换、聚合、可视化等一系列操作。
在实际开发中,Pandas 常见的应用场景包括:
- 数据清洗:处理缺失值、重复值、异常值。
- 数据转换:类型转换、字段抽取、分箱、归一化。
- 数据聚合:分组统计、透视表、多表关联。
- 数据输出:把处理结果导出为 Excel、CSV、数据库表。
- 与机器学习联动:为 sklearn、XGBoost 等模型准备训练数据。
本文会围绕一套“由浅入深”的学习路径:先理解核心数据结构,再掌握数据读写,接着完成数据清洗和数据分析实战案例,最后补充常见问题和工程建议。文章中的代码均基于 Python 3 + Pandas 编写,读者可以直接复制运行。
2. Pandas 环境准备与安装
在开始写代码之前,先确认环境中已经安装了 Pandas。
2.1 检查 Python 版本
Pandas 对 Python 版本有一定要求。写教程时当前稳定版本通常支持 Python 3.9 及以上。如果你使用的是 Python 3.10,一般可以直接安装最新版 Pandas;如果使用更高版本或更老版本,建议先查看官方安装文档。
打开终端(Windows 为 CMD 或 PowerShell,macOS / Linux 为 Terminal),执行:
python --version如果提示python不是内部或外部命令,可以尝试:
python3 --version2.2 安装 Pandas
使用 pip 安装:
pip install pandas如果你的环境同时存在多个 Python 版本,建议使用虚拟环境,避免包冲突。例如使用 venv:
python -m venv .venv .venv\Scripts\activate # Windows source .venv/bin/activate # macOS / Linux pip install pandas安装完成后,验证版本:
import pandas as pd print(pd.__version__)看到类似2.1.4的输出,说明安装成功。
2.3 搭配工具推荐
Pandas 只是一个库,还需要一个运行环境。常用选择:
- Jupyter Notebook:适合交互式分析,数据展示直观。
- PyCharm:适合写完整脚本和工程化分析代码。
- VS Code + Python 插件:轻量、灵活。
本文示例在 Jupyter Notebook 和 PyCharm 中均可运行,关键是要把示例代码按顺序复制到同一个.py文件或 Notebook Cell 中。
3. 核心数据结构:Series 和 DataFrame
学习 Pandas,第一步不是背 API,而是弄清楚两个核心对象——Series和DataFrame。
3.1 Series:带索引的一维数组
Series可以理解为“带标签的一维数组”。它由两部分组成:
- 数据部分:任意类型(整数、浮点数、字符串、对象)。
- 索引部分:每个数据的标签,默认是
0, 1, 2, ...。
创建一个最简单的 Series:
import pandas as pd s = pd.Series([10, 20, 30, 40]) print(s)输出:
0 10 1 20 2 30 3 40 dtype: int64你也可以显式指定索引:
s = pd.Series([10, 20, 30], index=['a', 'b', 'c']) print(s)输出:
a 10 b 20 c 30 dtype: int64这里的索引相当于“行名”,在数据分析中很有用,比如按月份、按城市作为索引。
3.2 DataFrame:带行列标签的二维表格
DataFrame是 Pandas 最常用的数据结构,可以理解成一张 Excel 表或一张数据库表。它由行索引(index)和列名(columns)组成。
创建一个 DataFrame:
import pandas as pd df = pd.DataFrame({ '姓名': ['张三', '李四', '王五'], '部门': ['技术部', '产品部', '运营部'], '薪资': [15000, 13000, 12000] }) print(df)输出:
姓名 部门 薪资 0 张三 技术部 15000 1 李四 产品部 13000 2 王五 运营部 12000创建后,可以查看一些常用属性:
print(df.shape) # 几行几列 print(df.columns) # 列名 print(df.index) # 行索引 print(df.dtypes) # 每一列的数据类型 print(df.info()) # 数据概览df.info()的输出会显示行数、列数、非空值数量和每列类型,这对于检查数据完整性非常有用。
3.3 从列表、字典、NumPy 数组创建
除了字典方式,还可以从列表嵌套创建:
df2 = pd.DataFrame([ ['张三', '技术部', 15000], ['李四', '产品部', 13000] ], columns=['姓名', '部门', '薪资'])从 NumPy 数组创建:
import numpy as np arr = np.array([[1, 2], [3, 4]]) df3 = pd.DataFrame(arr, columns=['A', 'B'])理解这两个结构之后,就可以开始做“正事”了——读取数据。
4. 数据读取与写入:CSV、Excel、Parquet
数据分析的第一步通常是把外部数据载入到 Pandas 中。常用的数据源包括 CSV、Excel、Parquet、Feather 和数据库。
4.1 读取 CSV 文件
import pandas as pd df = pd.read_csv('sales_data.csv') print(df.head())head()默认显示前 5 行,用于快速预览数据。如果 CSV 文件没有列名,可以指定header=None,并手动设置列名:
df = pd.read_csv('sales_data.csv', header=None, names=['日期', '销售额', '订单数'])如果文件编码不是 UTF-8,会出现乱码。比如中文 CSV 经常使用gbk编码,可以这样读取:
df = pd.read_csv('sales_data.csv', encoding='gbk')4.2 读取 Excel 文件
读取 Excel 需要额外的openpyxl或xlrd库,先安装:
pip install openpyxl然后读取:
df = pd.read_excel('sales_data.xlsx', sheet_name='Sheet1')如果 Excel 里有多个 Sheet,可以用sheet_name=None读取全部,返回一个字典,key 是 Sheet 名,value 是对应的 DataFrame。
4.3 读取 Parquet 和 Feather
在大数据场景下,Parquet 和 Feather 是两种高效的列式存储格式。很多从 Spark、Hive 同步过来的数据会直接落地为 Parquet,用 Pandas 读取非常方便。
df = pd.read_parquet('sales_data.parquet')读取 Feather:
df = pd.read_feather('sales_data.feather')需要提醒的是,read_parquet依赖pyarrow或fastparquet。安装命令:
pip install pyarrow写回文件时,可以这样:
# 导出为 CSV df.to_csv('output_data.csv', index=False) # 导出为 Excel df.to_excel('output_data.xlsx', index=False) # 导出为 Parquet df.to_parquet('output_data.parquet') # 导出为 Feather df.to_feather('output_data.feather')index=False的意思是不要把行索引导出为一列,通常更符合分析需求。
4.4 读取数据库表
如果数据在 MySQL 或 PostgreSQL 中,可以用read_sql:
import pandas as pd from sqlalchemy import create_engine engine = create_engine('mysql+pymysql://用户名:密码@localhost:3306/数据库名') df = pd.read_sql('SELECT * FROM sales_table', con=engine)读取数据库前,务必确认权限和网络是否允许,生产环境不要随便用高权限账号执行操作。
5. 数据清洗实战:缺失值、重复值、类型转换
真实场景里的数据很少是干净的,数据清洗是 Pandas 学习中最耗时间的部分。下面结合一个业务示例演示。
先构造一份“员工销售数据”,故意加入缺失值和重复值:
import pandas as pd import numpy as np data = { '员工ID': ['E001', 'E002', 'E003', 'E004', 'E005', 'E001'], '姓名': ['张伟', '李娜', '王强', '赵敏', '刘洋', '张伟'], '部门': ['销售一部', '销售二部', '销售一部', np.nan, '销售三部', '销售一部'], '销售额': [12000, 9800, None, 15000, 8800, 12000], '日期': ['2024-01-05', '2024-01-06', '2024-01-07', '2024-01-08', '2024-01-09', '2024-01-05'] } df = pd.DataFrame(data) print(df)输出:
员工ID 姓名 部门 销售额 日期 0 E001 张伟 销售一部 12000.0 2024-01-05 1 E002 李娜 销售二部 9800.0 2024-01-06 2 E003 王强 销售一部 NaN 2024-01-07 3 E004 赵敏 NaN 15000.0 2024-01-08 4 E005 刘洋 销售三部 8800.0 2024-01-09 5 E001 张伟 销售一部 12000.0 2024-01-05可以看到,这份数据既有缺失值(NaN),又有完全重复的行(第 0 行和第 5 行),日期列也是字符串类型而不是时间类型。
5.1 检查缺失值
print(df.isnull()) print(df.isnull().sum())isnull()会返回一个同形状的布尔 DataFrame,sum()按列统计缺失值数量。
5.2 处理缺失值
处理缺失值有两种思路:删除和填充。
删除包含缺失值的行:
df_drop = df.dropna() print(df_drop)填充缺失值,比如销售额缺失,用该列均值填充:
df['销售额'] = df['销售额'].fillna(df['销售额'].mean())部门缺失,用“未知部门”填充:
df['部门'] = df['部门'].fillna('未知部门')注意:fillna返回的是新 DataFrame,不会原地修改,因此需要重新赋值,或者设置inplace=True。不过现在更推荐直接赋值的方式,可读性更好。
5.3 删除重复值
根据所有列判断重复行:
df = df.drop_duplicates()如果只想根据“员工ID”判断重复,保留第一条:
df = df.drop_duplicates(subset=['员工ID'], keep='first')热搜词里提到“如果指定两列的值均相同,则取第一条数据”,写法就是:
df = df.drop_duplicates(subset=['员工ID', '姓名'], keep='first')keep='first'表示保留重复组中的第一条,keep='last'保留最后一条,keep=False删除所有重复行。
5.4 数据类型转换
Pandas 中常见的数据类型包括int64、float64、object、datetime64等。处理日期字符串时,需要转为时间类型:
df['日期'] = pd.to_datetime(df['日期']) print(df.dtypes)把销售额从浮点型转成整数型:
df['销售额'] = df['销售额'].astype(int)数据清洗后,再看一眼数据:
print(df)输出:
员工ID 姓名 部门 销售额 日期 0 E001 张伟 销售一部 12000 2024-01-05 1 E002 李娜 销售二部 9800 2024-01-06 3 E004 赵敏 未知部门 15000 2024-01-08 4 E005 刘洋 销售三部 8800 2024-01-09这样就得到一个相对干净、可继续分析的数据集。
6. 数据分析实战:筛选、排序、分组聚合
数据清洗完成后,就进入真正的分析阶段。这里用一个更完整的数据集,模拟“某公司销售订单明细”。
import pandas as pd sales_df = pd.DataFrame({ '订单号': ['A001', 'A002', 'A003', 'A004', 'A005', 'A006'], '地区': ['华东', '华北', '华东', '华南', '华北', '华南'], '品类': ['手机', '电脑', '手机', '平板', '手机', '电脑'], '销售额': [5000, 8000, 4500, 6000, 5200, 9200], '利润': [800, 1200, 600, 900, 700, 1500] }) print(sales_df)6.1 按条件筛选数据
筛选销售额大于 5000 的记录:
high_sales = sales_df[sales_df['销售额'] > 5000] print(high_sales)多条件筛选,用&表示“且”,用|表示“或”。注意每个条件都要加括号:
# 华东地区且销售额大于5000 result = sales_df[(sales_df['地区'] == '华东') & (sales_df['销售额'] > 5000)] print(result)6.2 排序
按销售额降序排序:
sorted_df = sales_df.sort_values('销售额', ascending=False) print(sorted_df)多列排序:
sorted_df = sales_df.sort_values(['地区', '销售额'], ascending=[True, False])6.3 分组聚合
分组聚合是 Pandas 最核心的能力之一。比如统计每个地区销售额总和:
grouped = sales_df.groupby('地区')['销售额'].sum() print(grouped)输出:
地区 华东 9500 华北 13200 华南 15200 Name: 销售额, dtype: int64也可以一次统计多个指标:
result = sales_df.groupby('地区').agg({ '销售额': ['sum', 'mean', 'count'], '利润': 'sum' }) print(result)这里的agg可以传入字典,key 是列名,value 是要执行的聚合函数。还可以用列表:
result = sales_df.groupby('地区')['销售额'].agg(['sum', 'mean', 'max', 'min'])6.4 多列分组
按“地区 + 品类”两个维度统计:
result = sales_df.groupby(['地区', '品类'])['销售额'].sum().reset_index() print(result)reset_index()的作用是把分组后的索引重新变成普通列,方便后续导出或继续处理。
6.5 透视表
透视表能更直观地展示二维汇总结果:
pivot_df = pd.pivot_table( sales_df, values='销售额', index='地区', columns='品类', aggfunc='sum', fill_value=0 ) print(pivot_df)输出:
品类 平板 手机 电脑 地区 华东 0 9500 0 华北 0 5200 8000 华南 6000 0 9200这就类似于 Excel 透视表,在分析报表中非常常见。
7. 数据可视化:用 Pandas + Matplotlib 快速出图
数据分析的最后一环往往是可视化。Pandas 本身不提供绘图能力,但可以很方便地调用 Matplotlib。
7.1 安装和导入
pip install matplotlib在代码中启用 Matplotlib 并设置中文显示(避免中文乱码):
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows 常用黑体 plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题macOS 或 Linux 可以换成['Arial Unicode MS']或系统支持的中文字体。
7.2 柱状图
按地区汇总销售额后画柱状图:
sales_by_region = sales_df.groupby('地区')['销售额'].sum() sales_by_region.plot(kind='bar', title='各地区销售额对比') plt.ylabel('销售额') plt.show()7.3 折线图
如果有时间序列数据,折线图更适合:
time_df = pd.DataFrame({ '月份': ['2024-01', '2024-02', '2024-03', '2024-04'], '销售额': [2000, 3500, 2800, 4200] }) time_df.plot(x='月份', y='销售额', kind='line', marker='o', title='月度销售额趋势') plt.ylabel('销售额') plt.show()7.4 饼图
展示品类占比:
sales_by_category = sales_df.groupby('品类')['销售额'].sum() sales_by_category.plot(kind='pie', autopct='%1.1f%%', title='品类销售额占比') plt.ylabel('') plt.show()可视化的核心目的是让数据结论一目了然。实际项目中不需要花哨,清晰、准确更重要。
8. 常见问题与排查思路
8.1 常见报错速查表
下面列出新手使用 Pandas 时最常遇到的几类问题,以及排查思路。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
ModuleNotFoundError: No module named 'pandas' | 尚未安装,或装到了另一个 Python 环境 | 确认当前解释器,执行pip install pandas |
| 读取 CSV 中文乱码 | 编码格式不对 | 指定encoding='gbk'或encoding='utf-8' |
| 列名有空格或中文,代码报错 | 用df.列名访问有局限 | 优先使用df['列名'] |
| 修改列后数据未生效 | 忘记重新赋值 | Pandas 大多数操作返回新对象,需df = df.dropna() |
KeyError: '列名' | 列名拼写错误或不存在 | 用df.columns查看实际列名 |
| groupby 后想转回普通表 | 索引层级复杂 | 使用.reset_index() |
| 安装 pandas 报错,版本冲突 | Python 与 Pandas 版本不匹配 | 查阅官方文档,升级 Python 或指定兼容版本 |
8.2 关于 Python 3.10 与 Pandas 的版本搭配
如果你的 Python 是 3.10,最新的 Pandas 稳定版一般都支持。比较稳妥的方式是:
pip install --upgrade pandas如果安装后导入报错,比如缺少依赖库,可以先安装numpy、python-dateutil、pytz等基础依赖,再重装 Pandas。
8.3 处理大文件时内存不足
如果数据量很大,比如几个 GB 的 CSV,推荐分块读取:
chunk_iter = pd.read_csv('big_file.csv', chunksize=10000) result_parts = [] for chunk in chunk_iter: # 先做清洗 chunk = chunk.dropna() # 再做聚合 result_parts.append(chunk.groupby('类目')['销售额'].sum()) final_result = pd.concat(result_parts).groupby(level=0).sum()分块读取的核心思路是:每次只处理 10000 行,减少内存占用,最后合并结果。
9. 最佳实践与工程建议
写了几年代码之后,你会发现 Pandas 入门不难,但要把代码写得稳定、可维护、可复用,还是需要一些工程经验的积累。下面分享几组实用建议。
9.1 用自定义函数组织数据处理流程
直接写一大段脚本,后期维护很痛苦。建议把每个环节封装成函数:
def load_data(file_path): return pd.read_csv(file_path) def clean_data(df): df = df.drop_duplicates(subset=['订单号']) df = df.dropna(subset=['销售额']) df['日期'] = pd.to_datetime(df['日期']) return df def analyze_data(df): return df.groupby('地区')['销售额'].sum().reset_index() def main(): df = load_data('sales.csv') df = clean_data(df) result = analyze_data(df) result.to_csv('sales_result.csv', index=False) if __name__ == '__main__': main()这样每一步都可以单独测试,也方便复用。
9.2 尽量少用inplace=True
inplace=True会原地修改 DataFrame,虽然省内存,但在管道式处理和调试时容易产生副作用。推荐写法是“生成新对象并重新赋值”,逻辑更清晰,也便于链式操作。
9.3 注意链式赋值的坑
有些新手会这样写:
df[df['销售额'] > 5000]['标记'] = '高'这被称为“链式赋值”,可能只修改了副本而不是原表,甚至会触发SettingWithCopyWarning。正确做法是:
df.loc[df['销售额'] > 5000, '标记'] = '高'loc是先按条件定位,再修改列,稳定又明确。
9.4 数据备份与操作安全
涉及修改原始数据时,尤其是从数据库临时表或生产环境取数据,建议先做好备份:
df_backup = df.copy()这样即使清洗逻辑写错了,也可以快速回退,不需要重新读一遍数据。
9.5 用nunique、value_counts辅助快速探查
数据分析前先摸清数据基本情况:
# 每个城市的数量分布 print(df['城市'].value_counts()) # 每个员工覆盖了多少个城市 print(df.groupby('员工')['城市'].nunique())这些指令能帮你快速判断数据完整性和业务规则,比直接跑统计模型更有价值。
10. 实战扩展:金融风控场景中的 Pandas 应用
数据分析本身是方法,落地到具体领域才有生命力。这里以一个金融风控场景为例,演示 Pandas 的完整用法,也能帮你巩固前面所有内容。
假设有一份“用户交易记录”数据,字段包括:用户ID、交易金额、交易时间、是否命中风控规则。
import pandas as pd import numpy as np trade_data = pd.DataFrame({ '用户ID': ['U001', 'U002', 'U003', 'U001', 'U004', 'U002'], '交易金额': [2000, 8500, 300, 15000, 5000, None], '交易时间': ['2024-03-01 10:00:00', '2024-03-01 10:05:00', '2024-03-01 10:10:00', '2024-03-02 09:30:00', '2024-03-02 09:45:00', '2024-03-02 10:00:00'], '命中规则': ['否', '是', '否', '是', '否', '是'] })第一步:清洗。把时间转为 datetime,删除空交易金额。
trade_data['交易时间'] = pd.to_datetime(trade_data['交易时间']) trade_data = trade_data.dropna(subset=['交易金额'])第二步:按用户聚合。统计每个用户的交易次数、总金额、最大单笔金额、平均金额。
user_stats = trade_data.groupby('用户ID').agg({ '交易金额': ['count', 'sum', 'max', 'mean'] }).round(2) print(user_stats)第三步:找出可疑用户。比如单日交易次数过多,或者命中风控规则的占比过高。这里简单演示“交易总金额大于 10000 的用户”:
user_total = trade_data.groupby('用户ID')['交易金额'].sum() high_risk_users = user_total[user_total > 10000] print(high_risk_users)输出:
用户ID U001 17000.0 Name: 交易金额, dtype: float64这个案例虽然简单,但完整覆盖了“读取、清洗、分组聚合、筛选”四条主线。金融风控场景中,还需要结合时间窗口分析、落库、监控预警等,这些都可以基于同样的 Pandas 功底逐步扩展。
11. 结束语:Pandas 这条路该怎么继续走
Pandas 是一座很好的桥,它能帮你从 Excel 表格思维过渡到编程化数据分析思维。通过本文,你应该已经掌握:
- Series 和 DataFrame 两个核心数据结构;
- CSV、Excel、Parquet、Feather 等文件的读写方式;
- 缺失值、重复值、类型转换等数据清洗操作;
- 筛选、排序、分组聚合、透视表等分析手段;
- 配合 Matplotlib 输出可视化图表的基本流程;
- 常见报错的排查思路和工程化的写法建议。
下一步,建议按这个顺序继续深入:先自己找一份真实数据集,完整跑一遍清洗和分析流程;然后学习apply自定义函数,解决更复杂的业务逻辑;接着了解merge和concat,掌握多张表关联;最后可以接触 Spark 或 Dask,让 Pandas 分析能力延伸到更大的数据规模。
如果本文对你有帮助,可以收藏备用;遇到具体问题,欢迎在评论区留言交流。