Python数据挖掘之数据预处理
一、 数据预处理的理由在现实应用中,原始数据往往直接来源于企业业务系统、传感器、用户输入或网络采集等多种渠道。这类数据通常存在以下问题:不完整性:部分属性缺失,导致数据无法完整反映对象特征。不一致性:同一对象在不同数据源中的描述可能不一样。冗余性:不同来源的数据存在重复,影响分析效率。噪声与错误:包括异常值、录入错误或不合理的取值。规模庞大:数据量巨大,直接挖掘会导致计算开销过高。因此,数据预处理
文章目录
一、 数据预处理的理由
在现实应用中,原始数据往往直接来源于企业业务系统、传感器、用户输入或网络采集等多种渠道。这类数据通常存在以下问题:
不完整性:部分属性缺失,导致数据无法完整反映对象特征。
不一致性:同一对象在不同数据源中的描述可能不一样。
冗余性:不同来源的数据存在重复,影响分析效率。
噪声与错误:包括异常值、录入错误或不合理的取值。
规模庞大:数据量巨大,直接挖掘会导致计算开销过高。
因此,数据预处理的目标是:
提高数据的质量,使其更真实、完整和一致。
降低数据挖掘的难度和计算开销。
为后续的数据建模与分析提供更高效、更可靠的输入。
二、数据预处理的主要内容
- 数据集成
定义:将来自多个异构数据源的数据进行统一整合,形成一致的全局视图。
内容:
识别并解决模式冲突(如不同系统对“性别”的编码方式不同:M/F vs 1/0)。
处理冗余数据,避免同一对象重复记录。
建立全局一致的数据仓库,方便统一分析。
- 数据清洗
定义:识别并纠正数据中的错误或异常,使数据更加准确和完整。
内容:
缺失值处理:删除记录、插值填补、均值替换或基于模型预测补齐。
异常值处理:识别错误或极端值,可选择修正或删除。
不一致值校正:例如日期格式统一(2025-09-13 vs 13/09/2025)。
噪声平滑:通过分箱、回归或聚类方法减少随机波动的干扰。
- 数据变换
定义:对原始数据进行数学或逻辑上的转换,以适应挖掘算法的输入需求。
内容:
归一化与标准化:将不同量纲的数据转化为同一量纲(如将收入、年龄都缩放到 0-1)。
离散化:把连续属性划分为有限区间(如年龄分组:青年、中年、老年)。
属性构造:通过已有数据生成新的属性,提高特征表达力(如通过“身高”和“体重”构造 BMI 指标)。
数据聚合:对数据进行汇总,例如按月统计销售额。
- 数据规约
定义:在尽量保留数据有效信息的前提下,减少数据规模,从而提高处理效率。
内容:
维度规约:通过主成分分析(PCA)、特征选择等方法减少属性数。
数值规约:采用直方图、聚类、采样等方法减少数据记录数量。
数据压缩:利用编码或存储优化技术降低数据存储空间。
数据集成
数据集成(Data Integration)
- 基本概念
数据集成是指将来自多个不同(异构)数据源的数据集合到一起,并在统一的存储环境(如数据仓库)中提供一致的、全局化的视图。
异构性:数据可能来自不同类型的系统,如关系数据库、Excel 文件、文本文件、多维数据集、传感器数据等。
统一性:通过数据集成,可以屏蔽底层数据格式和结构差异,向用户或应用提供统一的查询和分析接口。
简单来说,数据集成就是**“把分散的数据整合成一个整体”**。
- 数据集成的主要过程
数据集成通常包括以下几个环节:
数据源识别
确定需要集成的不同数据源(如销售数据库、客户信息系统、物流文件等)。
模式匹配与模式转换
不同数据源的**模式(schema)**可能不同,例如:
数据库 A 中“性别”字段存储为 M/F,数据库 B 存储为 1/0。
数据库 A 中“出生日期”字段是 yyyy-mm-dd,数据库 B 是 dd/mm/yyyy。
在集成时,需要通过模式映射将它们统一。
冲突检测与解决
命名冲突:不同系统可能用不同名称表示同一概念(如“客户号”和“客户ID”)。
数据冲突:同一对象在不同系统中的数据值不一致(如客户地址不同)。
冗余冲突:同一对象被重复存储。
这些冲突需要通过规则或算法来识别与消解。
数据清洗与转换
对不同来源的数据进行格式统一、缺失值补全、异常值处理。
确保集成后的数据准确、完整、一致。
数据存储与统一视图提供
集成后的数据一般存放在 数据仓库 或 数据湖中。
最终对用户或上层应用提供一个统一的访问接口,方便进行分析与挖掘。
- 数据集成的意义
打破“信息孤岛”,消除企业内部或跨系统的数据割裂。
提供完整一致的全局数据视图,支持决策分析。
为数据挖掘、机器学习和商业智能(BI)奠定基础。

数据集成过程中的主要问题
在数据集成过程中,由于不同数据源的设计目的、应用背景和管理方式不同,往往会出现以下常见冲突问题:
(1)同名异义
含义:不同数据源中使用了相同的属性名,但它们实际表示的含义不同。
例子:
员工信息表 A 中的属性 ID 表示 员工编号;
交易记录表 B 中的属性 ID 表示 订单编号。
影响:如果直接合并会造成语义混淆,导致数据分析错误。
解决方法:需要通过上下文语义分析或元数据管理来区分,并在集成时改为唯一、清晰的命名。
(2)异名同义
含义:不同数据源中使用了不同的名称,但它们表示的实际含义相同。
例子:
数据表 A 中的属性 sales_dt,
数据表 B 中的属性 sales_date,
实际上都表示 销售日期。
影响:如果不统一,会造成重复字段,影响分析效率。
解决方法:通过模式匹配、属性对齐技术识别同义属性,并在集成时进行统一命名。
(3)单位不统一
含义:不同数据源对同一属性采用了不同的计量单位。
例子:
数据表 A 中使用国际单位制(如米、千克、美元),
数据表 B 中使用我国传统单位(如市尺、市斤、人民币元)。
影响:如果不转换,数据无法进行准确比较和计算。
解决方法:在集成过程中进行单位换算,保证量纲一致。
(4)颗粒度不统一
含义:不同数据源对同一对象的描述粒度不同。
例子:
数据表 A 记录的是 每天、每个城市的销售数据;
数据表 B 记录的是 每月、每个省份的销售数据。
影响:直接整合会导致数据粒度不一致,难以进行统一分析。
解决方法:需要进行数据聚合或细分,即:
将细粒度数据汇总到粗粒度水平(如按日数据汇总为月数据);
或将粗粒度数据细化(如果可能,通过补充或推算获得更细粒度)。
小结
在数据集成过程中,必须重点解决以下四类问题:
同名异义 → 消歧义、改名
异名同义 → 统一命名
单位不统一 → 单位换算
颗粒度不统一 → 粒度对齐(聚合/细化)
这些处理步骤能够保证集成后的数据语义一致、结构统一、可比性强,为后续的数据分析和挖掘打下坚实基础。
数据堆叠(Stacking)
(1)基本概念
数据堆叠是指:按照某一维度(行或列)将两个或多个 DataFrame 对象拼接在一起,形成一个更大的数据表。
沿 行方向(axis=0):把多张表的数据上下堆叠。
沿 列方向(axis=1):把多张表的数据左右拼接。
这在数据集成中很常见,比如:
每个月的销售数据存放在单独的表中,需要按行拼接成年度数据;
不同来源的属性数据存放在不同表中,需要按列拼接成一个综合表。
(2)Pandas concat() 函数
Pandas 提供的 concat() 方法常用于数据堆叠。
pandas.concat(objs, axis=0, join='outer', ignore_index=False)
# 参数说明:
objs:需要拼接的对象,可以是一个列表(如 [df1, df2])。
axis:拼接方向,0 表示行拼接(默认),1 表示列拼接。
join:索引对齐方式,
'outer' 表示并集(保留所有索引,缺失位置补 NaN);
'inner' 表示交集(只保留公共索引)。
ignore_index:是否忽略原索引,重新生成新索引。
(3)示例代码
行拼接(纵向堆叠)
import pandas as pd
# 创建两个 DataFrame
df1 = pd.DataFrame({
'ID': [1, 2, 3],
'Name': ['张三', '李四', '王五']
})
df2 = pd.DataFrame({
'ID': [4, 5],
'Name': ['赵六', '孙七']
})
# 行拼接
result = pd.concat([df1, df2], axis=0, ignore_index=True)
print(result)
列拼接(横向堆叠)
df3 = pd.DataFrame({
'Age': [23, 25, 30]
})
# 列拼接
result = pd.concat([df1, df3], axis=1)
print(result)
行拼接(纵向堆叠)
Python 的代码是:
python
result = pd.concat([df1, df2], axis=0, ignore_index=True)
对应 SQL 里就是把两个表的数据 直接上下拼接,常用 UNION ALL:
sql
SELECT ID, Name
FROM df1
UNION ALL
SELECT ID, Name
FROM df2;
# 说明:
UNION ALL 会保留所有行(包括重复值)。
如果用 UNION,会自动去重。
ignore_index=True 在 SQL 里没有对应功能,因为 SQL 没有“行索引”概念。
2. 列拼接(横向堆叠)
Python 的代码是:
python
result = pd.concat([df1, df3], axis=1)
对应 SQL 里就是把两个表按照行号对齐拼接,常见写法是 JOIN + 行号对齐。
在标准 SQL 里没有“行号”,所以需要先给每个表加上行号,再按行号 JOIN。
例如在支持窗口函数的数据库(如 PostgreSQL、SQL Server、Oracle、MySQL 8+):
sql
WITH t1 AS (
SELECT ID, Name, ROW_NUMBER() OVER (ORDER BY ID) AS rn
FROM df1
),
t2 AS (
SELECT Age, ROW_NUMBER() OVER (ORDER BY Age) AS rn
FROM df3
)
SELECT t1.ID, t1.Name, t2.Age
FROM t1
JOIN t2 ON t1.rn = t2.rn;

