📊 第3章 Pandas 入门
📄 一句话概括

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 秒实验

  1. 把 head(3) 改成 head(10),能看出总分是怎么算的吗?手算一行验证。
  2. 把某一列的分数改成字符串(比如 "88"),再看 dtypes,sum 会发生什么?
  3. 想一想:为什么 describe 不统计姓名和班级?

常见误区

  1. 不写 index=False 就导出:下次读进来会多一列 unnamed,很碍事。
  2. 只用 head 不看 dtypes:类型错了不报错,只是算出来的数不对。
  3. 以为 DataFrame 是二维数组:它带标签,所以能按名字取,数组不能。
  4. 读进来就急着算:先看形状和类型,两分钟省两小时。
自测三问(点开对答案) 1. DataFrame 和二维数组最根本的区别是什么? —— DataFrame 有行列标签,能按名字取数据;数组只有位置。 2. 为什么 pandas 3 里字符串列的类型显示成 str 而不是 object? —— pandas 3.0 起默认用专门的 StringDtype 存字符串,比 object 省内存,也是为将来做准备。 3. describe 输出的 25%、50%、75% 是什么意思? —— 分位数。50% 就是中位数,25% 表示有四分之一的数比它小。

DataFrame 是一张表,表里的一列单独拿出来是什么?下一篇讲 Series——它比列表多了一组标签。

相关笔记