尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

数据清洗与转换实战:从概念到代码的完整DC2流程指南

数据清洗与转换实战:从概念到代码的完整DC2流程指南 最近在技术社区看到不少刚入行的开发者朋友在讨论“第一次做DC2”的经历既有成功的喜悦也踩了不少坑。DC2作为一个常见的开发任务其核心是数据转换与清洗看似基础实则关系到后续数据分析、模型训练乃至整个业务逻辑的准确性。本文将从一个完整的实战项目出发手把手带你完成一次标准的DC2流程涵盖从需求理解、环境搭建、核心代码实现到结果验证与优化的全过程。无论你是数据科学新手还是希望系统梳理数据预处理流程的开发者都能从中获得可直接复用的代码和清晰的避坑指南。1. 背景与核心概念什么是DC2在开始动手之前我们首先要明确DC2的含义。DC2通常指的是Data Cleaning and Conversion即数据清洗与转换。它是数据预处理Data Preprocessing中最关键、最耗时的一环目的是将原始、杂乱、不一致的数据转化为干净、统一、适合后续分析或建模的格式。一个典型的DC2流程需要解决以下几类问题数据质量问题如缺失值、异常值、重复记录。格式不一致问题如日期格式混乱2024-01-01vs01/01/2024、字符串大小写不统一、单位不统一kgvsKg。数据转换问题如类型转换字符串转数值、特征编码分类变量转数值、特征缩放归一化、标准化。数据集成问题合并来自多个来源的数据表。为什么DC2如此重要因为“垃圾进垃圾出”Garbage In, Garbage Out。无论后续的算法多么高级如果输入的数据质量低下得到的结果也必然不可靠。掌握DC2是每一位数据相关岗位开发者的基本功。2. 环境准备与版本说明本次实战我们将使用Python生态中最主流的数据处理库pandas和numpy。它们功能强大、社区活跃是处理DC2任务的利器。环境要求操作系统Windows 10/11, macOS, 或 Linux (如Ubuntu 20.04)。本文示例在macOS/Linux环境下编写Windows用户请注意路径分隔符的差异使用\。Python版本 3.8。建议使用Python 3.8或3.9以获得最佳的库兼容性。核心库及版本pandas 1.3.0numpy 1.21.0scikit-learn 1.0(用于部分高级转换如标准化)项目结构在开始前建议建立如下清晰的目录结构这有助于管理代码和数据。your_dc2_project/ ├── data/ # 存放原始数据和清洗后的数据 │ ├── raw/ # 原始数据不要直接修改 │ │ └── sales_data_raw.csv │ └── processed/ # 清洗转换后的数据 ├── notebooks/ # 可选Jupyter Notebook用于探索性分析 │ └── eda.ipynb ├── src/ # 源代码 │ ├── __init__.py │ ├── data_cleaner.py # 核心清洗转换逻辑 │ └── utils.py # 工具函数 ├── config.yaml # 可选配置文件如文件路径、参数 ├── requirements.txt # 项目依赖列表 └── main.py # 主程序入口安装依赖在项目根目录下创建requirements.txt文件内容如下pandas1.5.3 numpy1.24.3 scikit-learn1.3.0 pyyaml6.0 # 用于读取配置文件然后在终端中执行pip install -r requirements.txt3. 核心操作与pandas API拆解pandas的DataFrame是进行DC2的核心数据结构。下面我们拆解最常用的几组操作。3.1 数据读取与初步探查任何DC2任务的第一步都是将数据加载到DataFrame中并进行初步了解。import pandas as pd # 读取数据这里以CSV为例 df pd.read_csv(‘data/raw/sales_data_raw.csv’) # 1. 查看数据形状行数列数 print(f“数据形状: {df.shape}“) # 2. 查看前5行数据了解大致内容 print(df.head()) # 3. 查看列名、数据类型和非空值数量 print(df.info()) # 4. 查看数值型列的统计摘要计数、均值、标准差、最小/最大值、分位数 print(df.describe()) # 5. 查看唯一值数量特别是针对分类变量 for col in df.columns: print(f“{col}: {df[col].nunique()} unique values”)3.2 处理缺失值缺失值处理没有“唯一正确”的方法需要根据业务逻辑和数据性质决定。# 检查缺失值 missing_summary df.isnull().sum() print(“缺失值统计:”) print(missing_summary[missing_summary 0]) # 只显示有缺失的列 # 方法1删除缺失行当缺失行占比很小且缺失随机时 df_dropped df.dropna() # 删除任何包含NaN的行 # 或指定列 df_dropped_subset df.dropna(subset[‘customer_id‘ ‘amount’]) # 仅当这两列有缺失时才删除行 # 方法2填充缺失值 # 用固定值填充 df_filled_constant df.fillna({‘category’: ‘Unknown’ ‘amount’: 0}) # 用统计值填充如均值、中位数、众数 mean_amount df[‘amount’].mean() df_filled_mean df.fillna({‘amount’: mean_amount}) # 用前向或后向填充适用于时间序列 df_filled_ffill df.fillna(method‘ffill’) # 用上一个有效值填充 # 方法3插值法对于有序数据 df_interpolated df.interpolate(method‘linear’) # 线性插值3.3 处理异常值异常值可能是错误也可能是重要的信号需谨慎处理。# 常用方法基于标准差Z-score或四分位距IQR import numpy as np def detect_outliers_iqr(df column): “””使用IQR方法检测异常值””” Q1 df[column].quantile(0.25) Q3 df[column].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers df[(df[column] lower_bound) | (df[column] upper_bound)] return outliers lower_bound upper_bound # 检测‘amount’列的异常值 outliers low high detect_outliers_iqr(df ‘amount’) print(f“异常值数量: {len(outliers)} 边界: [{low:.2f} {high:.2f}]“) # 处理异常值可以剔除、替换为边界值或视为缺失值处理 # 剔除 df_no_outliers df[(df[‘amount’] low) (df[‘amount’] high)] # 缩尾处理Winsorization将超出边界的值替换为边界值 df[‘amount_winsorized’] df[‘amount’].clip(lowerlow upperhigh)3.4 数据转换与格式化这是DC2中创造性最强的一部分。# 1. 类型转换 df[‘date’] pd.to_datetime(df[‘date’] format‘%Y-%m-%d’ errors‘coerce’) # 日期转换 df[‘amount’] pd.to_numeric(df[‘amount’] errors‘coerce’) # 字符串转数值 # 2. 字符串操作 df[‘product_name’] df[‘product_name’].str.strip() # 去除首尾空格 df[‘product_name’] df[‘product_name’].str.lower() # 统一小写 df[‘category’] df[‘category’].str.replace(‘’ ‘and’) # 替换字符 # 3. 创建新特征特征工程 df[‘year’] df[‘date’].dt.year df[‘month’] df[‘date’].dt.month df[‘day_of_week’] df[‘date’].dt.dayofweek # 周一0 周日6 df[‘is_weekend’] df[‘day_of_week’].isin([5 6]).astype(int) # 4. 分类变量编码为机器学习准备 # 标签编码 (Label Encoding) from sklearn.preprocessing import LabelEncoder le LabelEncoder() df[‘category_encoded’] le.fit_transform(df[‘category’]) # 独热编码 (One-Hot Encoding) - 更常用避免引入序关系 df_encoded pd.get_dummies(df columns[‘category’] prefix‘cat’)4. 完整实战案例电商销售数据清洗与转换假设我们有一份原始的电商销售数据sales_data_raw.csv内容杂乱。我们的目标是将其清洗为可供分析使用的干净数据集。4.1 数据概览与问题诊断首先加载数据并查看原始面貌。import pandas as pd import numpy as np # 加载数据 df_raw pd.read_csv(‘data/raw/sales_data_raw.csv’) print(“ 原始数据概览 ”) print(df_raw.head(10)) print(“\n 原始数据信息 ”) print(df_raw.info())假设我们发现了以下问题列名有空格且大小写不一如Order IDorder date。order date列格式混乱有2023/12/01Dec 1 2023等。Amount列有字符串如$100.50150和缺失值NaN。Customer ID列有重复和空值。Product列产品名前后有空格且同产品有不同写法如Laptoplaptop。4.2 逐步清洗与转换我们将编写一个模块化的清洗函数。# file: src/data_cleaner.py import pandas as pd import numpy as np from datetime import datetime def clean_sales_data(df): “”” 清洗电商销售数据DataFrame 参数: df (pd.DataFrame): 原始销售数据 返回: pd.DataFrame: 清洗后的数据 “”” df_clean df.copy() # 避免修改原始数据 # 1. 规范化列名 df_clean.columns df_clean.columns.str.strip().str.lower().str.replace(‘ ‘ ‘_’) print(“步骤1: 列名已规范化。”) # 2. 处理‘order_date‘尝试多种日期格式解析 df_clean[‘order_date’] pd.to_datetime(df_clean[‘order_date’] errors‘coerce’ infer_datetime_formatTrue) # 检查转换失败的日期 failed_dates df_clean[df_clean[‘order_date’].isna()] if not failed_dates.empty: print(f“警告: {len(failed_dates)} 行日期无法解析已设为NaT。”) # 可以在这里添加更复杂的逻辑来处理特定格式 # 3. 处理‘amount‘移除货币符号转换为浮点数 # 先确保是字符串类型 df_clean[‘amount’] df_clean[‘amount’].astype(str) df_clean[‘amount’] df_clean[‘amount’].str.replace(‘$’ ‘’ regexFalse).str.replace(‘’ ‘’ regexFalse) df_clean[‘amount’] pd.to_numeric(df_clean[‘amount’] errors‘coerce’) print(“步骤3: ‘amount‘列已转换为数值。”) # 4. 处理‘customer_id‘去除空格填充缺失值为‘UNKNOWN_’索引 df_clean[‘customer_id’] df_clean[‘customer_id’].astype(str).str.strip() # 将空字符串或‘nan‘字符串视为缺失 mask_missing_id (df_clean[‘customer_id’].isin([‘’ ‘nan’ ‘NaN’ ‘None’])) | (df_clean[‘customer_id’].isna()) df_clean.loc[mask_missing_id ‘customer_id’] [f’UNKNOWN_{i}‘ for i in range(mask_missing_id.sum())] print(f“步骤4: 已处理 {mask_missing_id.sum()} 个缺失的customer_id。”) # 5. 处理‘product‘去除首尾空格统一小写 df_clean[‘product’] df_clean[‘product’].astype(str).str.strip().str.lower() # 简单的拼写纠正示例 product_mapping {‘laptop‘: ‘laptop’ ‘notebook‘: ‘laptop’ ‘mobile‘: ‘phone’ ‘cell phone‘: ‘phone’} df_clean[‘product’] df_clean[‘product’].replace(product_mapping) print(“步骤5: ‘product‘列已清洗和标准化。”) # 6. 处理缺失值金额用中位数填充比均值对异常值更鲁棒 if df_clean[‘amount’].isna().any(): median_amount df_clean[‘amount’].median() df_clean[‘amount’].fillna(median_amount inplaceTrue) print(f“步骤6: 用中位数 {median_amount:.2f} 填充了 {df_clean[‘amount’].isna().sum()} 个缺失金额。”) # 7. 删除完全重复的行所有列值都相同 initial_rows len(df_clean) df_clean.drop_duplicates(inplaceTrue) dropped_rows initial_rows - len(df_clean) print(f“步骤7: 删除了 {dropped_rows} 个完全重复的行。”) # 8. 创建衍生特征 df_clean[‘order_year’] df_clean[‘order_date’].dt.year df_clean[‘order_month’] df_clean[‘order_date’].dt.month df_clean[‘order_quarter’] df_clean[‘order_date’].dt.quarter df_clean[‘day_of_week’] df_clean[‘order_date’].dt.dayofweek # Monday0 df_clean[‘is_weekend’] (df_clean[‘day_of_week’] 5).astype(int) print(“步骤8: 已创建时间衍生特征。”) # 9. 重置索引 df_clean.reset_index(dropTrue inplaceTrue) print(“\n 清洗完成 ) print(f“原始数据行数: {len(df)}”) print(f“清洗后数据行数: {len(df_clean)}”) print(f“清洗后列信息:”) print(df_clean.info()) return df_clean4.3 运行与验证创建一个主程序来调用清洗函数并保存结果。# file: main.py import pandas as pd from src.data_cleaner import clean_sales_data import os def main(): # 1. 读取原始数据 input_path ‘data/raw/sales_data_raw.csv’ if not os.path.exists(input_path): print(f“错误: 未找到原始数据文件 {input_path}”) return df_raw pd.read_csv(input_path) print(“成功加载原始数据。”) # 2. 执行清洗 df_clean clean_sales_data(df_raw) # 3. 保存清洗后的数据 output_dir ‘data/processed/’ os.makedirs(output_dir exist_okTrue) # 确保目录存在 output_path os.path.join(output_dir ‘sales_data_clean.csv’) df_clean.to_csv(output_path indexFalse) print(f“\n清洗后的数据已保存至: {output_path}”) # 4. 简单验证 print(“\n 数据验证 ) print(“前5行清洗后数据:”) print(df_clean.head()) print(“\n基本统计信息:”) print(df_clean[[‘amount’ ‘order_year’ ‘order_month’]].describe()) if __name__ “__main__”: main()在终端运行python main.py4.4 结果说明运行上述脚本后你将在data/processed/sales_data_clean.csv中得到一个清洗后的数据文件。与原始数据相比它具有规范的列名全部小写并用下划线连接。统一的日期格式order_date列已成为datetime64类型。干净的数值amount列已是纯数值类型缺失值已被合理填充。标准化的文本product和customer_id列已无多余空格和大小写问题。丰富的特征新增了年、月、季度、是否周末等衍生列便于后续按时间维度分析。无完全重复行保证了数据的唯一性。5. 常见问题与排查思路在DC2过程中你可能会遇到以下典型问题问题现象可能原因排查与解决思路pd.read_csv报UnicodeDecodeError文件编码不是默认的utf-8。尝试指定编码如encoding‘gbk’encoding‘latin1’或encoding‘utf-8-sig’。用chardet库检测文件编码。日期列转换后全部变成NaT日期格式与pandas推断的格式不匹配。使用pd.to_datetime(df[‘col’] format‘%Y/%m/%d’)明确指定格式。或用errors‘coerce’找出无法转换的原始值进行单独处理。数值列转换后出现大量NaN列中混入了非数字字符如货币符号、逗号、文字。先使用.astype(str)转为字符串再用.str.replace()移除特殊字符最后用pd.to_numeric(errors‘coerce’)转换。内存使用量激增程序变慢数据集过大操作产生了不必要的中间副本。1. 读取时指定dtype参数。2. 使用df[df[‘col’] 0].copy()而非df[df[‘col’] 0]来避免链式索引警告。3. 对于超大文件考虑分块读取 (chunksize)。分组或聚合结果不符合预期分组键中存在意料之外的空格、大小写或数据类型不一致。在分组前对作为键的列执行.str.strip().str.lower().astype(str)进行标准化。使用df[‘col’].unique()查看唯一值。清洗后数据行数意外减少dropna()或drop_duplicates()条件过于严格合并操作丢失了数据。仔细检查每个删除或过滤步骤前后的行数。对于dropna使用subset参数指定关键列。对于合并确认使用的是inner、left、right还是outerjoin。6. 最佳实践与工程建议将DC2从一次性的脚本升级为可维护、可复用的工程代码需要遵循以下实践保持原始数据不可变永远在数据的副本df.copy()上进行操作并保留原始文件。这让你可以随时回溯和重新开始。模块化与函数化如案例所示将清洗逻辑封装在独立的函数或类中。这提高了代码的可读性、可测试性和复用性。可以为不同的数据源编写不同的清洗器。配置化参数将文件路径、填充值、映射字典如产品名映射等提取到配置文件如config.yaml或config.py中。这样无需修改代码即可调整清洗逻辑。记录与日志在清洗函数中加入详细的print语句或使用logging模块记录每一步的操作和影响如“删除了X行重复数据”。这对于调试和审计至关重要。单元测试为你的核心清洗函数编写单元测试。使用一个小型的、包含各种“脏数据”的样例数据集验证函数输出是否符合预期。pytest是很好的选择。处理大数据集如果数据量极大超过内存考虑使用Dask或Modin库它们提供了类似pandas的API但支持并行和核外计算。或者使用数据库如SQLitePostgreSQL进行清洗。自动化与流水线使用Makefile、Apache Airflow或Prefect等工具将数据清洗任务自动化、调度化形成可重复的数据流水线。版本控制数据对于重要的中间数据和最终产出考虑使用DVC(Data Version Control) 进行版本管理将数据和代码变更关联起来。第一次做DC2核心是建立起清晰、稳健的处理流程。从理解数据开始分步骤、模块化地解决每一个质量问题并始终牢记验证结果。本文提供的案例和代码是一个完整的起点你可以将其作为模板根据自己项目的具体需求进行修改和扩展。数据清洗是一项需要耐心和细心的工作但每一次成功的DC2都会让你对数据的理解更深一步为后续更有价值的分析和建模打下坚实的基础。动手运行一遍代码替换成你自己的数据集体验从混乱到有序的全过程吧。
返回列表