视频链接

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 │
└────┴────┴────┘        └────┴───────┘        └────┴───────┘

解释~(非)将条件取反TrueFalseFalseTrue


注意:多条件时必须用 &(与)、|(或)、~(非),而不是 andornot,并且每个条件要用括号括起来。

数据选择函数

函数/操作 示例 说明
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 数组。

posted on 2026-02-10 11:27  Yancey_chan  阅读(103)  评论(0)    收藏  举报