Data Cleaning

处理缺失值(Handling Missing Values)

1. 初始数据查看 (Initial Data Inspection)

pd.read_csv():读取CSV文件,加载数据集
data.head(): 查看前五行数据,初步发现缺失值(显示为NaNNone
加载时可能遇到混合数据类型警告(DtypeWarning),建议指定dtype或设置low_memory=False

1
pd.read_csv(file_path, low_memory=False)

2. 计算缺失值数量和占比 (Counting Missing Values)

首先计算
- data.isnull().sum(): 统计每列的缺失值数量。
- np.product(data.shape):计算总共的records
- 然后 missing/总 * 100 就可以计算到占比多少

3. 分析缺失原因 (Understanding Why Data is Missing)

  • 核心问题:
    • 区分缺失原因是未记录(Not Recorded)还是不存在(Does Not Exist)
    • 示例:
      • TimeSecs(比赛剩余秒数)缺失是因未记录,适合填充。
      • PenalizedTeam(受罚球队)缺失是因无罚球,可保留NaN或标记为”无”。

4. 删除缺失值 (Dropping Missing Values)

  • 方法:
    • 删除行: data.dropna() → 删除所有有missing value的records
    • 删除列: data.dropna(axis=1) → 但丢失大量信息。
    • 参数说明:
      • axis=0 (按行删除): 关注的是哪些行包含缺失值。
      • axis=1 (按列删除): 关注的是哪些列包含缺失值。
    • 结果检查:
      • nfl_data.shape[1]查看原始列数,columns_with_na_dropped.shape[1]查看删除后列数。

5. 填充缺失值 (Filling Missing Values)

  • 常用函数:
    • fillna(): 填充缺失值。
      • 填充固定值: data.fillna(0) → 所有NaN替换为0。
      • 向后填充(Backward Fill): fillna(method='bfill', axis=0).fillna(0) → 用后续值填充,剩余NaN填0。
    • 适用场景:
      • 时间序列数据(如TimeSecs)适合用前后值填充。

6. 关键注意事项 (Key Considerations)

  • 权衡删除与填充:
    • 删除列/行可能丢失有用信息,填充可能引入噪声。
    • 需结合业务逻辑(如PenalizedTeam列的处理)(例子)。
      • 在决定如何处理包含缺失值的 PenalizedTeam 这一列时,不能简单地说”缺失值太多就删除”或者”用某种固定的值填充”。而是需要思考:

        • PenalizedTeam 列的业务含义是什么? 它记录的是在某个比赛事件中受到处罚的队伍。
        • 为什么会出现缺失值? 缺失可能是因为在那个特定的比赛事件中没有发生任何处罚。如果真是这样,那么缺失值本身就代表了一种”没有处罚”的状态,这在业务上是有意义的。
        • 如果删除这一列会丢失什么信息? 如果我们删除了 PenalizedTeam 列,我们就无法分析哪些队伍更常受到处罚,或者处罚对比赛结果的影响等相关问题。
        • 如果填充缺失值会引入什么问题? 如果我们用一个虚拟的队伍名称或者”无”来填充缺失值,这可能会在后续的分析中产生误导,因为它会错误地将没有处罚的事件也归类到某个队伍或者”无”这个类别下。
      • 基于业务逻辑,更合理的处理方式是什么?

        • 不填充,保留缺失值: 如果缺失值代表”没有处罚”的实际业务含义,那么保留缺失值可能是最合适的做法。后续的分析需要考虑到这种缺失值的特殊含义。
        • 创建新的类别: 可以创建一个新的类别(例如”No Penalty”)来显式地表示没有处罚的情况,而不是使用 NaN。但这仍然需要基于对业务的理解来判断是否合理。
        • 与其他列结合分析: 可能会有其他列的信息能够帮助我们理解 PenalizedTeam 为空的情况。例如,是否存在一个”PenaltyType”列,当其为空时,PenalizedTeam 也为空?

数据缩放(Scaling)与归一化(Normalization)笔记


核心概念

  1. Scaling(缩放)

    • 改变数据范围(range)
    • 适用场景: 基于距离度量的算法(如SVM、KNN)
    • 示例: 不同量纲数据(如日元vs美元、身高vs体重)需统一量级
  2. Normalization(归一化)

    • 改变数据分布形状(shape of distribution)
    • 适用场景: 要求数据符合正态分布的算法(如LDA、高斯朴素贝叶斯)
    • 方法: Box-Cox Transformation(Box-Cox变换)


代码实现

1
2
3
4
5
6
7
8
9
10
  # 环境设置
import pandas as pd
import numpy as np
from scipy import stats # Box-Cox变换
from mlxtend.preprocessing import minmax_scaling # 最小-最大缩放
import seaborn as sns
import matplotlib.pyplot as plt

# 可重复性设置
np.random.seed(0)
  1. Min-Max Scaling示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 生成指数分布数据
    original_data = np.random.exponential(size=1000)

    # 应用最小-最大缩放(0-1范围)
    scaled_data = minmax_scaling(original_data, columns=[0])

    # 可视化对比
    fig, ax = plt.subplots(1, 2, figsize=(15,3))
    sns.histplot(original_data, ax=ax[0], kde=True)
    sns.histplot(scaled_data, ax=ax[1], kde=True)
    plt.show()
    • 效果:数据范围从[0,8]缩放到[0,1],分布形状不变
  2. Box-Cox归一化示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 生成偏态数据
    original_data = np.random.exponential(size=1000)

    # 应用Box-Cox变换(需数据>0)
    normalized_data = stats.boxcox(original_data)

    # 可视化对比
    fig, ax = plt.subplots(1, 2, figsize=(15,3))
    sns.histplot(original_data, ax=ax[0], kde=True)
    sns.histplot(normalized_data, ax=ax[1], kde=True)
    plt.show()
    • 效果:将指数分布转换为近似正态分布

minmax_scaling(最小-最大缩放)是一种数据标准化方法,用于将数据线性地变换到一个指定的范围(通常是 [0, 1] 或 [-1, 1])。它的核心作用是通过缩放消除原始数据的量纲和范围差异,使得不同特征具有可比性,同时保留原始数据的分布形状。


工作原理

假设有一组数据 minmax_scaling 的公式为:

  • 将数据压缩到 [0, 1] 区间(默认)。
  • 若想缩放到其他范围(如 [a, b]),公式可扩展为:

用途

  1. 消除量纲差异
    当数据特征的单位或范围差异很大时(例如:年龄 [0-100] 和收入 [0-1,000,000]),直接建模会导致数值大的特征主导模型。缩放后所有特征在同一量级上。

  2. 加速模型收敛
    许多机器学习算法(如神经网络、梯度下降、KNN、SVM)对特征的尺度敏感。缩放后的数据能更快收敛。

  3. 保护敏感算法
    例如,PCA(主成分分析)和 L2 正则化模型(如岭回归)会受特征尺度影响,缩放后结果更合理。

  4. 图像/信号处理
    将像素值或信号强度归一化到固定范围(如 [0, 1]),便于后续处理。


与标准化(Z-score)的区别

  • 最小-最大缩放

    • 范围固定(如 [0, 1])。
    • 对异常值敏感(极端值会压缩正常数据的范围)。
  • 标准化(Z-score)

    • 基于均值(μ)和标准差(σ),公式为
    • 范围无限制,更适合存在异常值的数据。

Python 示例

1
2
3
4
5
6
7
8
9
10
11
from sklearn.preprocessing import MinMaxScaler
import numpy as np

# 原始数据
data = np.array([[10, 2], [5, 3], [1, 8]])

# 缩放到 [0, 1]
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(data)

print(scaled_data)

输出:

1
2
3
[[1.   0.        ]
[0.444 0.166...]
[0. 1. ]]

注意

  • 异常值问题:如果数据存在极端值,minmax_scaling 会导致有效数据被压缩(例如,99% 的数据集中在 0.01-0.02 之间)。此时可用 RobustScaler(基于分位数缩放)或标准化(Z-score)。
  • 树模型(如随机森林)不需要缩放:因为这些模型不依赖特征尺度。

总结:minmax_scaling 是数据预处理的基础工具,适用于需要固定范围或量纲统一的场景,但在异常值较多时需谨慎使用。

关键差异

特征 Scaling Normalization
目的 统一量纲 改变分布形状
方法 线性变换(如Min-Max) 非线性变换(如Box-Cox)
适用算法 SVM, KNN LDA, 高斯模型
数据影响 保持原始分布 使分布趋近正态

应用场景

  • Scaling案例:比较不同货币(100日元≈1美元)或量纲差异大的特征
  • Normalization案例:使右偏/左偏数据更适合线性模型假设

注意

  • np.random.seed(0):确保随机过程可复现
  • Box-Cox要求输入数据必须>0
  • 可视化是验证效果的关键工具(如直方图+KDE曲线)

解析日期(Parsing Dates)

1. 环境设置

  • 导入必要库:pandas, numpy, seaborn, datetime
  • 读取数据:landslides = pd.read_csv("../input/landslide-events/catalog.csv")
  • 设置随机种子:np.random.seed(0)

2. 检查日期列的数据类型

  • 使用head()查看数据前几行
  • 打印日期列:data['date'].head()
  • 检查数据类型:data['date'].dtype返回object(对象)类型,表示Python未识别为日期

3. 转换日期列为datetime格式

  • 使用pd.to_datetime()函数解析日期:
    1
    data['date_parsed'] = pd.to_datetime(data['date'], format="%m/%d/%y")
  • strftime directives(格式化指令):
    • %m:月份
    • %d:日
    • %y:两位年份
    • %Y:四位年份

4. 处理多日期格式问题

  • 可选方法:让pandas自动推断格式
    1
    pd.to_datetime(landslides['Date'], infer_datetime_format=True)
  • 不推荐总是使用infer_datetime_format的原因:
    1. 可能无法正确推断复杂格式
    2. 比指定明确格式慢

5. 提取日期信息

  • 提取月份中的日:
    1
    day_of_month_landslides = landslides['date_parsed'].dt.day

6. 验证日期解析

  • 绘制日分布直方图验证解析正确性:
    1
    sns.distplot(day_of_month_landslides, kde=False, bins=31)
  • 预期:均匀分布(1-31日),31日较少(因为非所有月份都有31天)

关键概念

  • datetime64:pandas中的日期时间数据类型
  • dt accessor:用于访问datetime对象的属性和方法
  • strftime directives(格式化指令):定义日期格式的特殊代码
  • infer_datetime_format(推断日期格式):自动检测日期格式的参数

注意事项

  • 原始日期列是object类型,无法直接使用dt访问器
  • 解析日期后,可以进行各种日期相关操作和计算
  • 验证日期解析结果很重要,可避免月份和日期的混淆错误

字符编码(Character Encodings)总结

1. 基本概念

  • 字符编码(Character Encodings):将二进制字节(如 0110100001101001)映射为人类可读文本(如 “hi”)的规则集合。
  • 常见问题
    • 乱码(Mojibake):用错误编码解码文本时的混乱字符(如 æ–‡å—化ã??)。
    • 未知字符(Unknown Characters):无对应映射时显示的占位符(如 ����������)。
  • UTF-8:现代标准编码,Python 3 的默认编码,建议所有数据优先使用。

2. Python 中的字符串与字节串

  • 字符串(String):Python 3 默认以 UTF-8 存储文本。
    1
    2
    before = "This is the euro symbol: €"
    type(before) # 输出: str
  • 字节串(Bytes):通过 encode() 将字符串转换为字节序列。
    1
    2
    after = before.encode("utf-8", errors="replace")  # 用 'replace' 处理无法编码的字符
    type(after) # 输出: bytes
  • 解码(Decode):用正确编码将字节串转回字符串。
    1
    2
    print(after.decode("utf-8"))  # 正确输出原字符串
    print(after.decode("ascii")) # 报错: UnicodeDecodeError(编码不匹配)

3. 文件读取与编码问题

  • 常见错误:读取非 UTF-8 文件时可能触发 UnicodeDecodeError
    1
    2
    # 示例报错:'utf-8' codec can't decode byte 0x99...
    pd.read_csv("non_utf8_file.csv")
  • 解决方案
    • 使用 charset_normalizer 检测文件编码:
      1
      2
      3
      with open("file.csv", "rb") as rawdata:
      result = charset_normalizer.detect(rawdata.read(10000)) # 读取前10000字节猜测编码
      print(result) # 输出: {'encoding': 'Windows-1252', 'confidence': 0.73}
    • 指定正确编码读取文件:
      1
      df = pd.read_csv("file.csv", encoding="Windows-1252")

4. 保存文件为 UTF-8

  • 默认保存:Python 保存文件时默认使用 UTF-8。
    1
    df.to_csv("output_utf8.csv")  # 无额外参数即可保存为 UTF-8

5. 注意事项

  • 避免数据损坏:错误编码转换可能导致不可逆信息丢失(如用 errors="replace" 替换无法解析的字符)。
  • 混合数据类型警告:读取文件时可能触发 DtypeWarning,可通过指定 dtypelow_memory=False 解决。
    1
    pd.read_csv("file.csv", dtype={"column": str}, low_memory=False)

关键函数与工具

  • 函数
    • encode(encoding, errors):将字符串编码为字节串。
    • decode(encoding, errors):将字节串解码为字符串。
    • pd.read_csv(encoding=...):指定编码读取 CSV 文件。
    • to_csv():保存 DataFrame 为 CSV(默认 UTF-8)。
  • 模块
    • charset_normalizer:自动检测文件编码(替代 chardet)。

通过正确处理编码,可避免数据解析错误,确保文本处理的准确性。

总结:清理不一致文本数据输入的Jupyter Notebook

1. 环境设置 (Environment Setup)

  • 导入库 (Libraries Imported):
    • pandas & numpy:数据处理与分析。
    • fuzzywuzzy:模糊字符串匹配(用于纠正拼写不一致)。
    • charset_normalizer:检测文件编码。
  • 读取数据
    使用 pd.read_csv() 加载数据集 pakistan_intellectual_capital.csv
  • 设置随机种子np.random.seed(0) 确保结果可复现。

2. 数据预处理 (Data Preprocessing)

  • 查看数据professors.head() 显示前五行,重点关注 Country 列。
  • 统一格式
    • 小写转换professors['Country'].str.lower() 消除大小写差异。
    • 去除空格professors['Country'].str.strip() 清理前后空格。

3. 模糊匹配纠正数据 (Fuzzy Matching for Inconsistencies)

  • 问题示例Country 列中存在 south koreasouthkorea 等不一致条目。
  • 使用 fuzzywuzzy
    • 核心函数process.extract(string, list, scorer=fuzz.token_sort_ratio) 计算字符串相似度。
    • 相似度阈值:设定 min_ratio(如47)筛选需替换的条目。
  • 自定义函数 replace_matches_in_column
    • 功能:自动替换指定列中与目标字符串相似度高于阈值的条目。
    • 参数
      • df:目标DataFrame。
      • column:待清理的列名。
      • string_to_match:标准化的目标字符串(如 "south korea")。
      • min_ratio:最小匹配相似度(默认47)。

4. 应用示例 (Example Workflow)

  1. 识别相似项

    1
    matches = fuzzywuzzy.process.extract("south korea", countries, limit=10, scorer=fuzzywuzzy.fuzz.token_sort_ratio)

    输出结果包含相似字符串及其匹配分数(如 ('southkorea', 48))。

  2. 执行替换

    1
    replace_matches_in_column(df=professors, column='Country', string_to_match="south korea")

    southkorea 等条目统一为 south korea

  3. 验证结果
    professors['Country'].unique() 检查是否仅保留标准化值。


5. 后续任务 (Next Steps)

  • 继续清理其他列(如 Graduated fromDesignation)的不一致数据。
  • 扩展模糊匹配逻辑,处理更多边缘案例(如 usofausa)。

关键概念 (Key Terms)

  • 模糊匹配 (Fuzzy Matching):通过计算编辑距离(edit distance)量化字符串相似度,自动化纠正拼写错误或变体。
  • Token Sort Ratiofuzzywuzzy 的一种评分方法,忽略单词顺序和大小写的影响。

Inconsistent Data Entry

Fuzzy matching: The process of automatically finding text strings that are very similar to the target string. In general, a string is considered “closer” to another one the fewer characters you’d need to change if you were transforming one string into another. So “apple” and “snapple” are two changes away from each other (add “s” and “n”) while “in” and “on” and one change away (rplace “i” with “o”). You won’t always be able to rely on fuzzy matching 100%, but it will usually end up saving you at least a little time.

模糊匹配:自动查找与目标字符串非常相似的文本字符串的过程。一般来说,如果要将一个字符串转换成另一个字符串,需要更改的字符越少,就认为该字符串与另一个字符串越 “接近”。因此,“apple ”和 “snapple ”相差两个字符(添加 “s ”和 “n”),而 “in ”和 “on ”相差一个字符(用 “o ”替换 “i”)。你并不总是能百分之百地依赖模糊匹配,但它通常至少会为你节省一点时间。

保证数据的统一性

  • 去除空格
  • 大小写不一大等
  • 确保数据的限制是正确的
    • 如 Unique
    • Condition (range check 等等)
    • not null

跟Database 数据统一性差不多