一、题目
python
"""
===============================================================================
项目:评估和清理英国电商公司销售数据
===============================================================================
【分析目标】
对英国电商公司在 2010-12-01 到 2011-12-09 的交易数据进行评估与清洗,
为后续"畅销产品分析、国家分布分析、销售额分析"等任务提供干净的数据集。
【数据字段说明】
- InvoiceNo: 发票号,若以"C"开头代表订单取消
- StockCode: 产品代码
- Description: 产品名称
- Quantity: 数量
- InvoiceDate: 交易日期时间
- UnitPrice: 单价(英镑)
- CustomerID: 客户编号
- Country: 国家名称
【主要问题】
- 缺失值:Description、CustomerID
- 数据类型不正确:InvoiceDate、CustomerID
- 国家名称不一致:USA/United States,UK/U.K./United Kingdom
- 无效数据:负数 Quantity、负数 UnitPrice
- 无效交易:取消订单、单价为 0 的记录
===============================================================================
下面开始正式清理流程(按你要求采用统一的标题风格)
===============================================================================
"""
import pandas as pd
# pandas设置
pd.set_option('display.width', None) # 150,设置打印宽度
pd.set_option('display.max_rows', None) # 打印最大行数
pd.set_option('display.max_columns', None) # 打印最大列数
print("""
#############################################################################
# 01:读取原始数据 ------ p01.csv
#############################################################################
""")
print("\n================ 读取原始数据(p01.csv)================")
original_data = pd.read_csv("data/p01.csv")
print(original_data.head(10))
print("""
从抽样的10行数据数据来看,数据符合"每列是一个变量,每行是一个观察值",具体来看每行是关于某商品的一次交易,每列是交易相关的各个变量,因此不存在结构性问题。
""")
print("""
#############################################################################
# 02:评估数据 ------ 缺失值、数据类型、不一致性、无效值
#############################################################################
""")
print("\n================ original_data.info() ====================")
print(original_data.info())
print("""
从输出结果来看,数据共有541909条观察值,而Description、CustomerID变量存在缺失值。
此外,InvoiceDate的数据类型应为日期,CustomerID的数据类型应为字符串,应当进行数据格式转换。
""")
# ===== Description 缺失 =====
print("\n================ Description 缺失情况 ====================")
missing_desc = original_data[original_data["Description"].isnull()]
print(missing_desc.head(25))
print(f"""
有{len(missing_desc)}条交易数据缺失Description变量值。
从输出结果来看,这些缺失Description的交易数据,UnitPrice都为0。为了验证猜想,我们增加筛选条件,看是否存在Description变量缺失且UnitPrice不为0的数据。
""")
print("\n================ Description 缺失且 UnitPrice != 0? ====================")
print(original_data[(original_data["Description"].isnull()) &
(original_data["UnitPrice"] != 0)])
# ===== CustomerID 缺失 =====
print("\n================ CustomerID 缺失情况 ====================")
missing_customer = original_data[original_data["CustomerID"].isnull()]
print(missing_customer.head(25))
print("""
CustomerID表示客户编号,不是分析畅销商品的必要变量。
并且从输出结果来看,有些CustomerID缺失的交易数据仍然有效,因此保留此变量为空的观察值。
""")
# ===== Country 不一致数据 ===== 字符型处理.value_counts() 后续做名称统一
print("\n================ Country 唯一值统计 ====================")
print(original_data["Country"].value_counts())
print("""
从Country变量值来看,"USA"、"United States"均在表示美国,
"United Kingdom"、"UK"、"U.K."均在表示英国,
因此应该在清洗步骤对这些值进行统一,只保留一个指代值。
""")
# ===== 数值异常 =====
print("\n================ describe() 数值统计 数值型数据处理:describe()====================")
print(original_data.describe())
print("""
从输出结果来看,Quantity和UnitPrice存在负数,会对后续数值分析产生影响。
因此我们先筛选出Quantity数值为负数的观察值,进一步评估其含义。
""")
print("\n================ Quantity < 0 的数据 ====================")
print(original_data[original_data["Quantity"] < 0].head(25))
print("""
从筛选结果来看,Quantity变量为负数的观察值,InvoiceNo似乎均以C开头,表示订单被取消。
但此猜测不一定准确,为了验证猜想,我们增加筛选条件,看是否存在Quantity变量为负数且InvoiceNo不以C开头的观察值。
""")
print("\n================ Quantity<0 且 InvoiceNo 不以 C 开头 ====================")
print(original_data[(original_data["Quantity"] < 0) &
(original_data["InvoiceNo"].str[0] != "C")].head(25))
print("""
以上猜想被证实错误,因为还存在Quantity变量为负数且InvoiceNo不以C开头的观察值。
但根据以上输出结果,这些筛选出的观察值的UnitPrice观察值均为0,因此增加UnitPrice的条件进行验证。
""")
print("\n================ Quantity<0 且 InvoiceNo 非C 且 UnitPrice != 0? ====================")
print(original_data[(original_data["Quantity"] < 0) &
(original_data["InvoiceNo"].str[0] != "C") & #不以C开头
(original_data["UnitPrice"] != 0)])
print("""
根据输出结果,猜想得到验证,当Quantity变量为负数的时候,观察值满足以下条件之一:
- InvoiceNo以C开头,表示订单被取消
- UnitPrice为0,说明单价为0英镑
这些交易数据均不是有效成交数据,不贡献销售,不在后续分析范围内,因此我们将在数据清理步骤,将Quantity为负数的观察值删除。
""")
print("\n================ UnitPrice < 0 的数据 ====================")
print(original_data[original_data["UnitPrice"] < 0])
print("""
从输出结果来看,UnitPrice为负数的观察值都是坏账调账,不属于实际商品交易数据,因此也在数据清理步骤中将其删除。
""")
print("""
#############################################################################
# 03:清理数据 ------ 类型转换、缺失值处理、国家统一、删除无效行
#############################################################################
""")
print("\n================ 创建 cleaned_data 副本 ====================")
cleaned_data = original_data.copy()
print("\n================ InvoiceDate → 日期类型转换,其余是astype() ====================")
cleaned_data["InvoiceDate"] = pd.to_datetime(cleaned_data["InvoiceDate"])
print("\n================ CustomerID → 字符串================")
cleaned_data["CustomerID"] = cleaned_data["CustomerID"].astype(str)
cleaned_data["CustomerID"] = cleaned_data["CustomerID"].str.slice(0, -2)
#.str.slice(0, -2) :从0: 起始位置(从索引0开始)到-2: 结束位置(从末尾倒数第2个字符之前结束)
print("\n================ 删除缺失 Description ====================")
cleaned_data.dropna(subset=["Description"], inplace=True)
print("\n================ Country 名称统一 ====================")
cleaned_data["Country"] = cleaned_data["Country"].replace({
"USA": "United States",
"UK": "United Kingdom",
"U.K.": "United Kingdom"
})
print("\n================ 删除 Quantity < 0 ====================")
cleaned_data = cleaned_data[cleaned_data["Quantity"] >= 0]
print("\n================ 删除 UnitPrice < 0 ====================")
cleaned_data = cleaned_data[cleaned_data["UnitPrice"] >= 0]
print("\n================ 清洗完成,预览 cleaned_data ====================")
print(cleaned_data.head())
二、复盘