数据增补(Append)
(1)基本概念
数据增补是一种纵向合并方式,本质上与 concat(axis=0) 类似。
在 Pandas 中,可以通过 调用 append() 方法,将一个 DataFrame 的数据追加到另一个 DataFrame 的尾部。
常用于“补充数据”的场景,例如:在已有的客户表里,增加新导入的一批客户信息。
(2)语法
DataFrame.append(other, ignore_index=False)
# 参数说明:
other:需要追加的 DataFrame 或字典。
ignore_index:是否忽略原索引,重新生成新索引。
False(默认):保留原 DataFrame 的索引。
True:重新编号。
⚠️ 注意:在 Pandas 1.4 之后,官方建议直接使用 pd.concat([df1, df2]),因为 append() 已被弃用,但思想是一样的。
import pandas as pd
# 创建第一个 DataFrame
df1 = pd.DataFrame({
'ID': [1, 2, 3],
'Name': ['张三', '李四', '王五']
})
# 创建第二个 DataFrame
df2 = pd.DataFrame({
'ID': [4, 5],
'Name': ['赵六', '孙七']
})
# 使用 append 追加 df2 到 df1
result = df1.append(df2, ignore_index=True)
print(result)
(4)对应的 SQL 写法
数据增补和 concat(axis=0) 本质一样,对应 SQL 里的 UNION ALL:
SELECT ID, Name
FROM df1
UNION ALL
SELECT ID, Name
FROM df2;
append() 是一种 纵向拼接(数据增补),与 concat(axis=0) 效果类似。
在 Pandas 新版本中推荐使用 pd.concat() 替代 append()。
对应 SQL 操作是 UNION ALL。
数据合并(Merge)
(1)基本概念
数据合并 是指通过主键或共同字段将两个 DataFrame 进行对齐整合,类似于 SQL 中的 JOIN 操作。
Pandas 的 merge() 函数可以灵活实现 内连接、左连接、右连接、外连接等多种方式。
常用于:
将用户表和订单表通过 UserID 进行合并;
将学生表和成绩表通过 学号 进行合并。
(2)函数语法
pd.merge(left, right, how='inner', on=None,
left_on=None, right_on=None,
left_index=False, right_index=False, suffixes=('_x', '_y'))
# 参数说明:
left / right:要合并的两个 DataFrame。
how:连接方式
'inner'(默认):取交集(内连接)。
'left':以左表为主(左连接)。
'right':以右表为主(右连接)。
'outer':取并集(外连接)。
on:指定两个表共有的列作为连接键。
left_on / right_on:如果左右表的连接列名不同,可以分别指定。
left_index / right_index:是否用索引作为连接键。
suffixes:当两个表中存在同名列时,用于区分的后缀。
(3)示例代码
示例 1:内连接(inner join)
import pandas as pd
# 学生信息表
students = pd.DataFrame({
'ID': [1, 2, 3],
'Name': ['张三', '李四', '王五']
})
# 成绩表
scores = pd.DataFrame({
'ID': [1, 2, 4],
'Score': [85, 90, 88]
})
# 按照 ID 合并(内连接)
result = pd.merge(students, scores, how='inner', on='ID')
print(result)
对应 SQL:
SELECT s.ID, s.Name, sc.Score
FROM students s
INNER JOIN scores sc
ON s.ID = sc.ID;
示例 2:左连接(left join)
result = pd.merge(students, scores, how='left', on='ID')
print(result)
SELECT s.ID, s.Name, sc.Score
FROM students s
LEFT JOIN scores sc
ON s.ID = sc.ID;
示例 3:外连接(outer join)
result = pd.merge(students, scores, how='outer', on='ID')
print(result)
SELECT s.ID, s.Name, sc.Score
FROM students s
FULL OUTER JOIN scores sc
ON s.ID = sc.ID;
import pandas as pd
import numpy as np
left = pd.DataFrame({"A":[0,0,1,2], "B":[0,1,0,1], "C":[0,0,1,1]})
right = pd.DataFrame({"A":[0,1,0,2], "B":[0,0,1,0], "D":[1,1,0,0]})
print("左数据框对象: \n", left)
print("右数据框对象: \n", right)
result1 = pd.merge(left, right, how = "left", on = ["A", "B"]) # 左连接
print("(1)左连接数据结果: \n", result1)
result2 = pd.merge(left, right, how = "right", on = ["A", "B"]) # 右连接
print("(2)右连接数据结果: \n", result2)
result3 = pd.merge(left, right, how = "inner", on = ["A", "B"]) # 内连接
print("(3)内连接数据结果: \n", result3)
result4 = pd.merge(left, right, how = "outer", on = ["A", "B"]) # 外连接
print("(4)外连接数据结果: \n", result4)
数据清洗
-
基本概念
数据清洗是指:通过检测和处理数据中的各种质量问题(如重复值、错误值、异常值、缺失值、不一致值等),提高数据的准确性、完整性和一致性。
它是数据预处理阶段中最重要、最耗时的一项工作,通常占据整个数据挖掘流程 60%–80% 的时间。 -
常见的数据问题
在实际数据中,常见的问题包括:
重复值:相同记录多次出现,导致统计结果偏差。
错误值:由于录入或传感器错误,出现不合理的数值(如性别=“未知值”)。
异常值:与整体规律差异较大的值(如年龄=200)。
缺失值:部分属性没有记录(如客户手机号为空)。
不一致值:同一数据在不同表或字段中的格式或含义不一致(如日期格式 2025/09/13 vs 13-09-2025)。
- 常见的数据清洗技术
(1)缺失值处理
删除法:直接删除缺失值所在的记录或属性(适用于缺失率较低)。
填充法:使用均值、中位数、众数、前后值填充。
预测法:利用回归、插值、机器学习模型预测缺失值。
(2)重复值处理
检测:通过主键、唯一约束或相似性计算检测重复记录。
去重:保留一条有效记录,删除其他重复项。
(3)异常值处理
统计法:基于均值 ± 3σ、箱线图(IQR)等方法识别异常值。
模型法:通过聚类、分类或回归模型检测不符合规律的数据点。
处理:删除、修正或保留(若异常值确实有业务意义)。
(4)错误值处理
规则检查:如性别只能为 {男, 女};年龄必须 > 0。
格式校验:如身份证号码、邮箱地址、手机号的正则校验。
修正规则:如把 “M”/“F” 统一改成 “男”/“女”。
(5)不一致值处理
统一格式:日期、货币、编码等(如 YYYY-MM-DD)。
单位换算:不同系统可能使用不同单位,需要标准化。
语义对齐:通过元数据或映射表统一同义字段。
import pandas as pd
import numpy as np
# 构造示例数据
data = {
'ID': [1, 2, 2, 3, 4],
'Name': ['张三', '李四', '李四', '王五', None],
'Age': [23, -5, -5, 200, 30],
'Score': [85, 90, None, 120, 95]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
# 1. 去重
df = df.drop_duplicates()
# 2. 缺失值填充
df['Name'] = df['Name'].fillna('未知')
df['Score'] = df['Score'].fillna(df['Score'].mean())
# 3. 错误值和异常值处理
df.loc[df['Age'] < 0, 'Age'] = np.nan # 错误值标记为空
df.loc[df['Age'] > 120, 'Age'] = np.nan # 异常值标记为空
df['Age'] = df['Age'].fillna(df['Age'].median()) # 填充中位数
print("\n清洗后的数据:")
print(df)
数据清洗是数据预处理的核心任务,目标是提高数据的质量,使其更适合建模与挖掘。
常见问题包括:重复、缺失、错误、异常、不一致。
处理方法既有简单的统计规则,也有基于机器学习的智能方法。
数据重复问题
在数据集成过程中,由于不同来源的数据表可能包含相同或相似的信息,合并后容易产生数据重复。常见的重复分为两类:
(1)记录重复(Row Duplication)
定义:同一个实体被多个数据对象(行数据)重复描述。
表现形式:
不同数据源中包含相同的客户记录;
同一员工在不同系统中出现多条信息,部分字段可能不完全一致。
- DataFrame.duplicated()
作用
用于检测重复行,返回一个布尔型 Series,标记每一行是否为重复。
语法
DataFrame.duplicated(subset=None, keep='first')
# 参数说明
subset:指定要检测的列(默认 None,表示所有列)。
keep:定义保留哪一条为“非重复”,其余标记为重复。
'first':保留第一次出现的,把后续重复标记为 True(默认)。
'last':保留最后一次出现的,把前面的标记为 True。
False:所有重复值都标记为 True。
- DataFrame.drop_duplicates()
作用
用于删除重复行,返回去重后的 DataFrame。
语法
DataFrame.drop_duplicates(subset=None, keep='first', inplace=False)
参数说明
subset:指定要检查重复的列。
keep:与 duplicated() 相同,用于决定保留哪一条。
inplace:
False(默认):返回新对象,不修改原始 DataFrame。
True:直接在原始 DataFrame 上修改。
- 示例
import pandas as pd
# 构造数据
data = {
'ID': [1, 2, 2, 3, 3, 3],
'Name': ['张三', '李四', '李四', '王五', '王五', '王五']
}
df = pd.DataFrame(data)
print("原始数据:\n", df)
# 1. 检测重复
print("\n检测重复(默认):")
print(df.duplicated())
print("\n检测重复(subset=['Name'], keep=False):")
print(df.duplicated(subset=['Name'], keep=False))
# 2. 删除重复
print("\n删除重复(默认保留第一次出现):")
print(df.drop_duplicates())
print("\n删除重复(subset=['Name'], 保留最后一次):")
print(df.drop_duplicates(subset=['Name'], keep='last'))
duplicated() → 标记重复行(返回布尔值)。
drop_duplicates() → 删除重复行(返回新表或修改原表)。
常与 subset 参数配合使用,用于根据特定字段去重。
import pandas as pd
scores = {'姓名': ['张三', '李四', '王五', '张三'],
'语文': [ 84, 92, 87, 84],
'数学': [ 89, 90, 95, 89],
'英语': [ 90, 81, 75, 92],
'计算机': [ 85, 92, 90, 85]}
df = pd.DataFrame(scores)
print("检验重复的记录:\n", df.duplicated(subset = ['姓名']))
df_drop = df.drop_duplicates(subset = ['姓名'], keep = 'first')
print("去重的数据为:\n", df_drop )
特征重复(冗余特征)
- 基本概念
特征冗余:如果某个特征(列/属性)能由其它特征直接或间接推导出来,那么这个特征就是冗余的。
在数据集成时(尤其是堆叠、合并多表格的过程),常会出现多余的字段,例如:
出生日期 和 年龄 同时存在(其中一个可以由另一个推导)。
总价、数量 和 单价 同时存在(总价 = 数量 × 单价)。
多来源数据表中,同样的字段以不同名字出现(如 sales_date 与 order_date)。
特征冗余不仅浪费存储和计算资源,还可能导致模型过拟合,降低训练效率。
- 冗余检测方法
(1)标称特征(分类变量)
可以使用 卡方检验(Chi-square Test) 来检测特征间是否存在强关联。
如果两个分类特征之间高度相关,则可能存在冗余。
(2)数值特征(连续变量)
可以使用 相关系数(Correlation Coefficient) 来判断。
常见的相关系数有 皮尔逊相关系数(Pearson r)。
如果两个特征之间的相关系数接近 ±1,说明它们高度冗余。
import pandas as pd
import numpy as np
# 构造一个包含冗余特征的数据集
data = {
'age': [20, 25, 30, 35, 40],
'birth_year': [2003, 1998, 1993, 1988, 1983],
'income': [3000, 4000, 5000, 6000, 7000],
'spending': [1500, 2000, 2500, 3000, 3500]
}
df = pd.DataFrame(data)
print("数据集:\n", df)
# 计算相关系数矩阵
corr_matrix = df.corr()
print("\n相关系数矩阵:\n", corr_matrix)
import pandas as pd
scores = {'姓名': ['张三', '李四', '王五', '张三'],
'语文': [ 84, 92, 87, 84],
'数学': [ 89, 90, 95, 89],
'英语': [ 90, 81, 75, 92],
'计算机': [ 85, 92, 90, 85],
'计算机基础': [ 85, 92, 90, 85]}
df = pd.DataFrame(scores)
print(df.corr(method = 'pearson'))
缺失值处理
常用的缺失值检测采用统计分析方法,逐个检查数据的每个特征的缺失情况。 Pandas对象中isnull()函数提供了非常方便的缺失值检查功能,它将为返回一个布尔型矩阵,每个布尔值(True或False)指示数据是否缺失 具体使用中,我们可以使用isnull().sum()返回Pandas对象的每一列的缺失值计数。
1).直接删除
2)替换或插补缺失值
数据缺失是较为常见的数据质量问题,通常指数据记录中某些特征的缺失。造成数据缺失的原因很多,可能包括:字段在存储时的遗漏或丢失(例如,数据错误删除、采集设备故障、人为因素等)、数据获取成本较高等。
缺失值的存在会对数据挖掘模型的准确性、可靠性产生较大的影响。因此,在数据预处理阶段,需要检查缺失数据的情况,并给予针对性的处理。
(1)缺失值检测
方法:isnull() 或 isna() → 返回布尔矩阵。
统计:isnull().sum() → 返回每一列缺失值的数量。
import pandas as pd
import numpy as np
# 构造带缺失值的数据
data = {
'ID': [1, 2, 3, 4],
'Name': ['张三', '李四', None, '王五'],
'Age': [20, np.nan, 30, 40],
'Salary': [3000, 4000, None, 5000]
}
df = pd.DataFrame(data)
print("原始数据:\n", df)
# 检测缺失值
print("\n缺失值布尔矩阵:\n", df.isnull())
print("\n每列缺失值数量:\n", df.isnull().sum())
(2)缺失值处理方法
- 直接删除
适用场景:
缺失比例很小(比如 <5%)。
缺失数据没有明显规律,不会造成信息偏差。
# 删除含有缺失值的行
df_drop_row = df.dropna()
# 删除含有缺失值的列
df_drop_col = df.dropna(axis=1)
- 替换 / 插补缺失值
(a) 固定值填充
用 0、未知、均值、中位数、众数等填充。
# 用固定值填充
df_fill = df.fillna({'Name': '未知', 'Age': df['Age'].mean(), 'Salary': 0})
(b) 统计量填充
连续变量:均值 / 中位数 / 众数。
分类变量:众数。
# 用平均值填充 Age
df['Age'].fillna(df['Age'].mean(), inplace=True)
# 用众数填充 Name
df['Name'].fillna(df['Name'].mode()[0], inplace=True)
© 前向填充 / 后向填充(适合时间序列)
# 前向填充
df_ffill = df.fillna(method='ffill')
# 后向填充
df_bfill = df.fillna(method='bfill')
(d) 高级插补方法
回归模型:用其他特征预测缺失值。
KNN 插补:利用相似样本的值填充。
多重插补(MICE):基于统计建模方法。
import pandas as pd
import numpy as np
scores = {'姓名': ['张三', '李四', '王五', '刘一'],
'语文': [ 84, 92, 87, 84],
'数学': [ 89, np.NaN, 95, 89],
'英语': [ 90, 81, np.NaN, 92],
'计算机': [ 85, 92, 90, 85]}
df = pd.DataFrame(scores)
print('成绩数据对象的特征缺失值情况:')
print(df.isnull().sum()) #判断每列是否有缺失值
import pandas as pd
scores = {'姓名': ['张三', '李四', '王五', '刘一'],
'语文': [ 84, 92, 87, 84],
'数学': [ 89, pd.NA, 95, 89],
'英语': [ 90, 81, pd.NA, 92],
'计算机': [ 85, 92, 90, 85]}
df = pd.DataFrame(scores)
df.dropna(axis = 0, how = 'any', inplace = True) # 删除所有包含缺失值的行
print('删除包含缺失值记录后的数据为:\n', df)
import pandas as pd
import numpy as np
#生成包含缺失值的数据
scores = {'姓名': ['张三', '李四', '王五', '刘一'],
'语文': [ 84, 92, 87, 84],
'数学': [ 89, pd.NA, 95, 89],
'英语': [ 90, 81, pd.NA, 92],
'计算机': [ 85, 92, 90, 85]}
df = pd.DataFrame(scores)
# 1.均值替换
df_mean = df['数学'].fillna(value = df['数学'].mean(), inplace = False)
print('使用均值替换: \n', df_mean)
# 2.中位数替换
df_median = df['数学'].fillna(df['数学'].median(), inplace = False)
print('使用中位数替换: \n', df_median)
# 3.使用固定值0替换
df_zero = df['数学'].fillna(value = 0, inplace = False)
print('使用0替换: \n',df_zero)
# 4.使用缺失值前一个值进行填充(按照相应index前后填充)
df_ffill = df['数学'].fillna(method = 'ffill', inplace = False, axis = 0)
print('使用缺失值前一个值替换: \n', df_ffill)
# 5.使用缺失值后一个值进行填充(按照相应columns前后填充)
df_bfill = df['数学'].fillna(method = 'bfill', inplace = False, axis = 0)
print('使用缺失值后一个值替换: \n', df_bfill)
# 6.使用线性插值法进行填充
df['数学'] = pd.to_numeric(df['数学'], errors = 'coerce')
df_linear = df['数学'].interpolate(method = 'linear', inplace = False)
print('使用线性插值法进行填充: \n', df_linear)
# 7.使用多项式插值插值法进行填充
df_poly = df['数学'].interpolate(method = 'polynomial', order = 2, inplace = False)
print('使用多项式插值插值法进行填充: \n', df_poly)
# 8.使用样条插值法进行填充
df_spline = df['数学'].interpolate(method = 'spline', order = 2, inplace = False)
print('(6)使用样条插值法进行填充: \n', df_spline)
异常值检测和处理
在数据挖掘中,异常值是指不符合预期模式,且观测值明显偏离其它观测值的数据。异常值也称为离群点(Outlier),产生的原因很多,可能包括但不限于录入错误、测量错误、数据生成过程中的错误等。异常值的存在将显著影响构建的数据挖掘模型的准确性和可靠性。因此,需要对异常值进行检测和处理。通常,异常值的检测方法包括:3σ原则、箱线图分析、聚类分析。
- 3σ原则(标准差法)
基于正态分布假设。
若数据服从正态分布,则约 99.7% 的数据落在均值 ±3σ 范围内。
超过该范围的点一般可认为是异常值。
优点:简单直观。
缺点:依赖数据分布假设,非正态分布时可能不准确。
- 箱线图分析(IQR法)
使用四分位数(Q1, Q3)和四分位间距(IQR = Q3 - Q1)。
异常值判断标准:
下限:小于 Q1 - 1.5 × IQR
上限:大于 Q3 + 1.5 × IQR
优点:对数据分布没有强假设,适用于非正态分布。
缺点:在小样本或高度偏斜分布时可能敏感。
- 聚类分析
利用聚类算法(如 k-means、DBSCAN)将数据分组。
距离簇中心很远的点、或被划为噪声点的样本可视为异常值。
优点:适合复杂数据结构,可发现局部异常。
缺点:计算开销大,参数选择(如簇数 k、半径 ε)影响结果。
处理异常值的常见方法:
删除异常值(适用于极少量异常,且不是关键数据);
替换/修正(例如用均值、中位数、插值代替);
分箱处理(将异常值截断到某一合理范围内);
建模时使用鲁棒算法(如基于中位数或绝对偏差的算法)。
- 3σ原则(拉伊达准则,或称“3-sigma rule”)。
在正态分布下,数据的概率分布情况如下表所示:
| 区间范围 | 含义 | 覆盖概率 |
|---|---|---|
| μ ± 1σ | 大约68.27%的数据 | 约68% |
| μ ± 2σ | 大约95.45%的数据 | 约95% |
| μ ± 3σ | 大约99.73%的数据 | 约99.7% |
因此,超出 μ ± 3σ 范围的观测值只占 不到0.3%,在统计学和数据挖掘中通常被判定为异常值。
2. 箱线图
Boxplot, 盒须图)不仅能直观展现数据的分布,还能很好地辅助异常值检测。
在 箱线图 中:
Q1(下四分位数):25% 的数据点小于它。
Q2(中位数):50% 的数据点小于它。
Q3(上四分位数):75% 的数据点小于它。
IQR(四分位距):即 Q3 − Q1,用来衡量数据的离散程度。
异常值检测规则(常用 Tukey 法则):
下限:Q1 − 1.5 × IQR
上限:Q3 + 1.5 × IQR
低于下限或高于上限的点 → 判定为异常值(离群点),通常在箱线图中用小圆点或星号标记。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#生成原始数据
scores = {'姓名': ['S1', 'S2', 'S3', 'S4', 'S5', 'S6'], '英语': [90, 81, 110, 92, 83, 85]}
df = pd.DataFrame(scores)
# 绘制箱线图
plt.figure(figsize = (8, 6), dpi = 200)
axes = plt.boxplot(df['英语'], notch = True, patch_artist = True) #箱线图
outlier = axes['fliers'][0].get_ydata() #获取异常值
plt.show() #图形展示
print('异常值为:\n', outlier)
异常值判断的主要原理和过程为:
首先需要计算IQR(四分位数极差,inter-quartile range),即上四分位数(Q3)与下四分位数(Q1)之间的差值(IQR = Q3-Q1),可以看做是箱子的长度;
在此基础上,计算箱子的最小观测值min为:Q1 - 1.5IQR,最大观测值max为:Q3 +1.5IQR。箱体外部的两边缘线(胡须,whisker)分别表示最大观测值和最小观测值。
因此,小于最小观测值(Q1 - 1.5IQR)和大于最大观测值(Q3 +1.5IQR)的数值则被认定为异常值,在箱线图中表示为上下边缘线外的孤立点。
聚类
异常值通常数量较少,且正常数据有明显的差异。从聚类的角度,异常值在数据空间中孤立存在,不构成特定的簇,所在区域的密度很低。因此,一些基于密度的聚类算法能根据数据点所在位置的密度识别出异常数据,例如DBSCAN聚类算法,以上统计描述方法和可视化方法辅助检测异常值或离群点。那么如果检测到数据集出现异常值或离群点,常见的处理方法包括:
直接剔除掉异常值,主要用于异常值数量较少的时候。
借鉴缺失值的处理方法进行替换或插值。
不处理,在某些情况下,异常值可能代表特殊的含义,如何处理需要咨询业务人员或深入了解数据收集过程。
数据变换
数据变换是对数据进行规范化处理,转换为“适当”的形式以适应数据挖掘算法的需要。常见的数据变换操作包括:数据规范化、数值特征的二值化和离散化、标称特征的数值化处理等。
数据规范化
数据规范化是指为了消除不同数据(特征)在度量单位上的差异以及取值范围的影响,将数据按照一定比例进行标准化,使其落入到同一水平的取值范围内。常用规范化方法包括:“最小-最大(min-max)”规范化,“零-均值(Z-score)”规范化和小数定标规范化。
最小-最大规范化(Min-Max Normalization),又叫 离差标准化。它的公式如下:

