视频链接
https://www.bilibili.com/video/BV12TcxzqEBY/
Pandas 速成教程
目标:快速掌握 Pandas 数据分析的核心用法
前夕:Pandas 是什么?与 NumPy 的关系
🎯 Pandas 简介
Pandas 是 Python 最强大的数据分析库,它的名字来源于 "Panel Data"(面板数据)。
如果说 NumPy 是数学计算的基础(专注于数值数组),那么 Pandas 就是数据分析的利器(专注于表格数据)。
🔄 Pandas vs NumPy 对比
| 特性 | NumPy | Pandas |
|---|---|---|
| 核心对象 | ndarray(数组) |
DataFrame(数据框)、Series(序列) |
| 数据类型 | 必须是相同类型 | 可以混合不同类型 |
| 索引方式 | 只能用数字索引 | 可以用自定义标签索引 |
| 数据形式 | 纯数值矩阵 | 带标签的表格(像 Excel) |
| 适用场景 | 科学计算、矩阵运算 | 数据分析、数据清洗、统计 |
| 类比 | 计算器 | Excel 表格 |
💡 两者的关系
NumPy 是地基,Pandas 是建筑
NumPy 提供:高性能数组运算 ← 地基
Pandas 基于 NumPy 构建,增加:标签索引、缺失值处理、数据对齐等功能 ← 建筑
简单理解(表格对比):
NumPy 数组 - 只有纯数据,没有标签:
| 第0列 | 第1列 | 第2列 | |
|---|---|---|---|
| 第0行 | 18 | 85 | 92 |
| 第1行 | 19 | 90 | 88 |
| 第2行 | 18 | 78 | 95 |
❓ 问题:你知道 array[0, 1] 是 85,但你知道这代表什么含义吗?
- 是"年龄"?还是"成绩"?还是"体重"?
- NumPy 只认识数字位置,不认识"姓名"、"年龄"这些含义
Pandas DataFrame - 数据 + 标签(列名 + 行索引):
| 姓名 | 年龄 | 语文成绩 | 数学成绩 |
|---|---|---|---|
| 小明 | 18 | 85 | 92 |
| 小红 | 19 | 90 | 88 |
| 小刚 | 18 | 78 | 95 |
✅ 优势:
df["年龄"]→ 直接获取年龄列 [18, 19, 18]df["成绩"]→ 直接获取成绩列 [85, 90, 78]df.loc[0]或df[df["姓名"]=="小明"]→ 直接获取小明的数据- Pandas 认识标签,知道每一列代表什么含义
一句话总结:
- NumPy = 纯数字的矩阵(只知道第几行第几列)
- Pandas = 带表头的 Excel 表格(知道"姓名"列、"年龄"列,可以按名字查找)
📝 安装与导入
pip install pandas
import pandas as pd
import numpy as np
一、Series:一维带标签数组
1.1 核心知识点
Series 是带标签的一维数组,类似于一列数据(像 Excel 的一列)。
import pandas as pd
# 创建 Series(只有值,索引自动为 0,1,2,3)
s1 = pd.Series([85, 90, 78, 92])
print(s1)
# 输出:
# 0 85
# 1 90
# 2 78
# 3 92
# dtype: int64
# 创建 Series(自定义索引)
s2 = pd.Series([85, 90, 78, 92], index=["小明", "小红", "小刚", "小丽"])
print(s2)
# 输出:
# 小明 85
# 小红 90
# 小刚 78
# 小丽 92
# dtype: int64
# 通过标签访问
print(s2["小红"]) # 90
print(s2[1]) # 90(也可以用数字索引)
print(s2[s2 > 85])
🔍 可视化理解:Series 是什么?
Series = 一列带标签的数据
Series 就像 Excel 中的一列:
Excel 表格的一列: Pandas Series:
┌──────┬──────┐ ┌────────┬──────┐
│ 姓名 │ 成绩 │ │ 索引 │ 值 │
├──────┼──────┤ ├────────┼──────┤
│ 小明 │ 85 │ → │ 小明 │ 85 │
│ 小红 │ 90 │ → │ 小红 │ 90 │
│ 小刚 │ 78 │ → │ 小刚 │ 78 │
│ 小丽 │ 92 │ → │ 小丽 │ 92 │
└──────┴──────┘ └────────┴──────┘
代码:
s = pd.Series([85, 90, 78, 92], index=["小明", "小红", "小刚", "小丽"])
访问方式:
s["小红"] → 90 (通过标签访问)
s[1] → 90 (通过位置访问)
s.values → [85, 90, 78, 92] (获取所有值,NumPy数组)
s.index → ["小明", "小红", "小刚", "小丽"] (获取索引)
🔍 可视化理解:无索引 vs 自定义索引
对比:不设置索引 vs 设置自定义索引
【不设置索引 - 默认数字索引】
代码: s1 = pd.Series([85, 90, 78, 92])
内存结构:
┌─────────┬─────────┐
│ 索引 │ 值 │
├─────────┼─────────┤
│ 0 │ 85 │
│ 1 │ 90 │
│ 2 │ 78 │
│ 3 │ 92 │
└─────────┴─────────┘
↑ 默认从0开始的整数索引
访问方式: s1[0] → 85, s1[1] → 90
【设置自定义索引 - 标签索引】
代码: s2 = pd.Series([85, 90, 78, 92], index=["小明", "小红", "小刚", "小丽"])
内存结构:
┌─────────┬─────────┐
│ 索引 │ 值 │
├─────────┼─────────┤
│ 小明 │ 85 │
│ 小红 │ 90 │
│ 小刚 │ 78 │
│ 小丽 │ 92 │
└─────────┴─────────┘
↑ 自定义有意义的标签
访问方式: s2["小明"] → 85, s2["小红"] → 90
s2[0] → 85(数字索引仍然可用!)
关键区别总结:
| 特性 | 不设置索引 | 设置自定义索引 |
|---|---|---|
| 索引类型 | 默认整数 0,1,2,3... | 自定义标签(字符串、数字等) |
| 访问方式 | 只能用 s[0] |
可以用 s["标签"] 或 s[0] |
| 可读性 | 一般 | 更好(有意义的标签) |
| 适用场景 | 简单序列 | 需要按名称查找的数据 |
对比:NumPy 数组 vs Pandas Series
NumPy 数组: Pandas Series:
┌────┬────┬────┬────┐ ┌──────┬────┬────┬────┬────┐
│ 85 │ 90 │ 78 │ 92 │ │ 索引 │小明│小红│小刚│小丽│
└────┴────┴────┴────┘ ├──────┼────┼────┼────┼────┤
0 1 2 3 │ 值 │ 85 │ 90 │ 78 │ 92 │
↑只能用数字索引 └──────┴────┴────┴────┴────┘
↑可以用标签索引!
常用 Series 函数:
| 函数/属性 | 示例 | 说明 |
|---|---|---|
pd.Series() |
pd.Series([1, 2, 3], index=["a", "b", "c"]) |
创建 Series |
s.mean() |
s.mean() |
计算平均值 |
s.sum() |
s.sum() |
计算总和 |
s.max() |
s.max() |
获取最大值 |
s.min() |
s.min() |
获取最小值 |
s.values |
s.values |
获取值数组(NumPy 数组) |
s.index |
s.index |
获取索引 |
s.dtype |
s.dtype |
获取数据类型 |
| 布尔筛选 | s[s > 85] |
筛选大于85的元素 |
🔍 深入理解:Series 的布尔筛选
第一步:s > 85 返回什么?
import pandas as pd
s = pd.Series([85, 90, 78, 92], index=["小明", "小红", "小刚", "小丽"])
# 看看 s > 85 返回什么
condition = s > 85
print(condition)
print(type(condition))
输出:
小明 False
小红 True
小刚 False
小丽 True
dtype: bool
<class 'pandas.core.series.Series'>
解释:
s > 85返回一个 布尔 Series(Boolean Series)- 索引与原 Series 完全相同
True表示该位置的元素满足条件(值 > 85),False表示不满足- 数据类型是
bool(布尔型)
第二步:s[condition] 如何筛选数据?
# condition 是一个布尔 Series
condition = s > 85
print(condition)
# 小明 False
# 小红 True
# 小刚 False
# 小丽 True
# 用布尔 Series 筛选
result = s[condition]
print(result)
输出:
小红 90
小丽 92
dtype: int64
可视化理解:
原始 Series: 布尔 Series: 筛选结果:
┌────┬────┐ ┌────┬───────┐ ┌────┬────┐
│ 索引 │ 值 │ │ 索引 │ 条件 │ │ 索引 │ 值│
├────┼────┤ ├────┼───────┤ ├────┼────┤
│ 小明 │ 85 │ │ 小明 │ False │ │ 小红 │ 90│
│ 小红 │ 90 │ × │ 小红 │ True │ → │ 小丽 │ 92│
│ 小刚 │ 78 │ │ 小刚 │ False │ └────┴────┘
│ 小丽 │ 92 │ × │ 小丽 │ True │
└────┴────┘ └────┴───────┘
↑ ↑
索引对齐 ───────────────────────┘
筛选原理:
| 索引 | 值 | condition (s > 85) | 是否保留 |
|---|---|---|---|
| 小明 | 85 | False | ❌ 过滤掉 |
| 小红 | 90 | True | ✅ 保留 |
| 小刚 | 78 | False | ❌ 过滤掉 |
| 小丽 | 92 | True | ✅ 保留 |
核心机制:
- Pandas 会将布尔 Series 与原 Series 的索引对齐
- 只保留对应位置为
True的元素 - 返回一个新的 Series,保留原索引
布尔筛选常用场景:
# 筛选大于平均值的元素
result = s[s > s.mean()]
# 筛选在指定范围内的元素
result = s[(s >= 80) & (s <= 90)]
# 筛选等于特定值的元素
result = s[s == 90]
可视化理解:多条件筛选 (80 ≤ 值 ≤ 90):
原始 Series: 条件1: >=80 条件2: <=90 组合条件 (&): 筛选结果:
┌────┬────┐ ┌────┬───────┐ ┌────┬───────┐ ┌────┬───────┐ ┌────┬────┐
│ 索引 │ 值 │ │ 索引 │ 条件1 │ │ 索引│ 条件2 │ │ 索引 │ 结果 │ │ 索引 │ 值 │
├────┼────┤ ├────┼───────┤ ├────┼───────┤ ├────┼───────┤ ├────┼────┤
│ 小明 │ 85 │ │ 小明 │ True │ │ 小明 │ True│ & │ 小明 │ True │ → │ 小明 │ 85 │
│ 小红 │ 90 │ │ 小红 │ True │ & │ 小红 │ True │ = │ 小红 │ True │ → │ 小红 │ 90 │
│ 小刚 │ 78 │ │ 小刚 │ False│ │ 小刚 │ True │ │ 小刚 │ False │ └────┴────┘
│ 小丽 │ 92 │ │ 小丽 │ True │ & │ 小丽 │ False │ = │ 小丽 │ False │
└────┴────┘ └────┴───────┘ └────┴───────┘ └────┴───────┘
解释:&(与)要求两个条件同时满足,只有都为 True 时结果才为 True。
注意:多条件筛选时,每个条件需要用括号 () 包裹,不能使用 Python 的 and/or。
1.2 练习题
练习1-1:补全代码,创建和操作 Series
import pandas as pd
# TODO: 创建一个 Series,值为 [70, 85, 90, 65],索引为 ["语文", "数学", "英语", "科学"]
scores = __________
print(scores)
# 预期输出:
# 语文 70
# 数学 85
# 英语 90
# 科学 65
# dtype: int64
# TODO: 获取数学成绩,赋值给 math_score
math_score = __________
print(f"数学成绩: {math_score}")
# 预期输出: 数学成绩: 85
# TODO: 计算平均分,赋值给 average
average = __________
print(f"平均分: {average}")
# 预期输出: 平均分: 77.5
# TODO: 获取所有大于80分的科目(布尔索引),赋值给 high_scores
high_scores = __________
print(f"高分科目:\n{high_scores}")
# 预期输出:
# 高分科目:
# 数学 85
# 英语 90
# dtype: int64
二、DataFrame:二维带标签表格
2.1 核心知识点
🔍 可视化理解:DataFrame 是什么?
DataFrame = 带标签(默认有列标签)的二维表格(像 Excel 表格)
Excel 表格: Pandas DataFrame:
列名↓ 列名↓
┌────┬────┬────┬────┐ ┌────┬────┬────┬────┐
│ │ 姓名│ 年龄│ 成绩│ │ │ 姓名 │ 年龄 │ 成绩 │
├────┼────┼────┼────┤ ├────┼────┼────┼────┤
行索引 → │ 0 │ 小明 │ 18 │ 85 │ 行索引 → │ 0 │ 小明 │ 18 │ 85 │
│ 1 │ 小红 │ 19 │ 90 │ │ 1 │ 小红 │ 19 │ 90 │
│ 2 │ 小刚 │ 18 │ 78 │ │ 2 │ 小刚 │ 18 │ 78 │
│ 3 │ 小丽 │ 20 │ 92 │ │ 3 │ 小丽 │ 20 │ 92 │
└────┴────┴────┴────┘ └────┴────┴────┴────┘
代码:
data = {
"姓名": ["小明", "小红", "小刚", "小丽"],
"年龄": [18, 19, 18, 20],
"成绩": [85, 90, 78, 92]
}
df = pd.DataFrame(data)
属性说明:
df.shape → (4, 3) 4行3列
df.columns → ["姓名", "年龄", "成绩"] 列名
df.index → [0, 1, 2, 3] 行索引
df.dtypes → 每列的数据类型
DataFrame 是带标签(默认带列标签)的二维表格,类似于 Excel 表格或数据库表。
import pandas as pd
# 从字典创建 DataFrame
data = {
"姓名": ["小明", "小红", "小刚", "小丽"],
"年龄": [18, 19, 18, 20],
"成绩": [85, 90, 78, 92]
}
df = pd.DataFrame(data)
print(df)
# 输出:
# 姓名 年龄 成绩
# 0 小明 18 85
# 1 小红 19 90
# 2 小刚 18 78
# 3 小丽 20 92
# 查看基本信息
print(df.shape) # (4, 3) - 4行3列
print(df.columns) # Index(['姓名', '年龄', '成绩'], dtype='object')
print(df.index) # RangeIndex(start=0, stop=4, step=1)
# 设置自定义行索引(行标签)
df_custom = pd.DataFrame(data, index=["学生A", "学生B", "学生C", "学生D"])
print(df_custom)
# 输出:
# 姓名 年龄 成绩
# 学生A 小明 18 85
# 学生B 小红 19 90
# 学生C 小刚 18 78
# 学生D 小丽 20 92
🔍 可视化理解:默认行索引 vs 自定义行标签
对比:不设置行索引 vs 设置自定义行标签
【默认行索引 - 自动数字索引】
代码: df = pd.DataFrame(data)
列名: 姓名 年龄 成绩
↓ ↓ ↓
┌────┬────┬────┬────┐
行索引 → │ 0 │ 小明 │ 18 │ 85 │
│ 1 │ 小红 │ 19 │ 90 │
│ 2 │ 小刚 │ 18 │ 78 │
│ 3 │ 小丽 │ 20 │ 92 │
└────┴────┴────┴────┘
↑
默认从0开始的整数索引
访问方式: df.loc[0] → 第一行数据
【自定义行标签 - 有意义的索引】
代码: df = pd.DataFrame(data, index=["学生A", "学生B", "学生C", "学生D"])
列名: 姓名 年龄 成绩
↓ ↓ ↓
┌──────┬────┬────┬────┐
行标签 → │ 学生A │ 小明 │ 18 │ 85 │
│ 学生B │ 小红 │ 19 │ 90 │
│ 学生C │ 小刚 │ 18 │ 78 │
│ 学生D │ 小丽 │ 20 │ 92 │
└──────┴────┴────┴────┘
↑
自定义有意义的行标签
访问方式: df.loc["学生A"] → 学生A的数据
df.loc[0] → 仍然可用(数字位置索引)
关键区别总结:
| 特性 | 默认行索引 | 自定义行标签 |
|---|---|---|
| 索引类型 | 整数 0,1,2,3... | 自定义标签(字符串、数字等) |
| 访问方式 | df.loc[0] |
df.loc["标签"] 或 df.loc[0] |
| 可读性 | 一般 | 更好(有意义的标签) |
| 适用场景 | 简单表格 | 需要按名称查找行的数据 |
对比:Series vs DataFrame
Series(一列): DataFrame(多列):
┌────────┬──────┐ ┌────┬────┬────┬────┐
│ 索引 │ 值 │ │ │姓名│年龄│成绩│
├────────┼──────┤ ├────┼────┼────┼────┤
│ 小明 │ 85 │ │ 0 │小明│ 18 │ 85 │
│ 小红 │ 90 │ → │ 1 │小红│ 19 │ 90 │
│ 小刚 │ 78 │ │ 2 │小刚│ 18 │ 78 │
│ 小丽 │ 92 │ │ 3 │小丽│ 20 │ 92 │
└────────┴──────┘ └────┴────┴────┴────┘
只有一列数据 有多列数据
类似 Excel 的一列 类似 Excel 的整个表格
常用 DataFrame 函数:
| 函数/属性 | 示例 | 说明 |
|---|---|---|
pd.DataFrame() |
pd.DataFrame({"col": [1, 2, 3]}) |
从字典创建 DataFrame |
df.shape |
df.shape |
返回 (行数, 列数) |
df.columns |
df.columns |
获取列名 |
df.index |
df.index |
获取行索引 |
df.dtypes |
df.dtypes |
获取各列数据类型 |
df.head() |
df.head(5) |
查看前5行 |
df.tail() |
df.tail(3) |
查看后3行 |
df.info() |
df.info() |
查看数据信息 |
df.describe() |
df.describe() |
描述性统计 |
2.2 练习题
练习2-1:补全代码,创建 DataFrame
import pandas as pd
# TODO: 从以下数据创建 DataFrame
# 姓名: ["张三", "李四", "王五"]
# 语文: [85, 90, 78]
# 数学: [92, 88, 95]
# 英语: [88, 92, 85]
data = __________
df = pd.DataFrame(data)
print(df)
# 预期输出:
# 姓名 语文 数学 英语
# 0 张三 85 92 88
# 1 李四 90 88 92
# 2 王五 78 95 85
# TODO: 获取 DataFrame 的形状,赋值给 shape
shape = __________
print(f"形状: {shape}")
# 预期输出: 形状: (3, 4)
# TODO: 获取所有列名,赋值给 cols
cols = __________
print(f"列名: {list(cols)}")
# 预期输出: 列名: ['姓名', '语文', '数学', '英语']
三、数据读取与保存
3.1 核心知识点
Pandas 可以方便地读写各种数据格式:
| 格式 | 读取 | 保存 |
|---|---|---|
| CSV | pd.read_csv("file.csv") |
df.to_csv("file.csv", index=False) |
| Excel | pd.read_excel("file.xlsx") |
df.to_excel("file.xlsx", index=False) |
| JSON | pd.read_json("file.json") |
df.to_json("file.json") |
import pandas as pd
# 读取 CSV 文件
df = pd.read_csv("students.csv")
# 保存为 CSV 文件(index=False 表示不保存行索引)
df.to_csv("output.csv", index=False)
常用读写函数:
| 函数 | 示例 | 说明 |
|---|---|---|
pd.read_csv() |
pd.read_csv("data.csv", encoding="utf-8") |
读取 CSV 文件 |
pd.read_excel() |
pd.read_excel("data.xlsx", sheet_name="Sheet1") |
读取 Excel 文件 |
pd.read_json() |
pd.read_json("data.json") |
读取 JSON 文件 |
df.to_csv() |
df.to_csv("out.csv", index=False) |
保存为 CSV |
df.to_excel() |
df.to_excel("out.xlsx", index=False) |
保存为 Excel |
df.to_json() |
df.to_json("out.json") |
保存为 JSON |
3.2 练习题
练习3-1:补全代码,读取和保存数据
import pandas as pd
# TODO: 假设有一个 data.csv 文件,读取它并赋值给 df
# (这里用创建 DataFrame 模拟读取)
df = pd.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"score": [85, 90, 88]
})
print("原始数据:")
print(df)
# TODO: 只保留 age 大于 25 的行,赋值给 filtered_df
filtered_df = __________
print("\n年龄大于25的数据:")
print(filtered_df)
# 预期输出:
# 年龄大于25的数据:
# name age score
# 1 Bob 30 90
# 2 Charlie 35 88
# TODO: 将 filtered_df 保存为 output.csv(不包含索引)
__________
print("\n已保存到 output.csv")
四、数据选择与过滤
4.1 核心知识点
import pandas as pd
df = pd.DataFrame({
"姓名": ["小明", "小红", "小刚", "小丽"],
"年龄": [18, 19, 18, 20],
"成绩": [85, 90, 78, 92]
})
# 选择列
print(df["姓名"]) # 选择一列(返回 Series)
print(df[["姓名", "成绩"]]) # 选择多列(返回 DataFrame)
# 选择行
print(df.loc[0]) # 按标签选择第0行(仅当行索引包含0时有效)
print(df.iloc[0]) # 按位置选择第0行(总是有效,选第一行)
print(df[0:2]) # 切片选择前2行
# ⚠️ 重要区别:loc vs iloc
# df.loc[] - 按【标签】选择(取决于 index 的值)
# df.iloc[] - 按【位置】选择(总是 0,1,2,3...)
# 示例:自定义行索引时
# df_custom = pd.DataFrame(data, index=["学生A", "学生B", "学生C", "学生D"])
# df_custom.loc["学生A"] #✅ 合法 - 标签存在
# df_custom.iloc[0] #✅ 合法 - 选第一行(学生A)
# df_custom.loc[0] #❌ 报错 - 标签0不存在,因为已经设置了行标签,,所以该函数不能用行索引取值,但是df_custom.iloc[0]就可以取值
# 条件过滤
print(df[df["成绩"] > 85]) # 成绩大于85的行
print(df[df["年龄"] == 18]) # 年龄等于18的行
🔍 深入理解:loc vs iloc 的区别
df.loc[] vs df.iloc[] 可视化对比
【情况1:默认整数索引】
DataFrame:
┌────┬────┬────┬────┐
│ │ 姓名 │ 年龄 │ 成绩 │
├────┼────┼────┼────┤
标签 0 → │ 0 │ 小明 │ 18 │ 85 │ ← 位置 0
标签 1 → │ 1 │ 小红 │ 19 │ 90 │ ← 位置 1
标签 2 → │ 2 │ 小刚 │ 18 │ 78 │ ← 位置 2
标签 3 → │ 3 │ 小丽 │ 20 │ 92 │ ← 位置 3
└────┴────┴────┴────┘
此时: df.loc[0] ✅ 合法(标签0存在)
df.iloc[0] ✅ 合法(位置0,即第一行)
两者结果相同!
【情况2:自定义字符串索引】
DataFrame:
┌──────┬────┬────┬────┐
│ │ 姓名 │ 年龄 │ 成绩 │
├──────┼────┼────┼────┤
标签 "学生A" → │ 学生A │ 小明 │ 18 │ 85 │ ← 位置 0
标签 "学生B" → │ 学生B │ 小红 │ 19 │ 90 │ ← 位置 1
标签 "学生C" → │ 学生C │ 小刚 │ 18 │ 78 │ ← 位置 2
标签 "学生D" → │ 学生D │ 小丽 │ 20 │ 92 │ ← 位置 3
└──────┴────┴────┴────┘
此时: df.loc["学生A"] ✅ 合法(标签存在)
df.loc[0] ❌ 报错!标签0不存在
df.iloc[0] ✅ 合法(位置0,即学生A那行)
【关键区别总结】
┌──────────┬─────────────────────┬─────────────────────┐
│ 特性 │ df.loc[] │ df.iloc[] │
├──────────┼─────────────────────┼─────────────────────┤
│ 选择依据 │ 按【标签】 │ 按【位置】 │
│ 索引类型 │ 取决于 df.index │ 总是整数 0,1,2... │
│ 切片规则 │ 包含结束标签 │ 不包含结束位置 │
│ 使用建议 │ 知道行名称时用 │ 知道行位置时用 │
└──────────┴─────────────────────┴─────────────────────┘
🔍 深入理解:布尔索引的工作原理
第一步:df["成绩"] > 85 返回什么?
import pandas as pd
df = pd.DataFrame({
"姓名": ["小明", "小红", "小刚", "小丽"],
"年龄": [18, 19, 18, 20],
"成绩": [85, 90, 78, 92]
})
# 看看 df["成绩"] > 85 返回什么
condition = df["成绩"] > 85
print(condition)
print(type(condition))
输出:
0 False
1 True
2 False
3 True
Name: 成绩, dtype: bool
<class 'pandas.core.series.Series'>
解释:
df["成绩"] > 85返回一个 布尔 Series(Boolean Series)- 它是一个一维数组,每个元素对应 DataFrame 的一行
True表示该行满足条件(成绩 > 85),False表示不满足- 数据类型是
bool(布尔型)
第二步:df[condition] 如何筛选数据?
# condition 是一个布尔 Series
condition = df["成绩"] > 85
print(condition)
# 0 False
# 1 True
# 2 False
# 3 True
# 用布尔 Series 筛选 DataFrame
result = df[condition]
print(result)
输出:
姓名 年龄 成绩
1 小红 19 90
3 小丽 20 92
可视化理解:
原始 DataFrame: 布尔 Series: 筛选结果:
┌────┬────┬────┐ ┌────┬───────┐ ┌────┬────┬────┐
│ 姓名 │ 年龄 │ 成绩 │ │ 索引 │ 条件 │ │ 姓名 │ 年龄 │ 成绩 │
├────┼────┼────┤ ├────┼───────┤ ├────┼────┼────┤
│ 小明 │ 18 │ 85 │ │ 0 │ False │ → │ 小红 │ 19 │ 90 │
│ 小红 │ 19 │ 90 │ × │ 1 │ True │ → │ 小丽 │ 20 │ 92 │
│ 小刚 │ 18 │ 78 │ │ 2 │ False │ └────┴────┴────┘
│ 小丽 │ 20 │ 92 │ × │ 3 │ True │
└────┴────┴────┘ └────┴───────┘
↑ ↑
行索引对齐 ─────────────────────┘
筛选原理:
| 行索引 | 姓名 | 成绩 | condition (成绩>85) | 是否保留 |
|---|---|---|---|---|
| 0 | 小明 | 85 | False | ❌ 过滤掉 |
| 1 | 小红 | 90 | True | ✅ 保留 |
| 2 | 小刚 | 78 | False | ❌ 过滤掉 |
| 3 | 小丽 | 92 | True | ✅ 保留 |
核心机制:
- Pandas 会将布尔 Series 与 DataFrame 的行索引对齐
- 只保留对应位置为
True的行 - 返回一个新的 DataFrame,包含所有列
第三步:多条件组合
1. 与条件(&):成绩>85 且 年龄<20
result = df[(df["成绩"] > 85) & (df["年龄"] < 20)]
可视化理解:
DataFrame: 条件1: 成绩>85 条件2: 年龄<20 组合条件 (&):
┌────┬────┬────┐ ┌────┬───────┐ ┌────┬───────┐ ┌────┬───────┐
│ 姓名 │ 年龄 │ 成绩 │ │ 索引 │ 条件1 │ │ 索引 │ 条件2 │ │ 索引 │ 结果 │
├────┼────┼────┤ ├────┼───────┤ ├────┼───────┤ ├────┼───────┤
│ 小明 │ 18 │ 85 │ │ 0 │ False │ │ 0 │ True │ & │ 0 │ False │
│ 小红 │ 19 │ 90 │ │ 1 │ True │ & │ 1 │ True │ = │ 1 │ True │
│ 小刚 │ 18 │ 78 │ │ 2 │ False │ │ 2 │ True │ │ 2 │ False │
│ 小丽 │ 20 │ 92 │ │ 3 │ True │ & │ 3 │ False │ = │ 3 │ False │
└────┴────┴────┘ └────┴───────┘ └────┴───────┘ └────┴───────┘
↓
筛选结果: 小红
解释:&(与)要求两个条件同时满足,只有都为 True 时结果才为 True。
2. 或条件(|):成绩>90 或 成绩<80
result = df[(df["成绩"] > 90) | (df["成绩"] < 80)]
可视化理解:
DataFrame: 条件1: 成绩>90 条件2: 成绩<80 组合条件 (|): 筛选结果:
┌────┬────┬────┐ ┌────┬───────┐ ┌────┬───────┐ ┌────┬───────┐ ┌────┬────┬────┐
│ 姓名 │ 年龄 │ 成绩 │ │ 索引 │ 条件1 │ │ 索引 │ 条件2 │ │ 索引 │ 结果 │ │ 姓名 │ 年龄 │ 成绩 │
├────┼────┼────┤ ├────┼───────┤ ├────┼───────┤ ├────┼───────┤ ├────┼────┼────┤
│ 小明 │ 18 │ 85 │ │ 0 │ False │ │ 0 │ False │ | │ 0 │ False │ │ 小刚 │ 18 │ 78 │
│ 小红 │ 19 │ 90 │ │ 1 │ False │ | │ 1 │ False │ = │ 1 │ False │ → │ 小丽 │ 20 │ 92 │
│ 小刚 │ 18 │ 78 │ │ 2 │ False │ │ 2 │ True │ │ 2 │ True │ → └────┴────┴────┘
│ 小丽 │ 20 │ 92 │ │ 3 │ True │ | │ 3 │ False │ = │ 3 │ True │ →
└────┴────┴────┘ └────┴───────┘ └────┴───────┘ └────┴───────┘
解释:|(或)要求两个条件满足其一即可,只要有一个为 True 结果就为 `True**。
3. 非条件(~):成绩不大于85(即成绩<=85)
result = df[~(df["成绩"] > 85)]
可视化理解:
DataFrame: 条件: 成绩>85 取反 (~): 筛选结果:
┌────┬────┬────┐ ┌────┬───────┐ ┌────┬───────┐ ┌────┬────┬────┐
│ 姓名 │ 年龄 │ 成绩 │ │ 索引 │ 原条件 │ │ 索引 │ 结果 │ │ 姓名 │ 年龄 │ 成绩 │
├────┼────┼────┤ ├────┼───────┤ ├────┼───────┤ ├────┼────┼────┤
│ 小明 │ 18 │ 85 │ │ 0 │ False │ ~ │ 0 │ True │ → │ 小明 │ 18 │ 85 │
│ 小红 │ 19 │ 90 │ │ 1 │ True │ = │ 1 │ False │ → │ 小刚 │ 18 │ 78 │
│ 小刚 │ 18 │ 78 │ │ 2 │ False │ ~ │ 2 │ True │ → └────┴────┴────┘
│ 小丽 │ 20 │ 92 │ │ 3 │ True │ = │ 3 │ False │
└────┴────┴────┘ └────┴───────┘ └────┴───────┘
解释:~(非)将条件取反,True 变 False,False 变 True。
注意:多条件时必须用 &(与)、|(或)、~(非),而不是 and、or、not,并且每个条件要用括号括起来。
数据选择函数:
| 函数/操作 | 示例 | 说明 |
|---|---|---|
df["col"] |
df["姓名"] |
选择单列(返回 Series) |
df[["col1", "col2"]] |
df[["姓名", "成绩"]] |
选择多列(返回 DataFrame) |
df.loc[] |
df.loc[0] |
按标签选择行 |
df.iloc[] |
df.iloc[0] |
按位置选择行 |
df.loc[row, col] |
df.loc[0, "姓名"] |
选择指定行列 |
df.iloc[row, col] |
df.iloc[0, 1] |
按位置选择指定行列 |
df[start:end] |
df[0:2] |
切片选择行 |
| 条件过滤 | df[df["成绩"] > 85] |
筛选满足条件的行 |
| 多条件 | df[(df["成绩"] > 85) & (df["年龄"] < 20)] |
多条件筛选(与) |
| 多条件 | df[(df["成绩"] > 85) | (df["成绩"] < 80)] |
多条件筛选(或) |
4.2 练习题
练习4-1:补全代码,选择和过滤数据
import pandas as pd
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"语文": [85, 90, 78, 92],
"数学": [92, 88, 95, 80],
"英语": [88, 92, 85, 90]
})
# TODO: 只选择"姓名"和"数学"两列,赋值给 math_df
math_df = __________
print("数学成绩表:")
print(math_df)
# 预期输出:
# 数学成绩表:
# 姓名 数学
# 0 张三 92
# 1 李四 88
# 2 王五 95
# 3 赵六 80
# TODO: 选择第1行到第2行(切片),赋值给 rows_1_2
rows_1_2 = __________
print("\n第1-2行:")
print(rows_1_2)
# 预期输出:
# 第1-2行:
# 姓名 语文 数学 英语
# 1 李四 90 88 92
# 2 王五 78 95 85
# TODO: 筛选出语文成绩大于85的学生,赋值给 high_chinese
high_chinese = __________
print("\n语文大于85分的学生:")
print(high_chinese)
# 预期输出:
# 语文大于85分的学生:
# 姓名 语文 数学 英语
# 1 李四 90 88 92
# 3 赵六 92 80 90
# TODO: 筛选出数学和英语都大于85分的学生,赋值给 excellent
excellent = __________
print("\n数学和英语都大于85分的学生:")
print(excellent)
# 预期输出:
# 数学和英语都大于85分的学生:
# 姓名 语文 数学 英语
# 0 张三 85 92 88
# 1 李四 90 88 92
五、数据统计与运算
5.1 核心知识点
import pandas as pd
df = pd.DataFrame({
"语文": [85, 90, 78, 92],
"数学": [92, 88, 95, 80],
"英语": [88, 92, 85, 90]
})
# 基本统计
print(df.describe()) # 描述性统计
print(df.mean()) # 每列平均值
print(df.sum()) # 每列总和
print(df.max()) # 每列最大值
print(df.min()) # 每列最小值
# 按行/列计算
print(df.mean(axis=0)) # 按列计算(默认)
print(df.mean(axis=1)) # 按行计算
# 新增列
df["总分"] = df["语文"] + df["数学"] + df["英语"]
df["平均分"] = df[["语文", "数学", "英语"]].mean(axis=1)
统计运算函数:
| 函数 | 示例 | 说明 |
|---|---|---|
df.describe() |
df.describe() |
描述性统计(计数、均值、标准差等) |
df.mean() |
df.mean() |
每列平均值 |
df.mean(axis=1) |
df.mean(axis=1) |
每行平均值 |
df.sum() |
df.sum() |
每列总和 |
df.max() |
df.max() |
每列最大值 |
df.min() |
df.min() |
每列最小值 |
df.std() |
df.std() |
每列标准差 |
df.count() |
df.count() |
每列非空值计数 |
s.idxmax() |
df["成绩"].idxmax() |
最大值所在的索引 |
s.idxmin() |
df["成绩"].idxmin() |
最小值所在的索引 |
5.2 练习题
练习5-1:补全代码,数据统计与运算
import pandas as pd
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"语文": [85, 90, 78, 92],
"数学": [92, 88, 95, 80],
"英语": [88, 92, 85, 90]
})
# TODO: 计算每门课程的平均分,赋值给 course_means
course_means = __________
print("每门课程平均分:")
print(course_means)
# 预期输出:
# 每门课程平均分:
# 语文 86.25
# 数学 88.75
# 英语 88.75
# dtype: float64
# TODO: 计算每个学生的总分,添加为新列"总分"
__________
print("\n添加总分后:")
print(df)
# 预期输出:
# 添加总分后:
# 姓名 语文 数学 英语 总分
# 0 张三 85 92 88 265
# 1 李四 90 88 92 270
# 2 王五 78 95 85 258
# 3 赵六 92 80 90 262
# TODO: 计算每个学生的平均分,添加为新列"平均分"
__________
print("\n添加平均分后:")
print(df)
# 预期输出:
# 添加平均分后:
# 姓名 语文 数学 英语 总分 平均分
# 0 张三 85 92 88 265 88.333333
# 1 李四 90 88 92 270 90.000000
# 2 王五 78 95 85 258 86.000000
# 3 赵六 92 80 90 262 87.333333
# TODO: 找出总分最高的学生姓名
# 提示: 先找到最大值的索引,再用索引获取姓名
max_score_idx = df["总分"].idxmax()
top_student = __________
print(f"\n总分最高的学生: {top_student}")
# 预期输出: 总分最高的学生: 李四
六、数据清洗
6.1 核心知识点
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["小明", "小红", "小刚", "小丽"],
"年龄": [18, None, 18, 20], # 有缺失值
"成绩": [85, 90, None, 92] # 有缺失值
})
# 处理缺失值
print(df.isnull()) # 检查哪些是缺失值
print(df.dropna()) # 删除包含缺失值的行
print(df.fillna(0)) # 用0填充缺失值
print(df["年龄"].fillna(df["年龄"].mean())) # 用平均值填充
# 删除重复值
df.drop_duplicates()
# 修改列名
df.rename(columns={"姓名": "name", "年龄": "age"})
数据清洗函数:
| 函数 | 示例 | 说明 |
|---|---|---|
df.isnull() |
df.isnull() |
检查缺失值(返回布尔数组) |
df.notnull() |
df.notnull() |
检查非缺失值 |
df.dropna() |
df.dropna() |
删除包含缺失值的行 |
df.dropna(subset=["col"]) |
df.dropna(subset=["年龄"]) |
只删除指定列有缺失值的行 |
df.fillna(value) |
df.fillna(0) |
用指定值填充缺失值 |
df.fillna(method="ffill") |
df.fillna(method="ffill") |
用前一个值填充 |
s.fillna(s.mean()) |
df["年龄"].fillna(df["年龄"].mean()) |
用平均值填充 |
df.drop_duplicates() |
df.drop_duplicates() |
删除重复行 |
df.drop_duplicates(subset=["col"]) |
df.drop_duplicates(subset=["姓名"]) |
按指定列去重 |
df.rename() |
df.rename(columns={"旧名": "新名"}) |
重命名列 |
df.astype() |
df["年龄"].astype(int) |
转换数据类型 |
6.2 练习题
练习6-1:补全代码,数据清洗
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六", "张三"],
"语文": [85, 90, np.nan, 92, 85],
"数学": [92, np.nan, 95, 80, 92]
})
print("原始数据:")
print(df)
# TODO: 删除包含缺失值的行,赋值给 df_clean
df_clean = __________
print("\n删除缺失值后:")
print(df_clean)
# 预期输出:
# 删除缺失值后:
# 姓名 语文 数学
# 0 张三 85.0 92.0
# 3 赵六 92.0 80.0
# 4 张三 85.0 92.0
# TODO: 删除重复行(保留第一个),赋值给 df_unique
df_unique = __________
print("\n删除重复行后:")
print(df_unique)
# 预期输出:
# 删除重复行后:
# 姓名 语文 数学
# 0 张三 85.0 92.0
# 3 赵六 92.0 80.0
# TODO: 将"语文"列的缺失值用语文平均分填充,赋值给 df_filled
chinese_mean = df["语文"].mean()
df_filled = df.copy()
__________
print("\n填充缺失值后:")
print(df_filled)
# 预期输出:
# 填充缺失值后:
# 姓名 语文 数学
# 0 张三 85.0 92.0
# 1 李四 90.0 NaN
# 2 王五 89.0 95.0
# 3 赵六 92.0 80.0
# 4 张三 85.0 92.0
七、分组与排序
7.1 核心知识点
import pandas as pd
df = pd.DataFrame({
"姓名": ["小明", "小红", "小刚", "小丽"],
"班级": ["A班", "B班", "A班", "B班"],
"成绩": [85, 90, 78, 92]
})
# 分组统计
print(df.groupby("班级")["成绩"].mean()) # 每班平均成绩
print(df.groupby("班级").agg({"成绩": ["mean", "max", "min"]})) # 多统计量
# 排序
print(df.sort_values("成绩")) # 按成绩升序
print(df.sort_values("成绩", ascending=False)) # 按成绩降序
分组与排序函数:
| 函数 | 示例 | 说明 |
|---|---|---|
df.groupby() |
df.groupby("班级") |
按指定列分组 |
group.mean() |
df.groupby("班级")["成绩"].mean() |
分组求平均 |
group.sum() |
df.groupby("班级")["成绩"].sum() |
分组求和 |
group.agg() |
df.groupby("班级").agg({"成绩": "mean"}) |
分组聚合(可多统计量) |
df.sort_values() |
df.sort_values("成绩") |
按列排序(升序) |
df.sort_values() |
df.sort_values("成绩", ascending=False) |
按列排序(降序) |
df.sort_index() |
df.sort_index() |
按索引排序 |
八、综合练习
练习8-1:完整的数据分析流程
import pandas as pd
import numpy as np
# 创建学生成绩数据
df = pd.DataFrame({
"姓名": ["小明", "小红", "小刚", "小丽", "小华", "小强"],
"班级": ["A班", "B班", "A班", "B班", "A班", "B班"],
"语文": [85, 90, 78, 92, 88, np.nan],
"数学": [92, 88, 95, 80, np.nan, 90],
"英语": [88, 92, 85, 90, 90, 88]
})
print("原始数据:")
print(df)
# TODO 1: 用每列的平均值填充缺失值
df["语文"] = __________
df["数学"] = __________
print("\n填充缺失值后:")
print(df)
# TODO 2: 计算每个学生的总分和平均分,添加为新列
df["总分"] = __________
df["平均分"] = __________
print("\n添加总分和平均分后:")
print(df)
# TODO 3: 按班级分组,计算每班的平均成绩
class_avg = __________
print("\n各班平均分:")
print(class_avg)
# 预期输出:
# 各班平均分:
# 语文 数学 英语
# 班级
# A班 83.666667 93.5 87.666667
# B班 91.0 86.0 90.0
# TODO 4: 找出平均分最高的学生
best_student = __________
print(f"\n平均分最高的学生: {best_student}")
# 预期输出: 平均分最高的学生: 小红
附录:答案
练习8-1 答案
scores = pd.Series([70, 85, 90, 65], index=["语文", "数学", "英语", "科学"])
math_score = scores["数学"]
average = scores.mean()
high_scores = scores[scores > 80]
练习2-1 答案
data = {
"姓名": ["张三", "李四", "王五"],
"语文": [85, 90, 78],
"数学": [92, 88, 95],
"英语": [88, 92, 85]
}
shape = df.shape
cols = df.columns
练习3-1 答案
filtered_df = df[df["age"] > 25]
filtered_df.to_csv("output.csv", index=False)
练习4-1 答案
math_df = df[["姓名", "数学"]]
rows_1_2 = df[1:3]
high_chinese = df[df["语文"] > 85]
excellent = df[(df["数学"] > 85) & (df["英语"] > 85)]
练习5-1 答案
course_means = df[["语文", "数学", "英语"]].mean()
df["总分"] = df["语文"] + df["数学"] + df["英语"]
df["平均分"] = df[["语文", "数学", "英语"]].mean(axis=1)
top_student = df.loc[max_score_idx, "姓名"]
练习6-1 答案
df_clean = df.dropna()
df_unique = df_clean.drop_duplicates()
df_filled["语文"] = df_filled["语文"].fillna(chinese_mean)
练习8-1 答案
df["语文"] = df["语文"].fillna(df["语文"].mean())
df["数学"] = df["数学"].fillna(df["数学"].mean())
df["总分"] = df["语文"] + df["数学"] + df["英语"]
df["平均分"] = df[["语文", "数学", "英语"]].mean(axis=1)
class_avg = df.groupby("班级")[["语文", "数学", "英语"]].mean()
best_idx = df["平均分"].idxmax()
best_student = df.loc[best_idx, "姓名"]
快速参考表
| 操作 | 代码 | 说明 |
|---|---|---|
| 创建数据 | ||
| 创建 Series | pd.Series([1, 2, 3], index=["a", "b", "c"]) |
带标签的一维数组 |
| 创建 DataFrame | pd.DataFrame({"col": [1, 2, 3]}) |
从字典创建表格 |
| 数据读写 | ||
| 读取 CSV | pd.read_csv("file.csv") |
读取 CSV 文件 |
| 读取 Excel | pd.read_excel("file.xlsx") |
读取 Excel 文件 |
| 保存 CSV | df.to_csv("file.csv", index=False) |
保存为 CSV |
| 保存 Excel | df.to_excel("file.xlsx", index=False) |
保存为 Excel |
| 数据选择 | ||
| 选择单列 | df["col"] |
选择一列(返回 Series) |
| 选择多列 | df[["col1", "col2"]] |
选择多列(返回 DataFrame) |
| 按标签选行 | df.loc[label] |
按标签选择行 |
| 按位置选行 | df.iloc[index] |
按位置选择行 |
| 切片选择 | df[start:end] |
切片选择行 |
| 条件过滤 | df[df["col"] > 5] |
筛选满足条件的行 |
| 多条件(与) | df[(df["a"] > 5) & (df["b"] < 10)] |
同时满足多个条件 |
| 多条件(或) | df[(df["a"] > 5) | (df["b"] < 10)] |
满足任一条件 |
| 数据统计 | ||
| 描述统计 | df.describe() |
描述性统计 |
| 平均值 | df.mean() |
每列平均值 |
| 按行计算 | df.mean(axis=1) |
每行平均值 |
| 求和 | df.sum() |
每列总和 |
| 最大值 | df.max() |
每列最大值 |
| 最小值 | df.min() |
每列最小值 |
| 标准差 | df.std() |
每列标准差 |
| 最大值索引 | df["col"].idxmax() |
最大值的索引 |
| 数据清洗 | ||
| 检查缺失值 | df.isnull() |
检查缺失值 |
| 删除缺失值 | df.dropna() |
删除含缺失值的行 |
| 填充缺失值 | df.fillna(value) |
填充缺失值 |
| 删除重复 | df.drop_duplicates() |
删除重复行 |
| 重命名列 | df.rename(columns={"old": "new"}) |
修改列名 |
| 分组与排序 | ||
| 分组 | df.groupby("col") |
按列分组 |
| 分组统计 | df.groupby("col").mean() |
按列分组求平均 |
| 分组聚合 | df.groupby("col").agg({"a": "mean"}) |
分组多统计量 |
| 排序 | df.sort_values("col", ascending=False) |
按列降序排序 |
| 其他操作 | ||
| 新增列 | df["new"] = df["a"] + df["b"] |
添加新列 |
| 查看形状 | df.shape |
返回 (行数, 列数) |
| 查看列名 | df.columns |
获取列名 |
| 查看前N行 | df.head(5) |
查看前5行 |
Pandas vs NumPy 速查
| 需求 | 推荐库 | 原因 |
|---|---|---|
| 纯数值计算、矩阵运算 | NumPy | 更快、更轻量 |
| 表格数据分析、CSV/Excel | Pandas | 功能更丰富 |
| 需要自定义标签索引 | Pandas | Series/DataFrame 自带标签 |
| 处理缺失值 | Pandas | 内置 fillna/dropna |
| 数据分组统计 | Pandas | groupby 功能强大 |
| 机器学习预处理 | 两者结合 | Pandas 清洗 → NumPy 训练 |
记住:NumPy 是地基,Pandas 是建筑。做数据分析时,通常先用 Pandas 处理表格数据,需要高性能计算时再转为 NumPy 数组。
浙公网安备 33010602011771号