作者:许灼灼
pandas读写数据——一行代码搞定Excel和CSV
一、课堂故事
「上节课我们认识了 DataFrame,」我翻开课件,「大家还记得怎么创建一个 DataFrame 吗?」
小杰举手:「用字典!pd.DataFrame(字典) 就行!」
「没错。但我想问你们一个问题——」我把屏幕切到一个新建的 Python 文件,「如果班主任给你一个 Excel 成绩单,50 个学生,你要先手动把 50 行数据敲进字典,再 pd.DataFrame() 吗?」
全班沉默了三秒。
小婉小声说:「那……也太累了吧。」
「没错。手动建字典适合学习时练手,但真实场景里,数据都在文件里。pandas 的强大之处在于——它可以直接读文件。」
我在屏幕上输入:
import pandas as pd df = pd.read_excel("成绩单.xlsx") print(df.head()) 回车——屏幕上直接打印出成绩单的前 5 行,整整齐齐。
「一行。read_excel,一行代码,50 个学生的数据全部进了 DataFrame。还记得 openpyxl 怎么读的吗?」
小杰翻着笔记:「load_workbook → wb.active → iter_rows 遍历……至少五六行。」
「对。今天我们就来学 pandas 的读写四件套——读 Excel、读 CSV、写 Excel、写 CSV。学完你会明白,为什么说 pandas 是'先苦后甜'里那个'甜'。」
二、概念解释
读写四件套
pandas 读写文件就四个函数,两两配对:
| | |
|---|
| pd.read_excel("文件.xlsx") | df.to_excel("文件.xlsx", index=False) |
| pd.read_csv("文件.csv") | df.to_csv("文件.csv", index=False) |
就这四个。读进来是 DataFrame,写出去是文件。没有遍历,没有循环,没有逐格操作。
和 openpyxl / csv 模块的对比
| | |
|---|
| load_workbook → active → iter_rows 遍历 | pd.read_excel() |
| Workbook → append 逐行 → save | df.to_excel() |
| | pd.read_csv() |
| open → csv.writer → writerows | df.to_csv() |
| | |
index 参数
你可能注意到写文件时都有个 index=False。这又是啥?
DataFrame 自带一个行号索引(0, 1, 2, 3...),就像 Excel 左边的行号。写文件时如果不加 index=False,pandas 会把这个行号也写进文件——多出一列没用的数字。加上 index=False 就是告诉它:「行号别写,我只要数据。」
三、拆开看
3.1 read_excel——一行读 Excel
先准备一个 成绩单.xlsx,内容如下:
用 openpyxl 读(模块五学过的):
from openpyxl import load_workbook wb = load_workbook("成绩单.xlsx") ws = wb.active for row in ws.iter_rows(min_row=2, values_only=True): name, chinese, math, english = row print(name, chinese, math, english) 6 行代码,逐行遍历,手动解包。
用 pandas 读:
import pandas as pd df = pd.read_excel("成绩单.xlsx") print(df.head()) 2 行。完事。
输出:
姓名 语文 数学 英语 0 张三 85 92 78 1 李四 78 88 90 2 王芳 92 95 85 3 赵六 65 72 80
「等一下,」小婉举手,「它怎么知道第一行是表头?」
好问题。read_excel 默认把第一行当表头(列名),下面的行当数据。你不用告诉它,它自己判断。
如果你想指定某一行当表头,用 header 参数:
# 默认 header=0,第一行当表头 df = pd.read_excel("成绩单.xlsx", header=0) # 如果没有表头,第一行就是数据 df = pd.read_excel("成绩单.xlsx", header=None) 如果有多个工作表,用 sheet_name 指定:
# 读第二个工作表(按索引) df = pd.read_excel("成绩单.xlsx", sheet_name=1) # 按名字读 df = pd.read_excel("成绩单.xlsx", sheet_name="期中考试") 3.2 read_csv——一行读 CSV
还记得模块四学的 CSV 吗?用 csv 模块读:
import csv with open("成绩单.csv", encoding="utf-8-sig") as f: reader = csv.reader(f) header = next(reader) # 读表头 for row in reader: print(row) 5 行,还要手动跳表头。
用 pandas 读:
import pandas as pd df = pd.read_csv("成绩单.csv") print(df.head()) 2 行。而且 read_csv 和 read_excel 的用法几乎一模一样——读进来都是 DataFrame,后续操作完全相同。
「老师,CSV 文件如果有中文乱码怎么办?」小杰问。
和模块四一样,加 encoding 参数:
df = pd.read_csv("成绩单.csv", encoding="utf-8-sig") 如果是 Windows 记事本保存的文件,可能需要:
df = pd.read_csv("成绩单.csv", encoding="gbk") 3.3 to_excel——一行写 Excel
现在反过来——把 DataFrame 写成 Excel 文件。
用 openpyxl 写(模块五学过的):
from openpyxl import Workbook wb = Workbook() ws = wb.active ws.append(["姓名", "语文", "数学", "英语"]) ws.append(["张三", 85, 92, 78]) ws.append(["李四", 78, 88, 90]) wb.save("新成绩单.xlsx") 6 行,逐行 append。
用 pandas 写:
import pandas as pd df = pd.DataFrame({ "姓名": ["张三", "李四"], "语文": [85, 78], "数学": [92, 88], "英语": [78, 90] }) df.to_excel("新成绩单.xlsx", index=False) 一行 to_excel 搞定。打开文件,表头、数据、格式全有了。
注意那个 index=False——如果不加:
df.to_excel("新成绩单.xlsx") 写出来的文件会多一列无名的数字(0, 1),就是 DataFrame 的行号。加了 index=False 就干净了。
如果文件已存在,to_excel 会直接覆盖,不问你。要小心。
3.4 to_csv——一行写 CSV
写 CSV 同理:
用 csv 模块写(模块四学过的):
import csv with open("新成绩单.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["姓名", "语文", "数学", "英语"]) writer.writerow(["张三", 85, 92, 78]) writer.writerow(["李四", 78, 88, 90]) 5 行。
用 pandas 写:
df.to_csv("新成绩单.csv", index=False, encoding="utf-8-sig") 一行。index=False 不写行号,encoding="utf-8-sig" 让 Excel 打开不乱码——和模块四一模一样的道理。
3.5 读写四件套对比总表
到这里四件套全学完了,做个总对比:
| | | |
|---|
| load_workbook + iter_rows | pd.read_excel() | |
| | pd.read_csv() | |
| | df.to_excel(index=False) | |
| open + csv.writer + writerow | df.to_csv(index=False) | |
「所以之前学的 openpyxl 和 csv 都白学了?」小婉问。
「当然没有白学。」我笑了,「第一,你通过 openpyxl 理解了 Excel 的结构——工作簿、工作表、单元格。第二,你知道了'手动方式'有多麻烦,现在看到 pandas 一行搞定才会有'哇'的感觉。第三,有些精细操作(比如设置单元格颜色、合并单元格)pandas 做不到,还得靠 openpyxl。」
「工具没有好坏,只有适合不适合。日常读写数据用 pandas,精细调格式用 openpyxl。」
3.6 完整示例——读成绩单→算总分→写新文件
把四件套串起来,做一个真实场景:读 Excel 成绩单 → 算总分和平均分 → 写成新的 Excel 文件 + CSV 文件。
import pandas as pd # 第1步:读 Excel df = pd.read_excel("成绩单.xlsx") print("读取成功!共", len(df), "个学生") print(df) # 第2步:算总分和平均分 df["总分"] = df["语文"] + df["数学"] + df["英语"] df["平均分"] = df["总分"] / 3 df["平均分"] = df["平均分"].round(1) # 保留1位小数 print("\n添加总分和平均分后:") print(df) # 第3步:按总分降序排序 df = df.sort_values("总分", ascending=False) print("\n按总分排名:") print(df) # 第4步:写成新的 Excel 文件 df.to_excel("成绩单_含总分排名.xlsx", index=False) print("\n已保存:成绩单_含总分排名.xlsx") # 第5步:同时写成 CSV 文件 df.to_csv("成绩单_含总分排名.csv", index=False, encoding="utf-8-sig") print("已保存:成绩单_含总分排名.csv") 运行结果:
读取成功!共 4 个学生 姓名 语文 数学 英语 0 张三 85 92 78 1 李四 78 88 90 2 王芳 92 95 85 3 赵六 65 72 80 添加总分和平均分后: 姓名 语文 数学 英语 总分 平均分 0 张三 85 92 78 255 85.0 1 李四 78 88 90 256 85.3 2 王芳 92 95 85 272 90.7 3 赵六 65 72 80 217 72.3 按总分排名: 姓名 语文 数学 英语 总分 平均分 2 王芳 92 95 85 272 90.7 1 李四 78 88 90 256 85.3 0 张三 85 92 78 255 85.0 3 赵六 65 72 80 217 72.3 已保存:成绩单_含总分排名.xlsx 已保存:成绩单_含总分排名.csv
「看到了吗?读文件、算总分、算平均分、排序、写 Excel、写 CSV——整个流程加起来不到 20 行代码。如果用 openpyxl 手写,光遍历和排序就得四五十行。」
「这就是'先苦后甜'——你先学会了苦的方式,才知道甜有多甜。」
小杰看着屏幕,缓缓点头:「我现在懂了。之前学 openpyxl 的时候觉得'还挺方便',现在看到 pandas 才知道什么叫真的方便。」
四、常见坑
坑1:read_excel 报错 ModuleNotFoundError
ModuleNotFoundError: No module named 'openpyxl'
pandas 读 Excel 底层还是要调用 openpyxl,所以两个库都得装:
pip install pandas openpyxl
只装 pandas 不装 openpyxl,读 Excel 就会报错。
坑2:to_excel 忘了 index=False
df.to_excel("成绩单.xlsx") 打开文件发现多了一列无名的数字(0, 1, 2, 3...),就是 DataFrame 的行号索引。
解决:养成习惯,写文件永远带 index=False。
坑3:to_csv 中文乱码
df.to_csv("成绩单.csv", index=False) 用 Excel 打开发现中文全变乱码——和模块四一样的问题。
解决:加 encoding="utf-8-sig":
df.to_csv("成绩单.csv", index=False, encoding="utf-8-sig") 坑4:to_excel 覆盖已有文件不提醒
df.to_excel("成绩单.xlsx", index=False) 如果 成绩单.xlsx 已存在,pandas 直接覆盖,不问你。原来的数据全没了。
解决:写文件前确认文件名不冲突,或者先备份。
坑5:read_csv 编码错误
UnicodeDecodeError: 'utf-8' codec can't decode byte...
默认用 utf-8 编码读,但文件可能是 gbk 编码(Windows 记事本保存的)。
解决:换编码:
df = pd.read_csv("成绩单.csv", encoding="gbk") 常见编码就两种:utf-8-sig(推荐)和 gbk(Windows 记事本)。试一个不行就试另一个。
五、生活类比
搬家公司
想象你要搬家。
openpyxl / csv 模块 = 自己搬:
pandas = 搬家公司来了:
| | |
|---|
| | read_excel() |
| | to_excel() |
| | index=False |
| | |
先苦后甜的意义
你可能会想:「既然 pandas 这么方便,为什么一开始要学 openpyxl?」
因为不经历苦,就不知道甜。
如果你一上来就学 pandas,你会觉得「读 Excel 不就是 read_excel 吗,有什么难的?」——你不会 appreciate 它的方便。
但如果你先用 openpyxl 手动遍历了 50 行,手动解包、手动累加、手动写入——再看到 pandas 一行搞定,那个「哇」的瞬间,才是学习最珍贵的东西。
而且有些事情 pandas 做不了——设置单元格背景色、合并单元格、插入图片——这些精细操作还得靠 openpyxl。pandas 负责「快速搬数据」,openpyxl 负责「精细调格式」,分工合作。
六、练一练
基础题:读 CSV 并查看
准备一个 学生信息.csv 文件(用记事本或 Excel 创建):
姓名,年龄,班级 张三,17,一年一班 李四,16,一年二班 王芳,17,一年一班 赵六,16,一年三班
用 pandas 读取这个文件,打印前 3 行和 shape。
提示:pd.read_csv() + df.head(3) + df.shape
进阶题:读 Excel→算总分→写新文件
用本课的 成绩单.xlsx(或自己造一个),完成以下操作:
- 写成新的 Excel 文件
成绩排名.xlsx(记得 index=False) - 同时写成 CSV 文件
成绩排名.csv(记得 encoding="utf-8-sig")
挑战题:pandas vs openpyxl 对比实验
用两种方式完成同一个任务——读取 成绩单.xlsx,打印所有学生姓名和语文成绩:
方式一(openpyxl):
load_workbook → wb.active → iter_rows 遍历 → 打印
方式二(pandas):
pd.read_excel() → df[["姓名", "语文"]] → print()
对比两种方式的代码行数,写下你的感受。
参考答案(进阶题):
import pandas as pd # 读取 df = pd.read_excel("成绩单.xlsx") # 算总分 df["总分"] = df["语文"] + df["数学"] + df["英语"] # 排序 df = df.sort_values("总分", ascending=False) # 写 Excel df.to_excel("成绩排名.xlsx", index=False) # 写 CSV df.to_csv("成绩排名.csv", index=False, encoding="utf-8-sig") print("完成!") print(df)
下篇预告:pandas 数据分析——describe() 一键统计、sort_values 排序进阶、布尔筛选(df[df["语文"] > 80]),真正的数据分析来了!