查看全课大纲(第 45 / 101 节)
- 1.数据分析基本概念
- 2.学习数据分析的一般路线
- 3.数据分析的流程
- 4.数据类型
- 5.环境部署(1)
- 6.环境部署(2)
- 7.课程介绍
- 8.TXT文件操作
- 9.JSON文件操作
- 10.CSV文件操作
- 11.Excel文件操作
- 12.数据库及SQL常用语法
- 13.数据库基本操作
- 14.数据库多表连接
- 15.实战:欧洲职业足球数据库分析
- 16.爬虫简介
- 17.URL管理模块
- 18.网页下载模块
- 19.网页解析模块(1)
- 20.网页解析模块(2)
- 21.Scrapy简介
- 22.Scrapy使用步骤(1)
- 23.Scrapy使用步骤(2)
- 24.Scrapy使用步骤(3)
- 25.Scrapy使用步骤(4)
- 26.实战:获取国内城市空气质量指数数据
- 27.NumPy和SciPy介绍
- 28.多维数组
- 29.多维数组操作
- 30.NumPy的常用方法
- 31.向量化介绍
- 32.向量化及通用函数
- 33.实战:2016美国大选分析
- 34.数据结构-Series
- 35.数据结构-DataFrame
- 36.数据结构-Index
- 37.Series的索引操作
- 38.DataFrame的索引操作
- 39.索引操作总结
- 40.运算与对齐
- 41.函数应用操作(1) -- map
- 42.函数应用操作 (2) -- apply applymap
- 43.文件读写操作
- 44.排序操作
- 45.数据清洗--处理缺失数据
- 46.数据清洗--处理重复数据
- 47.数据清洗--替换数据
- 48.常用统计方法(1) -- describe quantile
- 49.常用统计方法(2) -- sum mean median count
- 50.常用统计方法(3) -- max min idxmax idxmin
- 51.常用统计方法(4) -- mad var std cumsum
- 52.实战:全球食品数据分析
- 53.层级索引
- 54.分组与聚合介绍
- 55.分组操作(1) -- GroupBy对象及常用聚合操作
- 56.分组操作(2) -- 自定义分组及聚合操作
- 57.透视表介绍
- 58.透视表操作
- 59.数据规整(1) -- 数据合并concat
- 60.数据规整(2) -- 数据连接merge
- 61.数据重构(3) -- 数据重构stack unstack
- 62.实战:互联网电影资料库分析
- 63.探索性数据分析EDA介绍
- 64.EDA的目的
- 65.EDA常用工具
- 66.Matplotlib绘图基本介绍
- 67.Matplotlib画布
- 68.散点图和柱状图的绘制
- 69.直方图的绘制
- 70.矩阵绘图
- 71.子图的使用
- 72.Matplotlib颜色、标记、线型
- 73.Matplotlib坐标刻度、标签、图例、标题
- 74.Seaborn介绍
- 75.数据集分布可视化(1) -- 单变量分布、双变量分布
- 76.数据集分布可视化(2) -- 变量关系可视化
- 77.类别数据可视化 -- 类别散布图、类别内数据分布、类别内统计图
- 78.交互式数据可视化工具Bokeh介绍
- 79.Bokeh绘制散点图、柱状图、盒子图、弦图
- 80.Bokeh绘制常用图形元素
- 81.3D绘图 -- mplot3d
- 82.3D曲线可视化
- 83.3D散点图可视化
- 84.3D柱状图可视化
- 85.Pandas绘图
- 86.实战:Lending Club借贷数据探索性分析及可视化
- 87.机器学习介绍及应用场景
- 88.机器学习建模介绍 (1) -- 分类
- 89.机器学习建模介绍 (2) -- 回归
- 90.机器学习建模介绍 (3) -- 聚类
- 91.机器学习分类
- 92.机器学习工具scikit-learn
- 93.使用scikit-learn的流程
- 94.数据集准备及划分
- 95.模型选择
- 96.数据预处理及特征工程
- 97.过拟合与欠拟合
- 98.模型调参介绍
- 99.模型调参方法
- 100.模型测试及评价
- 101.实战:通过移动设备行为数据预测性别和年龄
数据清洗--处理缺失数据
约 13 分钟
缺失数据的识别与处理
小象实战讲义 · Python数据分析实战
在数据分析的实际工作中,我们拿到的原始数据往往并不“干净”,其中可能包含缺失值、重复值或无效值。这些“脏数据”会严重影响后续的分析、处理和建模工作的准确性与可靠性。本节我们将聚焦于数据清洗中最常见的问题——缺失数据的处理,学习如何使用 pandas 的强大功能来识别、判断和处理这些“数据空洞”。
💡 核心导读
- 数据清洗的重要性:理解数据清洗是数据分析流程中不可或缺的迭代步骤,直接决定分析结果的质量。
- 缺失值检测:掌握使用
isnull()方法识别缺失值,并结合any()方法快速定位存在缺失值的行或列。 - 缺失值处理策略:学习两种核心处理方式:直接丢弃 (
dropna) 与填充 (fillna)。 - 智能填充方法:了解并实践基于数据顺序的前向填充 (
ffill) 与后向填充 (bfill),理解其适用场景与前提条件。
数据清洗:从“脏数据”到“干净数据”
数据清洗并非一次性任务,而是一个贯穿数据分析项目始终的迭代过程。我们首先要明确几个关键问题:数据是否需要修改?需要修改什么?如何调整数据才能使其适用于后续的分析与挖掘?数据清洗正是回答这些问题的实践,其常见操作包括处理缺失数据、处理重复数据以及处理无效数据。本节我们将深入探讨如何处理缺失数据。
识别缺失值:isnull() 与 any() 的组合拳
处理缺失值的第一步是发现它们。在 pandas 中,缺失值通常由 NaN(Not a Number)表示。我们可以使用 isnull() 方法来检测数据中的每个元素是否为缺失值。
isnull() 方法会返回一个与原数据结构(Series 或 DataFrame)形状相同的布尔型对象,其中 True 表示该位置是缺失值,False 则表示不是。
然而,直接查看每个元素的布尔值并不高效。我们更关心的是:哪些列或行存在缺失值? 这时,可以结合 any() 方法。any() 的作用是:在给定的轴向上,只要有一个 True,结果就返回 True。默认情况下,axis=0 表示按列计算,axis=1 表示按行计算。
下面我们通过一个模拟的用户行为日志数据来演示。
import pandas as pd
import numpy as np
# 构造一个模拟的用户播放日志DataFrame,包含缺失值
data = {
'time': [4424, 4424, 4425, 4426, 4427, 4428],
'user': ['ch', 'sue', 'ch', 'sue', 'ch', 'sue'],
'video': ['v1', 'v1', 'v2', 'v2', 'v3', 'v3'],
'playbackposition': [0, 5, 10, 15, 20, 25],
'paused': [False, np.nan, np.nan, True, np.nan, False],
'volume': [10, np.nan, 5, np.nan, 10, np.nan]
}
df = pd.DataFrame(data)
print("原始模拟数据:")
print(df)原始模拟数据:
time user video playbackposition paused volume
0 4424 ch v1 0 False 10.0
1 4424 sue v1 5 NaN NaN
2 4425 ch v2 10 NaN 5.0
3 4426 sue v2 15 True NaN
4 4427 ch v3 20 NaN 10.0
5 4428 sue v3 25 False NaN现在,我们来检测并定位缺失值。
# 检测缺失值并汇总 (此代码块完全自包含)
import pandas as pd
import numpy as np
# 重新构造数据,确保本代码块独立
df_local = pd.DataFrame({
'time': [4424, 4424, 4425, 4426, 4427, 4428],
'user': ['ch', 'sue', 'ch', 'sue', 'ch', 'sue'],
'video': ['v1', 'v1', 'v2', 'v2', 'v3', 'v3'],
'playbackposition': [0, 5, 10, 15, 20, 25],
'paused': [False, np.nan, np.nan, True, np.nan, False],
'volume': [10, np.nan, 5, np.nan, 10, np.nan]
})
print("1. 使用 isnull() 检测每个元素:")
print(df_local.isnull())
print("\n2. 检查哪些列包含缺失值 (any, axis=0):")
print(df_local.isnull().any())
print("\n3. 检查哪些行包含缺失值 (any, axis=1):")
print(df_local.isnull().any(axis=1))1. 使用 isnull() 检测每个元素:
time user video playbackposition paused volume
0 False False False False False False
1 False False False False True True
2 False False False False True False
3 False False False False False True
4 False False False False True False
5 False False False False False True
2. 检查哪些列包含缺失值 (any, axis=0):
time False
user False
video False
playbackposition False
paused True
volume True
dtype: bool
3. 检查哪些行包含缺失值 (any, axis=1):
0 False
1 True
2 True
3 True
4 True
5 True
dtype: bool从输出可以看出:
isnull()返回了每个单元格的布尔状态。- 结合
any(axis=0)发现,paused和volume两列存在缺失值。 - 结合
any(axis=1)发现,除了第0行,其他行都至少包含一个缺失值。
处理缺失值:丢弃与填充
发现缺失值后,我们有两种主要的处理策略:丢弃和填充。选择哪种策略需要结合业务背景和数据情况来决定。
丢弃缺失值:dropna()
如果缺失值数量不多,或者经过与业务方沟通后确认可以丢弃,那么 dropna() 是最直接的方法。默认情况下,dropna() 会丢弃任何列包含缺失值的整行。
# 处理缺失值:丢弃与填充 (此代码块完全自包含)
import pandas as pd
import numpy as np
# 重新构造数据
df_local = pd.DataFrame({
'time': [4424, 4424, 4425, 4426, 4427, 4428],
'user': ['ch', 'sue', 'ch', 'sue', 'ch', 'sue'],
'video': ['v1', 'v1', 'v2', 'v2', 'v3', 'v3'],
'playbackposition': [0, 5, 10, 15, 20, 25],
'paused': [False, np.nan, np.nan, True, np.nan, False],
'volume': [10, np.nan, 5, np.nan, 10, np.nan]
})
print("1. 丢弃任何列包含缺失值的行 (dropna):")
df_dropped_all = df_local.dropna()
print(df_dropped_all)
print("\n2. 只丢弃 'paused' 列有缺失值的行 (dropna, subset=['paused']):")
df_dropped_paused = df_local.dropna(subset=['paused'])
print(df_dropped_paused)
print("\n3. 用固定值 -1 填充所有缺失值 (fillna):")
df_filled_const = df_local.fillna(-1)
print(df_filled_const)1. 丢弃任何列包含缺失值的行 (dropna):
time user video playbackposition paused volume
0 4424 ch v1 0 False 10.0
2. 只丢弃 'paused' 列有缺失值的行 (dropna, subset=['paused']):
time user video playbackposition paused volume
0 4424 ch v1 0 False 10.0
3 4426 sue v2 15 True NaN
5 4428 sue v3 25 False NaN
3. 用固定值 -1 填充所有缺失值 (fillna):
time user video playbackposition paused volume
0 4424 ch v1 0 False 10.0
1 4424 sue v1 5 -1 -1.0
2 4425 ch v2 10 -1 5.0
3 4426 sue v2 15 True -1.0
4 4427 ch v3 20 -1 10.0
5 4428 sue v3 25 False -1.0关键点解析:
dropna():由于只有第0行没有缺失值,所以丢弃后只剩一行。subset参数:通过指定subset=['paused'],我们只关注paused列是否有缺失值。因此,paused列不为NaN的行(第0、3、5行)被保留了下来,即使它们的volume列可能有缺失值。fillna(value):使用一个固定的值(这里是-1)填充所有缺失值。这种方法简单,但可能引入偏差,需谨慎使用。
智能填充:前向与后向填充
在时间序列或有序数据中,我们常常使用相邻的数据来填充缺失值,即前向填充(ffill) 和后向填充(bfill)。
ffill:用缺失值前面的有效值进行填充。(旧版 pandas 中它也叫pad,该别名已在 pandas 3.0 移除,请统一使用ffill。)bfill:用缺失值后面的有效值进行填充。(旧别名backfill同样已移除。)
重要前提:使用这两种方法时,数据必须是有序的(例如按时间排序),否则“前”、“后”将失去意义。
# 智能填充:前向与后向填充 (此代码块完全自包含)
import pandas as pd
import numpy as np
# 重新构造数据
df_local = pd.DataFrame({
'time': [4424, 4424, 4425, 4426, 4427, 4428],
'user': ['ch', 'sue', 'ch', 'sue', 'ch', 'sue'],
'video': ['v1', 'v1', 'v2', 'v2', 'v3', 'v3'],
'playbackposition': [0, 5, 10, 15, 20, 25],
'paused': [False, np.nan, np.nan, True, np.nan, False],
'volume': [10, np.nan, 5, np.nan, 10, np.nan]
})
print("1. 对数据按时间和用户排序:")
df_sorted = df_local.sort_values(by=['time', 'user'])
print(df_sorted)
print("\n2. 应用前向填充 (ffill):")
df_ffill = df_sorted.ffill()
print(df_ffill)
print("\n3. 应用后向填充 (bfill):")
df_bfill = df_sorted.bfill()
print(df_bfill)1. 对数据按时间和用户排序:
time user video playbackposition paused volume
0 4424 ch v1 0 False 10.0
1 4424 sue v1 5 NaN NaN
2 4425 ch v2 10 NaN 5.0
3 4426 sue v2 15 True NaN
4 4427 ch v3 20 NaN 10.0
5 4428 sue v3 25 False NaN
2. 应用前向填充 (ffill):
time user video playbackposition paused volume
0 4424 ch v1 0 False 10.0
1 4424 sue v1 5 False 10.0
2 4425 ch v2 10 False 5.0
3 4426 sue v2 15 True 5.0
4 4427 ch v3 20 True 10.0
5 4428 sue v3 25 False 10.0
3. 应用后向填充 (bfill):
time user video playbackposition paused volume
0 4424 ch v1 0 False 10.0
1 4424 sue v1 5 True 5.0
2 4425 ch v2 10 True 5.0
3 4426 sue v2 15 True 10.0
4 4427 ch v3 20 False 10.0
5 4428 sue v3 25 False NaN填充逻辑解读:
- 前向填充 (ffill):
- 第1行:
paused用上一行(第0行)的False填充;volume用上一行的10.0填充。 - 第2行:
paused继续用上一行(现在是填充后的第1行)的False填充。 - 第3行:
paused本身是True,这是一个新值,后续的填充将基于此新值。volume用上一行(第2行)的5.0填充。 - 第4行:
paused用上一行(第3行)的True填充;volume用上一行的5.0填充?等等,这里输出是10.0。仔细看原始数据,第4行的volume原始值就是10.0,并非缺失值,所以它不会被填充,而是保持原值。paused列第4行是缺失值,所以被填充为True。 - 第5行:
paused本身是False,非缺失;volume是缺失值,用上一行(第4行)的10.0填充。
- 第1行:
- 后向填充 (bfill):
- 从下往上用后面的值填充前面的缺失值。
- 第5行:
volume是缺失值,但后面没有行了,所以无法填充,保持NaN。 - 第4行:
paused是缺失值,用下一行(第5行)的False填充。 - 第3行:
volume是缺失值,用下一行(第4行)的10.0填充。 - 第2行:
paused是缺失值,用下一行(第3行)的True填充。 - 第1行:
paused和volume都是缺失值,分别用下一行(第2行)的True和5.0填充。
📝 动手练一练
- 概念辨析:
df.isnull().any()和df.isnull().any(axis=1)返回的结果分别代表什么含义?在什么业务场景下你会更关注行的缺失情况,什么场景下更关注列的缺失情况?
👉 点击查看参考答案
df.isnull().any()(默认axis=0)返回一个 Series,索引是列名,布尔值表示该列是否至少包含一个缺失值。它帮助我们快速定位哪些特征(列)存在数据缺失。df.isnull().any(axis=1)返回一个 Series,索引是行号,布尔值表示该行是否至少包含一个缺失值。它帮助我们快速定位哪些样本(行)存在数据不完整。- 业务场景:
- 关注列缺失:当进行特征工程或建模时,我们需要知道哪些特征的数据质量有问题,可能需要考虑删除该特征、进行专门填充或使用能处理缺失值的算法。
- 关注行缺失:当样本非常珍贵,或者分析是基于完整用户行为序列时,我们需要知道哪些用户的记录不完整,可能会影响对用户整体行为的分析,从而决定是剔除该用户还是想办法补全其数据。
- 动手实践:假设你有一个小的销售数据
DataFrame,包含产品A和B的每日销售额,其中有很多缺失值。请先按日期排序,然后分别用前向填充和后向填充处理缺失值,并观察最后一天和第一天的数据在两种填充方式下的结果有何不同。# 初始数据 import pandas as pd import numpy as np df_sales = pd.DataFrame({ 'date': ['2023-01-02', '2023-01-01', '2023-01-04', '2023-01-03'], 'A': [100, np.nan, np.nan, 150], 'B': [np.nan, 50, 80, np.nan] })
👉 点击查看参考答案
import pandas as pd
import numpy as np
df_sales = pd.DataFrame({
'date': ['2023-01-02', '2023-01-01', '2023-01-04', '2023-01-03'],
'A': [100, np.nan, np.nan, 150],
'B': [np.nan, 50, 80, np.nan]
})
# 1. 按日期排序
df_sales['date'] = pd.to_datetime(df_sales['date'])
df_sorted = df_sales.sort_values('date')
print("排序后数据:")
print(df_sorted)
# 2. 前向填充
df_ffill = df_sorted.ffill()
print("\n前向填充结果:")
print(df_ffill)
# 3. 后向填充
df_bfill = df_sorted.bfill()
print("\n后向填充结果:")
print(df_bfill)
# 观察首尾
print("\n观察:")
print("- 前向填充:第一天(2023-01-01)的A列因前面无值,仍为NaN;最后一天(2023-01-04)的A、B列均用前值填充。")
print("- 后向填充:最后一天(2023-01-04)的A列因后面无值,仍为NaN;第一天(2023-01-01)的A、B列均用后值填充。")排序后数据:
date A B
1 2023-01-01 NaN 50.0
0 2023-01-02 100.0 NaN
3 2023-01-03 150.0 NaN
2 2023-01-04 NaN 80.0
前向填充结果:
date A B
1 2023-01-01 NaN 50.0
0 2023-01-02 100.0 50.0
3 2023-01-03 150.0 50.0
2 2023-01-04 150.0 80.0
后向填充结果:
date A B
1 2023-01-01 100.0 50.0
0 2023-01-02 100.0 80.0
3 2023-01-03 150.0 80.0
2 2023-01-04 NaN 80.0
观察:
- 前向填充:第一天(2023-01-01)的A列因前面无值,仍为NaN;最后一天(2023-01-04)的A、B列均用前值填充。
- 后向填充:最后一天(2023-01-04)的A列因后面无值,仍为NaN;第一天(2023-01-01)的A、B列均用后值填充。本章小结
本节我们深入学习了数据清洗中处理缺失数据的关键技能。数据清洗是保障分析质量的基础,而处理缺失值是其中的首要任务。
📋 行动清单
- 掌握检测技能:对于任何新拿到手的数据集,第一件事就是用
df.isnull().any()和df.isnull().sum()快速扫描缺失情况。 - 明确处理策略:根据业务背景和数据缺失的严重程度,与业务方沟通后,决定采用丢弃 (
dropna)、固定值填充 (fillna) 还是智能填充 (ffill/bfill)。 - 理解填充前提:牢记使用
ffill或bfill时,必须确保数据在相关维度上(如时间)是有序的,否则结果将误导分析。 - 迭代验证:处理缺失值后,务必再次检查数据,确保处理方式没有引入新的问题或偏差,并理解这很可能是一个需要多次调整的迭代过程。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载本节课件:数据清洗--处理缺失数据PDF · 215KB
- 下载全套课件打包(第1-5章)ZIP · 12.8MB
- 下载全套课件打包(第6-8章)ZIP · 15MB
- 下载实战数据集:AppleStore 应用商城分析ZIP · 329KB
- 下载实战数据集:女性服装电商分析ZIP · 2.8MB
- 下载Python 数据分析环境搭建指南PDF · 2MB
- 下载Scrapy 安装教程PDF · 12.7MB
- 下载附加实战项目:AppleStore 应用商城数据分析ZIP · 0.3MB · ipynb + CSV 数据
- 下载附加实战项目:银行电话营销数据分析ZIP · 0.4MB · ipynb + CSV 数据
- 下载附加实战项目:女性服装电商评论数据分析ZIP · 2.7MB · ipynb + CSV 数据
- 下载附加实战项目:美国化学学会杂志数据分析ZIP · 34.2MB · ipynb + SQLite 数据库