from sklearn.datasets import load_iris
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
iris = load_iris().data
#使用离差标准化对数据进行预处理
m_scaler = MinMaxScaler() #创建一个min-max规范化对象
iris_scale = m_scaler.fit_transform(iris)
iris_scale= pd.DataFrame(data = iris_scale,
columns = ["petal_len", "petal_wid", "sepal_len", "sepal_wid"])
print("规范化后的前5条iris数据:\n", iris_scale[0:5] )
零-均值(Z-score)
零-均值”规范化,也称为标准差规范化或Z-分数规范化(Z-score Scaling),是按照均值中心化,然后利用标准差重新缩放数据,使数据服从均值为0,方差为1的正态分布,计算方式如下:
-均值规范化(Zero-mean Normalization),也叫
标准差规范化(Standardization)
Z-分数规范化(Z-score Scaling)


转换后数据的均值 = 0,标准差 = 1。
适用于不同量纲、不同量级的特征。
对 异常值敏感,因为均值和标准差会受极端值影响。
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
import pandas as pd
iris = load_iris().data
#使用标准差规范化对数据进行处理
iris_scale = StandardScaler() #创建一个标准差规范化对象
iris_scale = iris_scale.fit_transform(iris)
iris_scale= pd.DataFrame(data = iris_scale,
columns = ["petal_len", "petal_wid", "sepal_len", "sepal_wid"])
print("规范化后的前5条iris数据:\n", iris_scale[0:5] )
import numpy as np
x = np.array([[ 0., -3., 1.], # 初始化数据
[ 3., 1., 2.],
[ 0., 1., -1.]])
j = np.ceil(np.log10(np.max(abs(x)))) # 获取小数点移动最大位数
sc_C = x/(10**j)
print('标准化后的数据为: \n', sc_C)
数值特征的二值化和离散化
数值特征的二值化(Binarization)
定义:将连续型数值特征根据某个阈值(threshold)转化为 0 或 1 的二值特征。
公式:
应用场景:
文本挖掘中,词频是否出现(出现=1,不出现=0)。
医学数据中,某项指标是否超过临界值。
特征工程中,将连续数值转化为布尔变量,便于模型处理。
数值特征的离散化(Discretization)
定义:把连续型数值特征转换为有限个离散的区间或类别。
常见方法:
等宽离散化(Equal-width binning)
将取值范围均分为若干等宽区间。
简单直观,但可能导致部分区间过稀或过密。
等频离散化(Equal-frequency binning)
按数据分布,把样本分到数量大致相等的区间。
更平衡,但区间宽度不一致。
聚类离散化(Clustering-based)
使用 K-Means 等方法,把相似的数据聚在一起作为一个类别。
基于业务规则的离散化
例如:年龄段划分为「青年」「中年」「老年」。
应用场景:
便于模型解释(例如决策树)。
将非线性关系转化为类别特征。
在某些统计分析中,离散化后的特征更容易处理。
from sklearn.preprocessing import Binarizer
import numpy as np
price= np.array([1000, 2530, 3500, 6000, 200, 8200])
b = Binarizer(threshold = 3000) #创建二值化对象,阙值为3000
b_price = b.fit_transform(price.reshape(1,-1))
print("二值化后的价格:\n", b_price)
import pandas as pd
# 原始年龄数据
data = {'年龄': [15, 22, 28, 35, 42, 50, 61, 70, 80]}
df = pd.DataFrame(data)
# 定义年龄分段(bins)和对应的标签(labels)
bins = [0, 30, 60, 120] # 区间:[0,30), [30,60), [60,120)
labels = ['青年', '中年', '老年'] # 对应的业务规则分类
# 利用 pd.cut 进行离散化
df['年龄段'] = pd.cut(df['年龄'], bins=bins, labels=labels, right=False)
print(df)
等宽法(Fixed-Width)
该方法将连续属性的特征从最小值到最大值划分成具有相同宽度的n个区间,即n个等距区间。其中区间总个数n需要自行设定。等宽法离散化可以通过pandas模块的cut()函数实现,其基本语法为
pandas.cut() 的基本语法
python
pandas.cut(x, bins, right=True, labels=None)
x:要离散化的序列(如 DataFrame 的某一列)。
bins:分箱数(如 4),或自定义的分割点(如 [0,10,20,30])。
right:是否包含右边界,默认 True。
labels:是否用标签替换区间,默认返回区间对象。
import pandas as pd
# 原始数据
data = {'成绩': [55, 62, 70, 75, 80, 85, 90, 95]}
df = pd.DataFrame(data)
# 等宽离散化为 4 个区间
df['等宽区间'] = pd.cut(df['成绩'], bins=4)
# 如果希望加标签
labels = ['较差', '中等', '良好', '优秀']
df['成绩等级'] = pd.cut(df['成绩'], bins=4, labels=labels)
print(df)
等频法(Fixed-Frequency)
该方法主要根据数据频率进行区间划分,即将数量基本相同的记录放入区间,划分成为等频区间,以保证每个区间的频率基本一致。与等宽法类似,等频法离散化可以通过pandas库中的qcut()函数实现,其基本语法为:
pandas.qcut() 的基本语法
python
复制
编辑
pandas.qcut(x, q, labels=None, duplicates='raise')
x:要离散化的序列。
q:分箱数量(如 4 表示四分位数),或者自定义分位点(如 [0,0.25,0.5,0.75,1.0])。
labels:是否使用标签代替区间。
duplicates:如果分箱边界有重复,是否报错(可设置为 'drop')
import pandas as pd
# 原始数据
data = {'成绩': [55, 62, 70, 75, 80, 85, 90, 95]}
df = pd.DataFrame(data)
# 等频离散化为 4 个区间
df['等频区间'] = pd.qcut(df['成绩'], q=4)
# 添加标签
labels = ['低分', '中下', '中上', '高分']
df['成绩等级'] = pd.qcut(df['成绩'], q=4, labels=labels)
print(df)
import pandas as pd
#生成销量数据
sale_df = pd.DataFrame({'sale': [400, 50, 100, 450, 500, 320, 160, 280,
320, 380, 200, 460]})
# 等宽离散化
sale_df['sale_fixedwid'] = pd.cut(sale_df["sale"], bins = 3)
print(sale_df)
# 等频离散化
sale_df['sale_fixedfreq'] = pd.qcut(sale_df["sale"], q = 4)
print(sale_df)
标称特征的数值化处理
(1) 独热编码
它将标称特征类型编码为二元数值特征,如0或1。
例如,一个名称为“天气”的标称特征可能包含的取值为“晴天”、“阴天”和“雨天”。通过数据变换处理,结果生成三个二元特征,即判断“天气=晴天”、“天气=阴天”和“天气=雨天”。这三个二元特征的可能值为1(True/真)或0(False/假)
import pandas as pd
df = pd.DataFrame({
'天气': ['晴天', '阴天', '雨天', '晴天', '阴天']
})
print(df)
from sklearn.preprocessing import OneHotEncoder
# 定义编码器
encoder = OneHotEncoder(sparse_output=False)# 新的PD用sparse_output=False
# 执行编码
encoded = encoder.fit_transform(df[['天气']])
# 转换为 DataFrame,添加列名
encoded_df = pd.DataFrame(encoded, columns=encoder.get_feature_names_out(['天气']))
# 合并到原始数据
result = pd.concat([df, encoded_df], axis=1)
print(result)
sparse=False:返回的是数组而不是稀疏矩阵,方便展示。
get_feature_names_out():获取新生成的特征名。
参数内容
OneHotEncoder(
categories='auto',
drop=None,
sparse=True,
handle_unknown='error'
)
| 参数 | 默认值 | 含义 | 取值说明 | 详细描述 |
|---|---|---|---|---|
| categories | 'auto' |
确定每个特征的类别或类别推断方式 | 'auto' / 手动定义的类别数组(如[["晴天", "阴天", "雨天"]]] |
- 'auto':自动根据数据推断类别。- 手动指定:可以传入数组,明确类别,适合已知类别或需要控制类别顺序的情况。 |
| drop | None |
是否删除某些类别,避免虚拟变量陷阱(多重共线性) | None / 'first' / 手动类别(如"阴天") |
- None:保留所有类别,生成全部哑变量。- 'first':删除第一个类别,常用以避免虚拟变量陷阱(生成n-1个哑变量)。- 其它类别:专门删除某个类别。 |
| sparse | True |
返回的编码数据格式 | True / False |
- True:返回稀疏矩阵(节省内存,适合大规模数据)。- False:返回密集数组,便于观察或小数据集。 |
| handle_unknown | 'error' |
遇到未见类别时的处理方式 | 'error' / 'ignore' |
- 'error':遇到未知类别会报错,确保数据一致性。- 'ignore':忽略未知类别,编码为0,避免中断处理,适用于新数据可能出现未知类别的场景。 |
这种方法又称为哑变量编码。
import pandas as pd
# 原始数据
df = pd.DataFrame({
'天气': ['晴天', '阴天', '雨天', '晴天', '阴天']
})
# 使用 get_dummies() 进行独热编码
encoded_df = pd.get_dummies(df, columns=['天气'])
print(encoded_df)
get_dummies(data, prefix = None, prefix_sep = '_', dummy_na = False, columns = None, drop_first = False, dtype = None)
| 参数名称 | 默认值 | 作用说明 | 取值范围/说明 | 详细描述 |
|---|---|---|---|---|
| data | — | 输入的数据集(DataFrame、数组等) | — | 调用get_dummies的必填参数,指定需要转化的原始数据或部分列。 |
| prefix | None |
生成的哑变量的前缀 | 字符串或字典、序列 | 设置哑变量的列名前缀,默认使用原列名。可自定义前缀。 |
| prefix_sep | '_' |
前缀与类别名连接的分隔符 | 字符串 | 用于连接前缀与类别名,默认为下划线“_”。 |
| dummy_na | False |
是否为缺失值(NaN)创建哑变量 | True / False |
若为True,NaN值会被编码为单独的类别。 |
| columns | None |
指定需要转换的列(只转换部分列) | 列名或列索引的序列 | 如果为None,转换所有类别型列,否则只转换指定列。 |
| drop_first | False |
是否删除第一个类别(避免虚拟变量陷阱) | True / False |
设置为True可以减少多重共线性(生成n-1个哑变量)。 |
| dtype | None |
返回的哑变量的数据类型 | 数据类型(如np.uint8,np.int8等) |
指定返回列的數值类型,默认为int类型。 |
标签编码
独热编码和哑变量编码的方法可以简单、有效地将标称特征进行数值编码,但是也可能会带来维度“爆炸”和特征稀疏等问题。标签编码可以在避免此类问题的同时简单、有效地将标称变量转换为连续的数值型变量,即对离散的类别进行编号。例如,对应上述“天气”标称特征的 3 个取值: [‘晴天’, ‘阴天’, ‘雨天’],分别让’晴天’=0、 ‘阴天’=1、 ‘雨天’=2,以实现数值编码。
| 编码方法 | 简要描述 | 主要优点 | 主要缺点 | 适用场景 |
|---|---|---|---|---|
| 哑变量/独热编码 | 将每个类别转化为互不相交的二元变量(虚拟变量) | 能保留类别的原始信息,避免算法误解类别间的大小关系 | 维度“爆炸”,特征稀疏,可能引入多重共线性 | 线性回归、逻辑回归等需要数值输入的模型,类别数较少时效果较好 |
| 标签编码 | 用整数对类别进行编号(如’晴天’=0, ‘阴天’=1, ‘雨天’=2) | 简单、有效,避免特征空间维度过高,节省存储空间 | 滑坡:引入类别间的大小关系,可能误导模型(只适合类别无序或可转化为顺序) | 树模型(随机森林、梯度提升树)等可以处理类别顺序的模型 |
标签编码的实现——sklearn.preprocessing.LabelEncoder
作用:将无序类别变量转换为对应的连续整数编码。
实现方式:
from sklearn.preprocessing import LabelEncoder
# 创建编码器对象
le = LabelEncoder()
# 拟合并转换:示例天气特征
weather = ['晴天', '阴天', '雨天', '晴天', '阴天']
weather_encoded = le.fit_transform(weather)
print(weather_encoded)
# 输出可能为:[0, 1, 2, 0, 1]
import pandas as pd
from sklearn.preprocessing import OneHotEncoder,LabelEncoder
#原始数据
weather_df = pd.DataFrame({'天气': ['晴天', '雨天', '阴天', '晴天'], '销量': [400, 50, 100, 450]})
#独热编码
oneHot_weather = OneHotEncoder().fit_transform(weather_df[["天气"]])
print('独热编码的结果为:')
print(oneHot_weather)
#哑变量编码
dummy_weather = pd.get_dummies(weather_df[["天气"]], drop_first = False)
print('哑变量的结果为:')
print(dummy_weather)
#标签编码
label_weather = LabelEncoder().fit_transform(weather_df[["天气"]])
print('标签编码的结果为:')
print(label_weather)
数据规约
尽管目前的硬件技术和存储技术已经能够较为有效地处理大规模数据,但是当数据的规模过大时,数据挖掘的性能和效率会受到巨大的挑战。另一方面,构建一个有效的数据挖掘模型通常也不需要全部的数据集或者特征。因此,我们常需要使用数据规约技术在建模之前降低数据的规模。
数据规约(Data Reduction)是指在尽量保持数据原始分布及特点的基础上,降低数据规模的方法,主要包括:样本规约,维度规约和数据压缩等。
样本规约
1) 简单随机抽样
简单随机抽样(Simple Random Sampling)是指从数据总体中随机抽样出一定比例或数量的样本子集。Pandas模块中的sample()函数可以实现简单随机抽样,其基本语法为:
sample(n, frac, replace, axis, random_state)
| 参数名称 | 默认值 | 作用说明 | 取值范围/说明 | 示例说明 |
|---|---|---|---|---|
| n | 无(必须提供) | 要抽样的样本数 | 正整数 | sample(n=3) 表示随机抽取3个样本。 |
| frac | 无(必须提供) | 要抽样的比例(占全部样本的比例) | 0到1之间的小数 | sample(frac=0.2) 表示抽取全部样本的20%。 |
| replace | False |
是否允许有放回抽样 | True / False |
True:可以重复抽取同一行数据。False:不允许重复。 |
| axis | 0 |
按行还是列进行抽样 | 0(行) / 1(列) |
通常按行抽样,列抽样罕见。 |
| random_state | None |
设置随机数种子,保证结果可复现 | 整数或None |
random_state=42,每次抽样结果一致,便于调试。 |
import pandas as pd
df = pd.DataFrame({
'A': ['A1', 'A2', 'A3', 'A4', 'A5'],
'B': [10, 20, 30, 40, 50]
})
# 抽样示例
sampled_df = df.sample(n=2, replace=False, random_state=1)
print(sampled_df)
| 操作参数 | 取值 | 说明 | 作用 |
|---|---|---|---|
| n | 2 | 采样2个样本 | 随机抽取2条数据,组成子集。 |
| replace | False |
不放回抽样 | 保证样本不重复。 |
| random_state | 1 / 42 / None |
设置随机种子,确保每次结果一致 | 调试和复现实验的关键参数。 |
n和frac二者任选其一,不能同时设置,代表抽样数量或比例。
replace=True适合重采样场景,比如bootstrap采样。
设置random_state可以获得可复现的随机结果。
2) 分层抽样
分层抽样也称为“按类别”抽样,即数据先分成不同类别(或层),再针对每个类别(或层)分别进行随机抽样的方法。显然这种方法保证了抽样结果中每一类样本的比例不发生明显改变,能保留数据的原始条件分布,对于类别不平衡情况下的数据抽样更加有效。
Scikit-learn的model_selection 模块中提供StratifiedShuffleSplit类可以实现分层抽样。
StratifiedShuffleSplit(n_splits, test_size, train_size, random_state)
| 参数名称 | 默认值 | 作用说明 | 取值范围/说明 | 示例说明 |
|---|---|---|---|---|
| n_splits | 10 | 迭代切割的次数(训练集/测试集划分次数) | 正整数 | n_splits=5,意味着会生成5组不同的划分。 |
| test_size | 0.1 | 测试集占数据的比例,或具体样本数 | 浮点(比例)或整数(样本数) | test_size=0.2,测试集占20%。 |
| train_size | None |
训练集占数据的比例或样本数(若未设置,自动为剩余部分) | 浮点(比例)或整数(样本数) | 设置train_size=0.8表示训练集占80%。 |
| random_state | None |
随机数种子,确保每次划分复现 | 整数或None |
random_state=42,保证划分结果每次一致。 |
import numpy as np
from sklearn.model_selection import StratifiedShuffleSplit
X = np.arange(20).reshape((10, 2))
y = np.array([0, 0, 1, 1, 0, 0, 1, 1, 0, 1]) # 类别标签一致,支持分层抽样
sss = StratifiedShuffleSplit(n_splits=3, test_size=0.3, random_state=42)
for train_index, test_index in sss.split(X, y):
print("TRAIN:", train_index, "TEST:", test_index)
print("TRAIN y:", y[train_index])
print("TEST y:", y[test_index])
print("-" * 30)
保持类别比例(stratified):StratifiedShuffleSplit在每一折中保持每个类别的比例,与原始数据一致,用于分类任务。
n_splits:设置划分次数,也就是会生成多少组不同的训练/测试集。
test_size 和 train_size:控制划分比例,避免样本偏差。
random_state:确保每次划分结果可复现。
案例 每个类别的数量分别是20、7和3。
import pandas as pd
# 构建示例数据,包含‘天气’和‘销售额’
sale_df = pd.DataFrame({
'weather': ['晴天', '雨天', '阴天', '晴天', '晴天', '晴天', '晴天', '晴天', '晴天', '晴天',
'晴天', '阴天', '雨天', '阴天', '晴天', '阴天', '雨天', '阴天', '晴天', '晴天',
'晴天', '晴天', '阴天', '晴天', '晴天', '晴天', '阴天', '晴天', '晴天', '晴天'],
'sale': [400, 50, 100, 450, 620, 325, 170, 280, 710, 330,
500, 320, 160, 280, 175, 240, 605, 270, 250, 510,
320, 380, 200, 460, 380, 420, 560, 80, 240, 630]
})
# 从数据集中随机抽取10个样本,不放回
random_sample = sale_df.sample(10, random_state=124)
print('简单随机抽样方法的结果:\n', random_sample)
sample():Pandas中随机采样方法。
n=10:抽取10个样本。
random_state=124:设置随机数种子,保证每次运行的抽样结果保持一致。
特点:
不考虑类别分布,随机抽取。
适用于样本量大、类别分布平衡的情况。
from sklearn.model_selection import StratifiedShuffleSplit
# 创建分层抽样对象
split = StratifiedShuffleSplit(n_splits=1, train_size=10, random_state=124)
# 执行分层抽样
for train_index, test_index in split.split(sale_df, sale_df['weather']):
strat_sample_set = sale_df.loc[train_index]
strat_test_set = sale_df.loc[test_index]
print('分层抽样方法的结果:\n', strat_sample_set)
StratifiedShuffleSplit:确保每个类别在抽样集合中比例保持一致(即“类别比例平衡”)。
参数:
n_splits=1:只做一次划分。
train_size=10:训练集包含10个样本(总样本量根据需要调整)。
random_state=124:随机状态,保证结果复现。
3) 聚类抽样
定义:将总体划分成若干个互不重叠的簇(通常是地理区域、学校、公司等自然形成的组),再从中随机抽取若干簇,抽样的整体就是选中的簇内所有样本。
特点:
不需要知道每个样本的类别或标签。
适用于地区或组的自然划分数据,操作简单。
但可能存在簇与簇之间差异较大,导致样本代表性不足。
【案例示范】
假设我们有一份员工公司数据,包括每个员工的“所在部门”和“工资”。我们随机抽取若干个部门,采集所有该部门的员工信息。
import pandas as pd
import numpy as np
# 构造示例数据
data = {
'department': ['销售', '销售', '财务', '财务', 'IT', 'IT', '市场', '市场', 'HR', 'HR'],
'employee_id': range(1, 11),
'salary': [5000, 5200, 4500, 4700, 6000, 6100, 4800, 4900, 4300, 4400]
}
df = pd.DataFrame(data)
# 1. 按“部门”划分簇,即每个部门为一个簇
# 2. 使用随机抽样从簇中选择若干部门(比如抽取2个部门)
sampled_departments = df['department'].drop_duplicates().sample(n=2, random_state=42)
# 3. 获取所抽中的部门的全部员工
cluster_sample = df[df['department'].isin(sampled_departments)]
print(cluster_sample)
操作流程:
先从所有部门(簇)中随机抽取若干簇,比如抽取“财务”和“市场”部门。
获取这两个部门内所有员工作为样本。
优点:
不需要事先知道样本具体的个人类别信息。
节省调查成本,例如只需到少数几个部门进行采样。
注意点:
如果簇内信息差异大,可能影响代表性。
维度规约
维度规约,又称为数据降维,目的是通过线性或非线性变换的方法,将数据投影到低维空间中,从而减少数据特征的个数,主要方法分为线性方法和非线性方法。其中线性方法主要包括:
主成分分析( Principal Component Analysis, PCA)
奇异值分解( Singular Value Decomposition, SVD
线性判别分析(Linear Discriminant Analysis, LDA)等,
非线性方法包括:核主成分分析(Kernel PCA)、多维缩放(Multidimensional Scaling, MDS)等
from sklearn.decomposition import PCA
import zlib
X_multi = np.random.rand(10, 4)
pca = PCA(n_components=2)
reduced_X = pca.fit_transform(X_multi)
print("降到2维的结果:\n", reduced_X)
数据压缩
数据压缩(Data Compression)是指使用相应的压缩技术或编码,以获得原始数据的简化或压缩表示。如果可以从压缩后的数据中重建原始数据而不会丢失任何信息,则称为无损数据压缩,例如熵编码等。然而,如果我们只是近似重构原数据,则称为有损数据压缩,例如小波压缩等。维度规约和样本规约也可以视为特殊形式的数据压缩。常见的例子是 ZIP 数据格式,此格式不仅提供压缩功能,还可作为归档工具(Archiver),能够将许多文件存储到同一个文件中,完成数据压缩。
from sklearn.decomposition import PCA
import zlib
original_str = "这是示例文本,用于无损压缩"
compressed = zlib.compress(original_str.encode('utf-8'))
decompressed = zlib.decompress(compressed).decode('utf-8')
print("原始字符串:", original_str)
print("压缩后字节数:", len(compressed))
print("还原字符串:", decompressed)
练习题
题目1:
用pandas的sample()方法,从一个DataFrame随机抽取2行,固定随机种子为42。
# 从DataFrame中随机抽取2行
sampled_df = pd.DataFrame(***).sample(n=2, random_state=42)
题目2:
使用StratifiedShuffleSplit,将一份带类别标签的数据按比例80%训练、20%测试划分,且保证每个类别比例不变。
# 定义分层划分器
split = ***(n_splits=1, train_size=0.8, random_state=42)
# 按类别划分,获取训练集索引
for train_idx, _ in split.split(X, y):
train_df = pd.DataFrame(***)
题目3:
假设有一个员工部门数据表,用pandas随机抽取两个部门,然后获取这两个部门内的所有员工信息。
# 先随机抽取两个部门
sampled_depts = ***.drop_duplicates().sample(n=2, random_state=1)
# 获取这些部门的员工
cluster_sample = df[df['department'].isin(***)]
题目4:
用sklearn自带的PCA,对一个随机生成的10行4列数据进行降维,将数据降到2维,输出结果。
# 生成随机数据
X = np.random.rand(10, 4)
# 降到2维
reduced_X = ***.fit_transform(***)
# 输出
print(***)
题目5:
用Python的zlib库,把一句话“简单压缩示例”做压缩后再解压,确保恢复出原文。
import zlib
original_str = "简单压缩示例"
# 压缩
compressed = zlib.compress(***)
# 解压还原
restored = zlib.decompress(***).decode('utf-8')
# 打印
print(***)
import pandas as pd
import numpy as np
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.decomposition import PCA
import zlib
# 题目1:抽样
df1 = pd.DataFrame({'A': ['A1', 'A2', 'A3', 'A4', 'A5'], 'B': range(5)})
sampled_df = pd.DataFrame(***).sample(n=2, random_state=42)
# 替换:df1
# 结果
print("随机抽样(2行):\n", sampled_df)
# 题目2:分层划分
X = np.array([[1], [2], [3], [4], [5], [6]])
y = np.array(['类别1', '类别1', '类别2', '类别2', '类别1', '类别2'])
split = ***(n_splits=1, train_size=0.8, random_state=42)
# 替换:StratifiedShuffleSplit
for train_idx, _ in split.split(X, y):
train_df = pd.DataFrame(***)
# 替换:X[train_idx], y[train_idx]
print("分层划分训练集:\n", train_df)
# 题目3:按部门抽样
df2 = pd.DataFrame({
'department': ['销售', '销售', '财务', '财务', 'IT', 'IT', '市场', '市场', 'HR', 'HR'],
'员工': ['X', 'Y', 'Z', 'W', 'L', 'M', 'N', 'O', 'P', 'Q'],
'工资': [5000, 5200, 4800, 4600, 7000, 7200, 4300, 4400, 5100, 5300]
})
sampled_depts = ***.drop_duplicates().sample(n=2, random_state=1)
cluster_sample = df2[df2['department'].isin(***)]
# 替换:df2, sampled_depts
print("按部门抽样:\n", cluster_sample)
# 题目4:PCA降维
X = np.random.rand(10, 4)
pca = PCA(n_components=2)
reduced_X = ***.fit_transform(***)
# 替换:pca, X
print("降维结果:\n", ***)
# 题目5:zlib压缩
original_str = "简单压缩示例"
compressed = zlib.compress(***)
restored = zlib.decompress(***).decode('utf-8')
# 替换:original_str, compressed, compressed
print("原文:", original_str)
print("解压还原:", ***)
更多推荐


所有评论(0)