在实际业务中,数据往往存在不整洁、不一致,并伴随各种异常。数据清洗的过程包括格式的校正、缺失值的处理、异常数据的处理以及数据的标准化等操作。而 Pandas 和 NumPy 提供了一整套高效的工具来应对这些挑战,尤其在处理结构化数据方面展现出显著的优势。
以下是几种典型的业务场景,结合 Pandas 与 NumPy 进行数据清洗与转换的详细步骤。
一、数据加载与理解
实际的业务数据通常源自 CSV 文件、数据库等。首先需要将数据导入,以便后续进行清洗处理。假设我们有一个包含客户信息的数据集,数据中存在诸多不一致和异常情况。
我们使用 Pandas 工具辅助:
使用 Pandas 读取数据示例如下:
import pandas as pd
import numpy as np
# 假设我们有一个包含客户数据的 CSV 文件
data = {
"客户ID": [101, 102, 103, 104, 105],
"姓名": ["张三", "李四", None, "王五", "赵六"],
"年龄": ["25", "三十", "42", "NaN", 28],
"电话": ["13800138000", "无", "13900139000", "12345678901", "13900139001"],
"邮箱": ["zhangsan@example.com", "lisi@invalid", "wangwu@domain.com", "missing", "zhaoliu@domain.cn"],
"注册日期": ["2020-05-01", "2021/07/15", "03-15-2021", "2021年5月20日", "20210518"],
"消费金额": ["200.5元", "350", "三百五十元", None, "400"]
}
df = pd.DataFrame(data)
print(df)
该数据框存在如下问题:
-
姓名
列存在缺失值。 -
年龄
列中含有非数值文本(如三十
)。 -
电话
列中存在无效值无
。 -
邮箱
列存在无效的电子邮件格式。 -
注册日期
格式不统一。 -
消费金额
含有非标准的单位和文本信息。
二、数据清洗与标准化步骤
以下将逐步分解数据清洗的过程。
1. 处理缺失值
缺失值的处理是数据清洗的基础之一。可以选择删除含有缺失值的行,或者使用统计值(如均值、中位数等)进行填补。针对 姓名
列的缺失值,这里用字符串 未知
进行填充。
# 填充姓名列中的缺失值为 '未知'
df['姓名'].fillna('未知', inplace=True)
print(df)
2. 清洗与转换年龄列
年龄
列中包含非数值数据,为了标准化年龄数据,可以采用以下策略:
- 将中文数字转换为阿拉伯数字。
- 将
NaN
等缺失值填充为中位数或均值。
可以使用 map
与正则表达式完成转换。
import re
# 定义函数将中文数字转换为阿拉伯数字
def convert_chinese_age(age_str):
chinese_map = {
"一": 1, "二": 2, "三": 3, "四": 4,
"五": 5, "六": 6, "七": 7, "八": 8,
"九": 9, "十": 10, "三十": 30, "四十": 40, "五十": 50
}
if isinstance(age_str, str):
return chinese_map.get(age_str, np.nan)
return age_str
# 转换年龄列中的文本并进行数值化处理
df['年龄'] = df['年龄'].apply(lambda x: convert_chinese_age(x) if isinstance(x, str) else x)
df['年龄'] = pd.to_numeric(df['年龄'], errors='coerce')
# 使用中位数填充缺失的年龄值
age_median = df['年龄'].median()
df['年龄'].fillna(age_median, inplace=True)
print(df)
3. 处理电话号码中的异常值
电话
列中包含无效的值 无
。需要使用正则表达式过滤无效号码,确保电话号码符合正确格式。
# 使用正则表达式过滤有效电话号码
def validate_phone(phone):
if re.match(r'^1[3-9]\d{9}$', str(phone)):
return phone
return np.nan
df['电话'] = df['电话'].apply(validate_phone)
print(df)
4. 清洗和标准化邮箱地址
对于 邮箱
列,可以使用正则表达式验证邮箱的有效性,将无效的邮箱标记为 np.nan
。
# 验证邮箱格式
def validate_email(email):
if re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
return email
return np.nan
df['邮箱'] = df['邮箱'].apply(validate_email)
print(df)
5. 标准化日期格式
由于日期格式各异,需要统一标准化为 YYYY-MM-DD
格式。可以使用 pd.to_datetime()
处理。
# 使用 pandas 的 to_datetime 函数标准化日期
df['注册日期'] = pd.to_datetime(df['注册日期'], errors='coerce')
print(df)
errors='coerce'
会将无法解析的日期转换为 NaT
,方便进一步处理。
6. 清洗消费金额
消费金额
列中包含文本单位,需要清洗这些单位并将其转换为浮点数。
# 定义函数提取数值并转换为浮点数
def clean_amount(amount):
if isinstance(amount, str):
amount = re.sub(r'[^\d.]', '', amount)
return float(amount) if amount else np.nan
df['消费金额'] = df['消费金额'].apply(clean_amount)
print(df)
7. 处理缺失值与异常值
通常需要对各列整体检查异常值或缺失值,例如,如果 电话
或 邮箱
为空,可以选择删除这些行,因为这些字段通常是必须的。
# 删除 '电话' 和 '邮箱' 为 NaN 的行
df.dropna(subset=['电话', '邮箱'], inplace=True)
print(df)
三、数据转换
数据清洗完成后,可能还需进一步转换数据以便进行后续分析。
1. 添加新列
基于现有数据生成新的列。例如,基于 年龄
列创建一个 年龄段
列。
# 创建新列 '年龄段'
def categorize_age(age):
if age < 20:
return '少年'
elif age < 40:
return '青年'
elif age < 60:
return '中年'
else:
return '老年'
df['年龄段'] = df['年龄'].apply(categorize_age)
print(df)
2. 类别字段的处理
如果需要将 年龄段
这样的类别字段用于机器学习模型,通常需要将其转换为数值型,例如使用独热编码(One-Hot Encoding)。
# 使用 pandas 的 get_dummies 方法进行独热编码
df = pd.get_dummies(df, columns=['年龄段'])
print(df)
3. 数据归一化与标准化
为了后续的数据分析或模型训练,通常需要对数值型数据进行标准化或归一化处理,例如对 消费金额
进行归一化。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df['消费金额_归一化'] = scaler.fit_transform(df[['消费金额']])
print(df)
四、完整代码与总结
经过以上分步骤处理,我们对包含缺失、不一致和异常值的数据进行了全面的清洗和标准化。
以下是完整的实现代码:
import pandas as pd
import numpy as np
import re
from sklearn.preprocessing import MinMaxScaler
# 原始数据
data = {
"客户ID": [101, 102, 103, 104, 105],
"姓名": ["张三", "李四", None, "王五", "赵六"],
"年龄": ["25", "三十", "42", "NaN", 28],
"电话": ["13800138000", "无", "13900139000", "12345678901", "13900139001"],
"邮箱": ["zhangsan@example.com", "lisi@invalid", "wangwu@domain.com", "missing", "zhaoliu@domain.cn"],
"注册日期": ["2020-05-01", "2021/07/15", "03-15-2021", "2021年5月20日", "20210518"],
"消费金额": ["200.5元", "350", "三百五十元", None, "400"]
}
# 创建 DataFrame
df = pd.DataFrame(data)
# 处理缺失值
df['姓名'].fillna('未知', inplace=True)
# 清洗年龄列
def convert_chinese_age(age_str):
chinese_map = {
"一": 1, "二": 2, "三": 3, "四": 4,
"五": 5, "六": 6, "七": 7, "八": 8,
"九": 9, "十": 10, "三十": 30, "四十": 40, "五十": 50
}
if isinstance(age_str, str):
return chinese_map.get(age_str, np.nan)
return age_str
df['年龄'] = df['年龄'].apply(lambda x: convert_chinese_age(x) if isinstance(x, str) else x)
df['年龄'] = pd.to_numeric(df['年龄'], errors='coerce')
age_median = df['年龄'].median()
df['年龄'].fillna(age_median, inplace=True)
# 清洗电话列
def validate_phone(phone):
if re.match(r'^1[3-9]\d{9}$', str(phone)):
return phone
return np.nan
df['电话'] = df['电话'].apply(validate_phone)
# 清洗邮箱列
def validate_email(email):
if re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
return email
return np.nan
df['邮箱'] = df['邮箱'].apply(validate_email)
# 统一注册日期格式
df['注册日期'] = pd.to_datetime(df['注册日期'], errors='coerce')
# 清洗消费金额列
def clean_amount(amount):
if isinstance(amount, str):
amount = re.sub(r'[^\d.]', '', amount)
return float(amount) if amount else np.nan
df['消费金额'] = df['消费金额'].apply(clean_amount)
# 删除电话和邮箱为空的行
df.dropna(subset=['电话', '邮箱'], inplace=True)
# 创建年龄段列
def categorize_age(age):
if age < 20:
return '少年'
elif age < 40:
return '青年'
elif age < 60:
return '中年'
else:
return '老年'
df['年龄段'] = df['年龄'].apply(categorize_age)
# 独热编码
df = pd.get_dummies(df, columns=['年龄段'])
# 消费金额归一化
scaler = MinMaxScaler()
df['消费金额_归一化'] = scaler.fit_transform(df[['消费金额']])
# 打印最终结果
print(df)
五、结论
通过上述代码,我们有效解决了实际业务中常见的数据不一致性问题,包括缺失值、格式不一致以及异常值处理。同时,通过 Pandas 和 NumPy 的结合使用,能够对数据进行有效的清洗、标准化和转换,从而为后续的数据分析与机器学习任务奠定坚实的基础。
这些操作对于各类数据处理任务而言是至关重要的,能够显著提高数据的分析质量与一致性,确保业务分析的准确性与可靠性。
网友评论