Pandas 只提供一个东西:DataFrame,一张带行列标签的表。这一章和第 4 章都在玩它。 read_excel 一行就能把 xlsx 读进来,背后调的是 openpyxl。 读进来先做两件事:head() 看五行、shape 看形状。十秒钟,能挡掉后面九成的错。
第 2 章把环境搭好了,这章开始干活。全章十三篇只围绕一个对象转:DataFrame。 本课的数据是一份 10 行的手写成绩表,本机 pandas 是 3.1.0,所有输出都是真跑出来的。
一、不用 Pandas 会怎样
一份 xlsx 文件,内部是压缩的 XML。自己解析要写上百行,还得处理合并单元格、日期格式、公式。 用 Pandas:read_excel("成绩表.xlsx"),一行。 读进来的东西不是列表,也不是二维数组,而是一个 DataFrame——带行列标签的二维表。
这层标签是关键。列表只有位置,DataFrame 有名字,所以可以「按姓名取张伟的分」,而不是「取第三个」。
二、读进来第一眼该看什么
先 head():默认看前五行,检查列名对不对、值有没有错位。
| 姓名 | 班级 | 语文 | 数学 | 英语 | 总分 |
|---|---|---|---|---|---|
| 张伟 | 一班 | 88 | 92 | 85 | 265 |
| 王芳 | 一班 | 92 | 95 | 90 | 277 |
| 李娜 | 一班 | 75 | 68 | 80 | 223 |
再 shape:10 行、6 列。列名是姓名、班级、语文、数学、英语、总分。 这两步加起来十秒,能挡掉后面九成的「数算错了」——多数错误在读进来那一步就发生了。
三、每一列的类型
dtypes 打出每列的类型:语文、数学、英语、总分是 int64,能直接算。 姓名和班级是 str——注意它不是老版本里的 object。这是 pandas 3.0 之后的新默认。 类型不对,后面全错:本该是数字的列如果是 str,sum 会变成字符串拼接,不报错。
所以看到「求和结果是一长串数字连在一起」,就是这个原因。astype(int) 能救回来。
四、一行代码看清全貌
describe() 给每个数值列算八个数:计数、均值、标准差、最小、四分位、最大。
| 统计量 | 语文 | 数学 | 英语 |
|---|---|---|---|
| count | 10.0 | 10.0 | 10.0 |
| mean | 82.70 | 83.00 | 81.90 |
| std | 8.99 | 10.66 | 10.14 |
| min | 68.0 | 65.0 | 60.0 |
| max | 95.0 | 95.0 | 95.0 |
一眼能看出:数学均值最高、标准差也最大,说明这科分得最开。 describe 只统计数值列,姓名和班级会被自动跳过。这不是 bug,是设计。
五、为什么它是数据分析的主力
三个原因。一,读取方便:Excel、csv、txt、网页、数据库、JSON,全都能一行读进来。 二,操作像说话:筛、排、分组、合并、透视,每个动作一两个函数,不用写循环。 三,跟别的库都通:numpy 算、matplotlib 画、sklearn 建模,接口都是 DataFrame。
所以后面几章不管讲什么,起点都是「先把数据读成 DataFrame」。
六、动手 Demo:第一次把 Excel 读进来
先造一份 10 行成绩表写到 xlsx,再用 read_excel 读回来。 依次打 head、shape、columns、dtypes、describe 五个东西。
import pandas as pd
DF = pd.DataFrame({
"姓名": ["张伟", "王芳", "李娜", "刘洋", "陈静",
"杨帆", "赵敏", "黄磊", "周涛", "吴霞"],
"班级": ["一班"] * 5 + ["二班"] * 5,
"语文": [88, 92, 75, 85, 90, 68, 95, 72, 83, 79],
"数学": [92, 95, 68, 88, 73, 82, 91, 65, 86, 90],
"英语": [85, 90, 80, 79, 95, 71, 88, 60, 84, 87],
})
DF["总分"] = DF["语文"] + DF["数学"] + DF["英语"]
DF.to_excel("成绩表.xlsx", index=False)
df = pd.read_excel("成绩表.xlsx")
print("形状 %s" % (df.shape,))
print("列名 %s" % list(df.columns))
print(df.head(3).to_string(index=False))
print("类型"); print(df.dtypes.to_string())
print(df.describe().round(2).to_string())
实跑输出:
--------------------------------------------------------------------
第一次把 Excel 读进来
--------------------------------------------------------------------
① 为什么要用 Pandas 读,而不是自己解析
--------------------------------------------------------------------
Excel 文件内部是压缩的 XML,自己解析要写上百行,还得处理合并单元格、日期格式。
Pandas 的 read_excel 一行搞定,背后调的是 openpyxl。本机 openpyxl 是 3.1.5。
读进来的东西叫 DataFrame,不是列表、不是二维数组,是一张带行列标签的表。
! DataFrame 是这一章和第 4 章唯一的对象。所有操作都是对它做的。
② 读进来第一眼该看什么
--------------------------------------------------------------------
先 head() 看前五行,看列名对不对、值有没有错位。
| 行 | 姓名 | 班级 | 语文 | 数学 | 英语 | 总分 |
|---|---:|---:|---:|---:|---:|---:|
| 0 | 张伟 | 一班 | 88 | 92 | 85 | 265 |
| 1 | 王芳 | 一班 | 92 | 95 | 90 | 277 |
| 2 | 李娜 | 一班 | 75 | 68 | 80 | 223 |
| 3 | 刘洋 | 一班 | 85 | 88 | 79 | 252 |
| 4 | 陈静 | 一班 | 90 | 73 | 95 | 258 |
再看 shape:10 行、6 列。
列名是:姓名、班级、语文、数学、英语、总分。
! 这两步加起来 10 秒,能挡掉后面 90% 的「数算错了」——多数错误在读进来那一步就发生了。
③ 每一列是什么类型
--------------------------------------------------------------------
| 行 | dtype |
|---|---:|
| 姓名 | str |
| 班级 | str |
| 语文 | int64 |
| 数学 | int64 |
| 英语 | int64 |
| 总分 | int64 |
语文、数学、英语、总分是 int64,能直接算。
姓名和班级是 str,注意它不是普通的 object——这是 pandas 3.0 之后的新默认。
! 类型不对,后面全错:本该是数字的列如果是 str,sum 会变成字符串拼接。
④ 一行代码看清全貌
--------------------------------------------------------------------
describe() 给每个数值列算八个数:计数、均值、标准差、最小、四分位、最大。
| 行 | 语文 | 数学 | 英语 | 总分 |
|---|---:|---:|---:|---:|
| count | 10.0 | 10.0 | 10.0 | 10.0 |
| mean | 82.7 | 83.0 | 81.9 | 247.6 |
| std | 8.99 | 10.66 | 10.14 | 25.74 |
| min | 68.0 | 65.0 | 60.0 | 197.0 |
| 25% | 76.0 | 75.25 | 79.25 | 230.25 |
| 50% | 84.0 | 87.0 | 84.5 | 254.5 |
| 75% | 89.5 | 90.75 | 87.75 | 263.25 |
| max | 95.0 | 95.0 | 95.0 | 277.0 |
这一张表就能看出哪一科分数普遍高、哪一科拖后腿。
! describe 只统计数值列,姓名和班级会被自动跳过——这不是 bug,是设计。
to_excel 要 index=False,否则行号会被写成一列,下次读进来多一列怪东西。
to_string(index=False) 是打印用的,它把表格变成纯文本。
不加 index=False 会带上行号,肉眼看更清楚。
30 秒实验
- 把 head(3) 改成 head(10),能看出总分是怎么算的吗?手算一行验证。
- 把某一列的分数改成字符串(比如 "88"),再看 dtypes,sum 会发生什么?
- 想一想:为什么 describe 不统计姓名和班级?
常见误区
- 不写 index=False 就导出:下次读进来会多一列 unnamed,很碍事。
- 只用 head 不看 dtypes:类型错了不报错,只是算出来的数不对。
- 以为 DataFrame 是二维数组:它带标签,所以能按名字取,数组不能。
- 读进来就急着算:先看形状和类型,两分钟省两小时。
自测三问(点开对答案)
1. DataFrame 和二维数组最根本的区别是什么? —— DataFrame 有行列标签,能按名字取数据;数组只有位置。 2. 为什么 pandas 3 里字符串列的类型显示成 str 而不是 object? —— pandas 3.0 起默认用专门的 StringDtype 存字符串,比 object 省内存,也是为将来做准备。 3. describe 输出的 25%、50%、75% 是什么意思? —— 分位数。50% 就是中位数,25% 表示有四分之一的数比它小。DataFrame 是一张表,表里的一列单独拿出来是什么?下一篇讲 Series——它比列表多了一组标签。
- ☐ (本篇完,下一篇 3.2 Series:带标签的一列数)