【PYTHON10年经验总结】第二课 电商平台销售数据分析实践分解 – 常用数据清洗案例
1. 处理缺失值
实践: 使用均值、中位数或众数填充数值型数据的缺失值;使用最频繁类别填充分类数据。 案例: 在订单表中,某些客户的年龄信息缺失,可以使用所有客户年龄的平均值来填补这些缺失项。
Python深色版本import pandas as pd
# 示例数据
data = {'customer_id': [1, 2, 3, 4],
'age': [25, None, 30, 22]}
df = pd.DataFrame(data)
# 使用平均值填充缺失的年龄
mean_age = df['age'].mean()
df['age'].fillna(mean_age, inplace=True)
print(df)
2. 删除重复记录
实践: 利用pandas库去除重复的数据条目。 案例: 检查用户注册表,发现有多名用户的邮箱地址完全相同,删除多余的记录。
Python深色版本# 示例数据
data = {'email': ['user1@example.com', 'user2@example.com', 'user1@example.com'],
'name': ['Alice', 'Bob', 'Alice']}
df = pd.DataFrame(data)
# 删除重复记录
df.drop_duplicates(inplace=True)
print(df)
3. 标准化日期格式
实践: 将所有的日期字段转换为统一的标准格式(如YYYY-MM-DD)。 案例: 订单表中的日期字段格式不一,有些是MM/DD/YYYY,有些是DD/MM/YYYY,将其全部转换为YYYY-MM-DD。
Python深色版本from datetime import datetime
# 示例数据
data = {'order_date': ['01/15/2020', '15/01/2020', '02/20/2020']}
df = pd.DataFrame(data)
# 定义两种可能的日期格式
date_formats = ['%m/%d/%Y', '%d/%m/%Y']
# 解析并标准化日期
def parse_date(date_str):
for fmt in date_formats:
try:
return datetime.strptime(date_str, fmt).strftime('%Y-%m-%d')
except ValueError:
pass
raise ValueError(f"Date format not recognized: {date_str}")
df['order_date'] = df['order_date'].apply(parse_date)
print(df)
4. 统一单位
实践: 确保所有度量单位一致,例如重量、价格等。 案例: 商品表中,有些商品的价格是以元为单位,有些是以角为单位,将它们都转换为元。
Python深色版本# 示例数据
data = {'product_id': [1, 2],
'price': [250, 25]}
df = pd.DataFrame(data)
# 假设第二行的价格是以角为单位,转换为元
df.loc[df.index[1], 'price'] /= 10
print(df)
5. 处理异常值
实践: 使用箱线图或其他统计方法识别并处理异常值。 案例: 发现某个产品的单价高达数十万,明显是一个错误输入,需要进一步调查原因后决定如何处理。
Python深色版本import matplotlib.pyplot as plt
# 示例数据
data = {'product_id': [1, 2, 3, 4],
'price': [250, 260, 270, 250000]}
df = pd.DataFrame(data)
# 绘制箱线图以识别异常值
plt.boxplot(df['price'])
plt.show()
# 过滤掉异常值
q1 = df['price'].quantile(0.25)
q3 = df['price'].quantile(0.75)
iqr = q3 - q1
filtered_df = df[(df['price'] >= (q1 - 1.5 * iqr)) & (df['price'] <= (q3 + 1.5 * iqr))]
print(filtered_df)
6. 规范文本格式
实践: 对于文本字段,统一大小写,去除前后空格。 案例: 用户评论中的产品名称有时会因为大小写不同而被视为不同的产品,统一改为小写。
Python深色版本# 示例数据
data = {'comment': ['Great Product!', 'great product ', 'PRODUCT IS GOOD']}
df = pd.DataFrame(data)
# 统一小写并去除前后空格
df['comment'] = df['comment'].str.lower().str.strip()
print(df)
7. 正则表达式验证
实践: 使用正则表达式检查字符串是否符合预期模式。 案例: 验证电子邮件地址格式,确保其符合标准的电子邮件格式规则。
Python深色版本import re
# 示例数据
data = {'email': ['user1@example.com', 'invalid-email', 'user2@domain.co.uk']}
df = pd.DataFrame(data)
# 正则表达式验证电子邮件格式
email_pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
def is_valid_email(email):
return bool(re.match(email_pattern, email))
df['is_valid'] = df['email'].apply(is_valid_email)
print(df)
8. 类别一致性
实践: 确保分类字段中的类别名称一致。 案例: 商品分类中有“手机”、“智能手机”、“移动电话”,将它们统一为“手机”。
Python深色版本# 示例数据
data = {'category': ['手机', '智能手机', '移动电话', '手机']}
df = pd.DataFrame(data)
# 映射类别名称
category_mapping = {'智能手机': '手机', '移动电话': '手机'}
df['category'] = df['category'].map(category_mapping).fillna(df['category'])
print(df)
9. 地址标准化
实践: 使用地理编码服务将地址转换为经纬度坐标,以便进行空间分析。 案例: 将用户配送地址转换为经纬度,用于分析哪些地区的购买力更强。
Python深色版本# 示例数据
data = {'address': ['上海市浦东新区世纪大道100号', '北京市朝阳区建国门外大街1号']}
df = pd.DataFrame(data)
# 使用geopy库获取经纬度
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="geoapiExercises")
def get_coordinates(address):
location = geolocator.geocode(address)
if location:
return (location.latitude, location.longitude)
else:
return None
df['coordinates'] = df['address'].apply(get_coordinates)
print(df)
10. 数据类型转换
实践: 确保每个字段存储正确的数据类型。 案例: 用户ID原本以字符串形式存储,现在应转换为整数类型。
Python深色版本# 示例数据
data = {'user_id': ['1', '2', '3'], 'name': ['Alice', 'Bob', 'Charlie']}
df = pd.DataFrame(data)
# 转换数据类型
df['user_id'] = df['user_id'].astype(int)
print(df.dtypes)
11. 去除噪声
实践: 移除无关紧要的信息,例如HTML标签、特殊字符等。 案例: 清洗用户评论时,移除其中包含的HTML标签和其他不需要的符号。
Python深色版本from bs4 import BeautifulSoup
# 示例数据
data = {'comment': ['<b>Great</b> Product!', 'Product is <br/>good.', 'Excellent!']}
df = pd.DataFrame(data)
# 使用BeautifulSoup去除HTML标签
def remove_html_tags(text):
soup = BeautifulSoup(text, "html.parser")
return soup.get_text()
df['clean_comment'] = df['comment'].apply(remove_html_tags)
print(df)
12. 数据合并
实践: 合并来自不同源的数据集,确保所有相关信息都在一个表格中。 案例: 将库存数据表与订单数据表合并,以便了解哪些商品售罄最快。
Python深色版本# 示例数据
inventory_data = {'product_id': [1, 2, 3], 'stock': [10, 20, 0]}
orders_data = {'product_id': [1, 2, 1], 'quantity': [1, 2, 1]}
inventory_df = pd.DataFrame(inventory_data)
orders_df = pd.DataFrame(orders_data)
# 合并两个数据集
merged_df = pd.merge(inventory_df, orders_df, on='product_id')
print(merged_df)
13. 转换编码
实践: 将非UTF-8编码的数据转换为UTF-8编码。 案例: 某些CSV文件可能是GBK编码的,需要先转码再读取。
Python深色版本# 示例数据:假设有一个名为'sales_data.csv'的文件,它是GBK编码的
# sales_data.csv 内容:
# product_id,name,price
# 1,手机,250
# 2,电脑,3000
# 读取并转换编码
sales_df = pd.read_csv('sales_data.csv', encoding='gbk')
print(sales_df)
14. 缺失值标记
实践: 明确标记缺失值,避免在后续分析中被误认为实际数据。 案例: 在数据集中添加一个新的列,专门用来标识那些原始数据中存在缺失的情况。
Python深色版本# 示例数据
data = {'customer_id': [1, 2, 3, 4],
'age': [25, None, 30, 22]}
df = pd.DataFrame(data)
# 添加标记列
df['age_missing'] = df['age'].isnull()
print(df)
15. 字符串长度限制
实践: 对过长的字符串进行截断或者清理。 案例: 有些用户的备注栏非常冗长,导致数据库字段溢出,需要截断到一定长度。
Python深色版本# 示例数据
data = {'remark': ['This is a very long remark that needs to be truncated.',
'Short remark',
'Another long remark that should be shortened.']}
df = pd.DataFrame(data)
# 截断字符串
max_length = 20
df['short_remark'] = df['remark'].str[:max_length]
print(df)
16. 时间序列调整
实践: 确保时间序列数据按顺序排列,并且没有跳过的点。 案例: 销售数据的时间戳可能乱序,需要重新排序并且补全缺失的时间点。
Python深色版本import numpy as np
# 示例数据
data = {'timestamp': ['2020-01-01', '2020-01-03', '2020-01-02'],
'sales': [100, 200, 150]}
df = pd.DataFrame(data)
# 转换为datetime类型
df['timestamp'] = pd.to_datetime(df['timestamp'])
# 排序
df.sort_values(by='timestamp', inplace=True)
# 补充缺失的时间戳
all_dates = pd.date_range(start=df['timestamp'].min(), end=df['timestamp'].max())
df.set_index('timestamp', inplace=True)
df = df.reindex(all_dates, fill_value=np.nan)
df.reset_index(inplace=True)
df.rename(columns={'index': 'timestamp'}, inplace=True)
print(df)
17. 外键约束
实践: 在关系型数据库中设置外键约束,防止孤立记录的存在。 案例: 确保每个订单都有对应的用户ID,如果找不到匹配的用户,则该订单无效。
Python深色版本# 示例数据
users_data = {'user_id': [1, 2, 3], 'name': ['Alice', 'Bob', 'Charlie']}
orders_data = {'order_id': [1, 2, 3], 'user_id': [1, 2, 4]} # 注意最后一个user_id不存在
users_df = pd.DataFrame(users_data)
orders_df = pd.DataFrame(orders_data)
# 合并数据集并检查孤立记录
merged_df = pd.merge(orders_df, users_df, on='user_id', how='left')
isolated_orders = merged_df[merged_df['name'].isna()]
print("Isolated Orders:")
print(isolated_orders)
18. 异常检测算法
实践: 应用机器学习算法自动检测潜在的异常值。 案例: 使用聚类算法找出销售额分布中的离群点,确定是否存在欺诈行为。
Python深色版本from sklearn.cluster import DBSCAN
# 示例数据
data = {'store_id': [1, 2, 3, 4, 5],
'sales': [100, 120, 110, 500, 115]}
df = pd.DataFrame(data)
# 使用DBSCAN算法检测异常值
model = DBSCAN(eps=50, min_samples=2)
df['cluster'] = model.fit_predict(df[['sales']])
outliers = df[df['cluster'] == -1]
print("Outliers:")
print(outliers)
19. 批量更新脚本
实践: 创建自动化脚本来批量执行清洗任务。 案例: 每天晚上自动运行Python脚本,清除新导入数据中的脏数据。
Python深色版本# 假设我们每天从CSV文件导入新的数据
# 新数据文件名为'daily_sales_YYYYMMDD.csv'
# 以下是一个简单的脚本框架
import os
from datetime import datetime
def clean_data(file_path):
# 加载数据
df = pd.read_csv(file_path)
# 处理缺失值
mean_price = df['price'].mean()
df['price'].fillna(mean_price, inplace=True)
# 删除重复记录
df.drop_duplicates(inplace=True)
# 标准化日期格式
def parse_date(date_str):
try:
return datetime.strptime(date_str, '%m/%d/%Y').strftime('%Y-%m-%d')
except ValueError:
return None
df['order_date'] = df['order_date'].apply(parse_date)
# 其他清洗步骤...
# 保存清理后的数据
cleaned_file_path = file_path.replace('.csv', '_cleaned.csv')
df.to_csv(cleaned_file_path, index=False)
# 获取当天的文件名
today = datetime.now().strftime('%Y%m%d')
file_name = f'daily_sales_{today}.csv'
if os.path.exists(file_name):
clean_data(file_name)
else:
print(f"File {file_name} does not exist.")
20. 文档记录
实践: 记录每次清洗操作的过程和结果,方便日后审计和重现。 案例: 保存每次数据清洗前后的对比报告,包括具体的操作细节和影响范围。
Python深色版本import logging
# 设置日志记录器
logging.basicConfig(filename='data_cleaning.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
# 日志记录函数
def log_cleaning_operation(operation_description, before, after):
logging.info(f"Operation: {operation_description}")
logging.info(f"Before:\n{before.head()}\nAfter:\n{after.head()}")
# 示例数据
data = {'customer_id': [1, 2, 3, 4],
'age': [25, None, 30, 22]}
df = pd.DataFrame(data)
# 记录初始状态
log_cleaning_operation("Initial Data", df.copy(), df)
# 处理缺失值
mean_age = df['age'].mean()
df['age'].fillna(mean_age, inplace=True)
# 记录处理缺失值后的状态
log_cleaning_operation("Fill Missing Age Values", df.copy(), df)
# 删除重复记录
df.drop_duplicates(inplace=True)
# 记录删除重复记录后的状态
log_cleaning_operation("Remove Duplicate Records", df.copy(), df)
# 输出最终状态
print(df)