28 农产品交易可视化性分析
28.1 引言农产品电商的数据价值
农产品电子商务的快速发展产生了海量交易数据。通过可视化分析,我们可以:
发现业务规律: - 识别季节性需求和价格波动模式 - 发现区域消费偏好差异 - 优化供应链和库存管理
理论背景:探索性数据分析(EDA)
探索性数据分析(Exploratory Data Analysis, EDA)由统计学家John Tukey于1977年提出。其核心思想是在进行正式建模之前,通过统计图形和摘要统计来理解数据的特征。
EDA的四个核心原则: 1. 怀疑精神: 对数据和假设保持质疑 2. 可视化优先: 图形比数字更直观 3. 计算支撑: 用统计量验证视觉发现 4. 迭代探索: 分析是循环往复的过程
农产品数据的特殊性: - 季节性: 农产品生产受自然周期影响 - 地域性: 不同地区消费习惯差异大 - 易腐性: 保鲜期短,库存管理至关重要 - 价格波动: 受供需关系和政策影响明显
28.2 数据准备与探索
28.2.1 数据读取与初步检查
平台任务解答代码
以下代码与教学平台任务要求完全一致:
# 注:processed_data.csv数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
# 导入必要的数据分析和可视化库
import pandas as pd
import numpy as np # 导入NumPy数值计算库
import matplotlib.pyplot as plt # 导入Matplotlib绑图库
import seaborn as sns # 导入Seaborn可视化库
import warnings # 导入warnings模块用于控制警告输出
warnings.filterwarnings('ignore') # 忽略运行时警告信息以保持输出整洁
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体作为默认字体以支持中文
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
# 读取处理好的数据集
data = pd.read_csv('processed_data.csv')
# 查看数据集的基本情况
print(data.head()) # 显示数据集前几行
print("查看数据集的基本信息:") # 输出查看数据集的基本信息
print(data.info()) # 显示数据集的基本信息,包括列名、数据类型和非空值数量
print("查看数据集有无缺失值:") # 输出查看数据集有无缺失值
print(data.isnull().sum()) # 统计每列的缺失值数量
print("查看数据集有无重复值:") # 输出查看数据集有无重复值
print(data.duplicated().sum()) # 统计重复行的数量
# 创建特征映射字典,将英文列名映射为中文名称,用于图表标题
feature_map = {
'price': '价格', # 映射"price"→"价格"
'quantity': '数量', # 映射"quantity"→"数量"
'customer_age': '顾客年龄', # 映射"customer_age"→"顾客年龄"
'return_flag': '是否返单', # 映射"return_flag"→"是否返单"
'sales_amount': '销售额' # 映射"sales_amount"→"销售额"
} # 数据结构定义结束
# 创建箱型图展示各特征的分布情况
plt.figure(figsize=(20,10)) # 设置图形大小
for i, feature in enumerate(feature_map.keys()): # 循环遍历
plt.subplot(2,3, i+1) # 创建2行3列的子图布局
plt.title(f'{feature_map[feature]}的箱型图') # 设置子图标题
plt.boxplot(data[feature]) # 绑制箱线图
plt.xlabel(f'{feature_map[feature]}') # 设置x轴标签
plt.ylabel('频率') # 设置y轴标签
plt.savefig("特征箱型图.png") # 保存图形
# 创建一个字典来映射产品名称到正确的分类
category_mapping = {
'安溪铁观音': '茶叶', # 映射"安溪铁观音"→"茶叶"
'武夷岩茶': '茶叶', # 映射"武夷岩茶"→"茶叶"
'福州茉莉花': '茶叶', # 映射"福州茉莉花"→"茶叶"
'古田银耳': '食用菌', # 映射"古田银耳"→"食用菌"
'建宁莲子': '中药材', # 映射"建宁莲子"→"中药材"
'琯溪蜜柚': '水果', # 映射"琯溪蜜柚"→"水果"
'宁德大黄鱼': '水产品' # 映射"宁德大黄鱼"→"水产品"
} # 数据结构定义结束
# 应用映射来更新category列,将产品名称转换为产品类别
data['category'] = data['product_name'].map(category_mapping).fillna(data['category'])
# 2.可视化展示 - 计算并转置描述性统计数据(结果未打印,仅计算)
data.describe().T
# 2.1 顾客信息可视化
plt.figure(figsize=(20,10)) # 设置图形大小
# 顾客年龄分布直方图
plt.subplot(2,2,1)
sns.distplot(data['customer_age'], bins=30, kde=True, color='blue') # 绘制带核密度估计的直方图
plt.title('顾客年龄分布', fontsize=16) # 设置标题
plt.xlabel('顾客年龄', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 顾客年龄组别分布柱状图
plt.subplot(2,2,2)
order = ['18-25', '26-35', '36-45', '46-55', '56+'] # 自定义年龄组顺序
sns.countplot(data=data, x='age_group', color='orange', order=order) # 按指定顺序绘制柱状图
plt.title('顾客年龄组别分布', fontsize=16) # 设置标题
plt.xlabel('顾客年龄组别', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 顾客性别分布柱状图
plt.subplot(2,2,3)
sns.countplot(data=data, x='customer_gender', color='green') # 绘制性别分布柱状图
plt.title('顾客性别分布', fontsize=16) # 设置标题
plt.xlabel('顾客性别', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 顾客性别比例饼图
plt.subplot(2,2,4)
plt.pie(data['customer_gender'].value_counts(), # 绑制饼图
labels=data['customer_gender'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%', colors=['green', 'orange']) # 绘制饼图并显示百分比
plt.title('顾客性别比例', fontsize=16) # 设置标题
plt.axis('equal') # 确保饼图是圆形
plt.savefig("顾客信息.png") # 保存图形
#1、从顾客年龄组别的分布可以了解到,26-55岁间的顾客最多。
#2、从顾客性别的分布可以了解到,男性顾客比女性顾客多,男性占比60.4%,女性占比39.6%。
# 区域分布可视化
plt.figure(figsize=(20,5)) # 设置图形大小
sns.countplot(x='region', data=data, palette='Set2') # 绑制计数柱状图
plt.title('区域分布', fontsize=20) # 设置标题
plt.xlabel('区域', fontsize=16) # 设置x轴标签
plt.ylabel('数量', fontsize=16) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
plt.savefig("区域分布.png") # 保存图形
# 2.2 商品信息可视化
plt.figure(figsize=(20,5)) # 设置图形大小
# 产品名称分布柱状图
plt.subplot(1, 2, 1)
sns.countplot(x='product_name', data=data, palette='Set2') # 绘制产品名称分布柱状图
plt.xlabel('产品名称', fontsize=16) # 设置x轴标签
plt.ylabel('数量', fontsize=16) # 设置y轴标签
plt.title('产品分布', fontsize=20) # 设置标题
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 产品类型分布柱状图
plt.subplot(1, 2, 2)
sns.countplot(x='category', data=data, palette='Set2') # 绘制产品类型分布柱状图
plt.xlabel('产品类型', fontsize=16) # 设置x轴标签
plt.ylabel('数量', fontsize=16) # 设置y轴标签
plt.title('产品类型分布', fontsize=20) # 设置标题
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
plt.savefig("商品信息.png") # 保存图形
#总体而言,茶叶是销量最多的产品类型,远超其他产品类型。
#具体产品分布上,销量最多的产品是古田银耳,共1474单;其次是安溪铁观音,共1461单;第三名是宁德大黄鱼,共1438单。
# 销售相关指标分布可视化
plt.figure(figsize=(20,6)) # 设置图形大小
# 价格分布直方图
plt.subplot(1, 3, 1)
sns.distplot(data['price'], kde=True, color='blue') # 绘制价格分布直方图
plt.title('价格分布', fontsize=16) # 设置标题
plt.xlabel('价格', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 数量分布直方图
plt.subplot(1, 3, 2)
sns.distplot(data['quantity'], kde=True, color='green') # 绘制数量分布直方图
plt.title('数量分布', fontsize=16) # 设置标题
plt.xlabel('数量', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 销售额分布直方图
plt.subplot(1, 3, 3)
sns.distplot(data['sales_amount'], kde=True, color='orange') # 绘制销售额分布直方图
plt.title('销售额分布', fontsize=16) # 设置标题
plt.xlabel('销售额', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
plt.savefig("销售.png") # 保存图形
# 2.3 平台及优惠信息可视化
plt.figure(figsize=(20,12)) # 设置图形大小
# 平台分布柱状图
plt.subplot(2, 3, 1)
sns.countplot(x='channel', data=data, palette='Set2') # 绘制平台分布柱状图
plt.title('平台分布', fontsize=16) # 设置标题
plt.xlabel('平台', fontsize=14) # 设置x轴标签
plt.ylabel('频数', fontsize=14) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 平台比例饼图
plt.subplot(2, 3 ,2)
plt.pie(data['channel'].value_counts(), # 绑制饼图
labels=data['channel'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%') # 绘制平台比例饼图
plt.title('平台比例', fontsize=16) # 设置标题
# 优惠情况分布柱状图
plt.subplot(2,3, 3)
sns.countplot(x='promotion', data=data, palette='Set2') # 绘制优惠情况分布柱状图
plt.title('优惠情况分布', fontsize=16) # 设置标题
plt.xlabel('优惠情况', fontsize=14) # 设置x轴标签
plt.ylabel('频数', fontsize=14) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 优惠情况比例饼图
plt.subplot(2, 3, 4)
plt.pie(data['promotion'].value_counts(), # 绑制饼图
labels=data['promotion'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%') # 绘制优惠情况比例饼图
plt.title('优惠情况比例', fontsize=16) # 设置标题
# 是否返单分布柱状图
plt.subplot(2, 3, 5)
sns.countplot(x='return_flag', data=data, palette='Set2') # 绘制是否返单分布柱状图
plt.title('是否返单分布', fontsize=16) # 设置标题
plt.xlabel('是否返单', fontsize=14) # 设置x轴标签
plt.ylabel('频数', fontsize=14) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 是否返单比例饼图
plt.subplot(2, 3, 6)
plt.pie(data['return_flag'].value_counts(), # 绑制饼图
labels=data['return_flag'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%') # 绘制是否返单比例饼图
plt.title('是否返单比例', fontsize=16) # 设置标题
plt.savefig("平台及优惠信息.png") # 保存图形
#从平台的分布上可以了解到,淘宝订单数量是最多的,占比29.6%;京东平台订单数量第二,共2504单,占比25%;第三是拼多多平台;自由平台的订单数量最少。
#从优惠情况可以了解到,没有优惠的订单占比50.3%,有优惠的订单占比49.7%。其中,满减订单占比29.4%,折扣订单占比20.3%。
#是否返单的情况可以了解到,有返单的订单占比4.8%,没有返单的订单占比95.2%。# 注:processed_data.csv数据文件本地没有,但平台已经内置
# =============================================================================
# 题目: 农产品交易数据预处理
# =============================================================================
# 本代码块演示农产品电商交易数据的加载和初步检查。数据预处理是分析
# 的第一步,通过查看数据结构、类型和基本统计,我们可以快速了解数据
# 的质量,为后续分析奠定基础。
# ==================== 导入必要的库 ====================
import pandas as pd # 导入pandas库,用于数据操作和分析
import numpy as np # 导入numpy库,用于数值计算
import matplotlib.pyplot as plt # 导入matplotlib库,用于数据可视化
import seaborn as sns # 导入seaborn库,用于绘制高级统计图形
import warnings # 导入warnings库,用于处理警告信息
warnings.filterwarnings('ignore') # 忽略警告信息,保持输出整洁
# ==================== 设置中文显示 ====================
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# ==================== 读取数据 ====================
data = pd.read_csv('processed_data.csv') # 读取CSV文件到DataFrame
# ==================== 输出数据基本信息 ====================
print('数据形状:', data.shape) # 打印数据维度(行数,列数)
print('\n数据前5行:') # 打印标题
print(data.head()) # 打印前5行数据,快速浏览数据结构
print('\n数据信息:') # 打印标题
print(data.info()) # 打印数据信息(列名、数据类型、非空值数量)
print('\n描述统计:') # 打印标题
print(data.describe().T) # 打印描述统计(转置显示更易读)代码深度解析:
- 数据结构:
shape: 返回(行数, 列数)info(): 显示每列的数据类型和非空值数量describe().T: 转置描述统计表,更易阅读
- 数据类型检查:
- 数值型(int/float): 年龄、金额、数量
- 字符型(object): 产品名称、类别
- 日期型(datetime): 需要转换
- 中文配置:
SimHei(黑体): 常用中文字体unicode_minus=False: 修复负号显示问题
28.2.2 产品分类映射
补充说明:产品分类学
产品分类是电商数据分析的基础。常见的分类方法:
- 功能分类: 按产品用途分类
- 属性分类: 按产品属性分类
- 场景分类: 按使用场景分类
在农产品电商中,我们通常采用混合分类策略: - 一级分类: 茶叶、水果、水产品等大类 - 二级分类: 具体品种(铁观音、武夷岩茶) - 三级分类: 产地、等级等细分
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 农产品分类映射
# =============================================================================
# 本代码块演示如何将具体产品名称映射到标准分类体系。分类映射是数据清洗
# 的重要步骤,统一的产品分类便于后续的聚合分析和可视化。
# ==================== 产品名称到分类的映射 ====================
category_mapping = {
'安溪铁观音': '茶叶', # 安溪铁观音属于茶叶类
'武夷岩茶': '茶叶', # 武夷岩茶属于茶叶类
'福州茉莉花': '茶叶', # 福州茉莉花属于茶叶类
'古田银耳': '食用菌', # 古田银耳属于食用菌类
'建宁莲子': '中药材', # 建宁莲子属于中药材类
'琯溪蜜柚': '水果', # 琯溪蜜柚属于水果类
'宁德大黄鱼': '水产品' # 宁德大黄鱼属于水产品类
}
# ==================== 应用映射 ====================
# map()函数根据字典映射,将产品名称转换为分类
# fillna()保留未匹配的值,保持原分类不变
data['category'] = data['product_name'].map(category_mapping).fillna(data['category'])
# ==================== 验证映射结果 ====================
print('分类映射结果:') # 打印标题
print(data[['product_name', 'category']].drop_duplicates().head(10)) # 去重查看唯一映射
# ==================== 验证分类完整性 ====================
print('\n分类统计:') # 打印标题
print(data['category'].value_counts()) # 统计各分类的产品数量代码深度解析:
map()函数:- 根据字典进行键值映射
fillna(): 处理未匹配的值(保留原分类)- 比循环映射高效100倍以上
- 数据质量验证:
drop_duplicates(): 去重查看唯一映射value_counts(): 统计各分类产品数量
28.3 数据质量检查
28.3.1 缺失值分析
理论背景:缺失值的类型
根据Rubin(1976)的分类,缺失值分为三类:
- MCAR (Missing Completely At Random):
- 缺失与任何变量无关
- 完全随机发生
- 处理方法:直接删除或插补
- MAR (Missing At Random):
- 缺失与观测变量相关
- 例如:高价值客户更不愿填写收入
- 处理方法:多重插补
- MNAR (Missing Not At Random):
- 缺失与未观测变量相关
- 例如:极高收入者拒绝回答
- 处理方法:建模机制
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 数据质量检查与可视化
# =============================================================================
# 本代码块演示如何进行数据质量检查,包括缺失值分析、重复值检测等。
# 数据质量直接影响分析结果的可靠性,因此是数据预处理的关键环节。
# ==================== 缺失值检查 ====================
print('缺失值统计:') # 打印标题
missing_counts = data.isnull().sum() # 统计每列的缺失值数量
missing_pct = (missing_counts / len(data)) * 100 # 计算缺失值比例
missing_df = pd.DataFrame({ # 将结果整理为DataFrame
'缺失数': missing_counts, # 缺失值数量
'缺失比例': missing_pct # 缺失值百分比
})
print(missing_df) # 打印缺失值统计表
# ==================== 缺失值可视化 ====================
plt.figure(figsize=(10, 6)) # 创建图形,尺寸10×6英寸
missing_df['缺失比例'].plot(kind='bar', color='steelblue', alpha=0.7) # 绘制柱状图
plt.title('各字段缺失值比例', fontsize=14) # 设置标题
plt.xlabel('字段名', fontsize=12) # 设置x轴标签
plt.ylabel('缺失比例(%)', fontsize=12) # 设置y轴标签
plt.xticks(rotation=45) # x轴标签旋转45度
plt.grid(axis='y', alpha=0.3) # 显示y轴网格
plt.axhline(y=5, color='red', linestyle='--', label='5%阈值') # 添加5%阈值参考线
plt.legend() # 显示图例
plt.tight_layout() # 调整布局
plt.show() # 显示图形
# ==================== 重复值检查 ====================
print(f'\n重复值数量: {data.duplicated().sum()}') # 打印重复行数量
print(f'重复值比例: {data.duplicated().sum() / len(data) * 100:.2f}%') # 打印重复值比例
# ==================== 数据清洗 ====================
data_clean = data.drop_duplicates() # 删除重复行
print(f'\n清洗后数据形状: {data_clean.shape}') # 打印清洗后的数据维度
print(f'数据保留率: {len(data_clean) / len(data) * 100:.2f}%') # 计算数据保留率28.3.2 异常值检测
理论背景:异常值的识别方法
异常值(Outlier)指显著偏离其他观测值的数据点。常见识别方法:
- 基于统计量:
- 3σ原则: 超出均值±3倍标准差
- IQR方法: 超出Q1-1.5×IQR或Q3+1.5×IQR
- 基于可视化:
- 箱线图: 直观显示离群点
- 散点图: 识别偏离整体趋势的点
- 基于模型:
- DBSCAN聚类: 密度较低的点
- 孤立森林: 异常得分高的点
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 农产品价格异常值检测
# =============================================================================
# 本代码块演示如何使用箱线图和IQR方法检测异常值。异常值可能由数据录入错误、
# 测量误差或真实的极端情况造成,需要根据业务判断如何处理。
# ==================== 选择数值型字段 ====================
numeric_cols = data_clean.select_dtypes(include=[np.number]).columns # 选择数值类型的列
# ==================== 绘制箱线图 ====================
fig, axes = plt.subplots(2, 2, figsize=(14, 10)) # 创建2×2子图布局
axes = axes.flatten() # 将子图数组展平为一维
# 遍历前4个数值字段,绘制箱线图
for i, col in enumerate(numeric_cols[:4]): # 只取前4个字段
data_clean.boxplot(column=col, ax=axes[i]) # 绘制箱线图
axes[i].set_title(f'{col} - 箱线图', fontsize=12) # 设置子图标题
axes[i].grid(axis='y', alpha=0.3) # 显示y轴网格
plt.tight_layout() # 调整布局
plt.show() # 显示图形
# ==================== 统计异常值数量(IQR方法) ====================
def detect_outliers_iqr(series):
"""
使用IQR方法检测异常值
参数:
series: pandas Series,待检测的数据序列
返回:
异常值数量
"""
Q1 = series.quantile(0.25) # 计算第一四分位数(25%分位)
Q3 = series.quantile(0.75) # 计算第三四分位数(75%分位)
IQR = Q3 - Q1 # 计算四分位距
lower = Q1 - 1.5 * IQR # 计算下界(小于此值为异常值)
upper = Q3 + 1.5 * IQR # 计算上界(大于此值为异常值)
return ((series < lower) | (series > upper)).sum() # 返回异常值数量
# ==================== 输出异常值统计 ====================
print('\n异常值统计(IQR方法):') # 打印标题
for col in numeric_cols: # 遍历所有数值字段
outlier_count = detect_outliers_iqr(data_clean[col].dropna()) # 检测异常值数量
print(f'{col}: {outlier_count}个异常值 ({outlier_count/len(data_clean)*100:.2f}%)') # 打印结果28.4 数据分布分析
28.4.1 单变量分布
理论背景:分布的特征
描述数据分布的三个维度:
- 集中趋势:
- 均值: 所有值的平均
- 中位数: 排序后的中间值
- 众数: 出现频率最高的值
- 离散程度:
- 方差: 数据偏离均值的程度
- 标准差: 方差的平方根
- 极差: 最大值与最小值的差
- 分布形状:
- 偏度: 分布的对称性
- 峰度: 分布的尖锐程度
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 农产品销售额分布分析
# =============================================================================
# 本代码块演示如何分析销售额的分布特征。通过直方图和箱线图,我们可以
# 直观地观察数据的分布形态、中心趋势和离散程度,为业务决策提供依据。
# ==================== 提取销售额数据 ====================
sales = data_clean['sales_amount'] # 提取销售额列
# ==================== 绘制分布图 ====================
fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 创建1×2子图布局
# 子图1:直方图
axes[0].hist(sales, bins=50, color='steelblue', alpha=0.7, edgecolor='black') # 绘制直方图
# 添加均值参考线
axes[0].axvline(sales.mean(), color='red', linestyle='--', linewidth=2, label=f'均值={sales.mean():.2f}')
# 添加中位数参考线
axes[0].axvline(sales.median(), color='green', linestyle='--', linewidth=2, label=f'中位数={sales.median():.2f}')
axes[0].set_title('销售额分布直方图', fontsize=14) # 设置标题
axes[0].set_xlabel('销售额(元)', fontsize=12) # 设置x轴标签
axes[0].set_ylabel('频数', fontsize=12) # 设置y轴标签
axes[0].legend(fontsize=10) # 显示图例
axes[0].grid(axis='y', alpha=0.3) # 显示y轴网格
# 子图2:箱线图
axes[1].boxplot(sales, vert=True) # 绘制箱线图,垂直方向
axes[1].set_title('销售额箱线图', fontsize=14) # 设置标题
axes[1].set_ylabel('销售额(元)', fontsize=12) # 设置y轴标签
axes[1].grid(axis='y', alpha=0.3) # 显示y轴网格
plt.tight_layout() # 调整布局
plt.show() # 显示图形
# ==================== 计算分布统计量 ====================
from scipy.stats import skew, kurtosis # 导入偏度和峰度函数
print('\n销售额分布统计:') # 打印标题
print(f'均值: {sales.mean():.2f}') # 打印均值
print(f'中位数: {sales.median():.2f}') # 打印中位数
print(f'标准差: {sales.std():.2f}') # 打印标准差
print(f'偏度: {skew(sales):.4f}') # 打印偏度(正值右偏,负值左偏)
print(f'峰度: {kurtosis(sales):.4f}') # 打印峰度(正值尖峰,负值平峰)
# ==================== 偏度解读 ====================
if abs(skew(sales)) < 0.5: # 偏度绝对值小于0.5
skew_interpret = '接近对称分布' # 分布接近对称
elif skew(sales) > 0: # 偏度大于0
skew_interpret = '右偏分布(存在高值异常)' # 正偏(右偏),存在高值异常
else: # 偏度小于0
skew_interpret = '左偏分布(存在低值异常)' # 负偏(左偏),存在低值异常
print(f'\n偏度解读: {skew_interpret}') # 打印偏度解读28.4.2 类别分布
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 农产品品类分布分析
# =============================================================================
# 本代码块演示如何分析不同品类农产品的销售情况。通过聚合统计和可视化,
# 我们可以识别核心品类、辅助品类和长尾品类,为商品策略和库存管理提供依据。
# ==================== 按品类聚合统计 ====================
category_sales = data_clean.groupby('category')['sales_amount'].agg([ # 按分类分组,聚合销售额
'sum', # 计算总销售额
'count', # 计算订单数
'mean' # 计算平均单价
])
category_sales.columns = ['总销售额', '订单数', '平均单价'] # 重命名列
category_sales = category_sales.sort_values('总销售额', ascending=False) # 按总销售额降序排序
# ==================== 输出统计结果 ====================
print('\n品类销售统计:') # 打印标题
print(category_sales) # 打印品类统计表
# ==================== 可视化 ====================
fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 创建1×2子图布局
# 子图1:总销售额柱状图
category_sales['总销售额'].plot(kind='bar', ax=axes[0], color='steelblue', alpha=0.7) # 绘制柱状图
axes[0].set_title('各品类总销售额', fontsize=14) # 设置标题
axes[0].set_xlabel('产品类别', fontsize=12) # 设置x轴标签
axes[0].set_ylabel('销售额(元)', fontsize=12) # 设置y轴标签
axes[0].tick_params(axis='x', rotation=45) # x轴标签旋转45度
axes[0].grid(axis='y', alpha=0.3) # 显示y轴网格
# 子图2:订单数饼图
axes[1].pie(category_sales['订单数'], labels=category_sales.index, autopct='%1.1f%%',
startangle=90, colors=sns.color_palette('Set3')) # 绘制饼图
axes[1].set_title('各品类订单数占比', fontsize=14) # 设置标题
plt.tight_layout() # 调整布局
plt.show() # 显示图形28.5 时间序列初步分析
28.5.1 时间特征提取
补充说明:时间序列的特征
时间序列数据具有独特特征: 1. 趋势性(Trend): 长期上升或下降趋势 2. 季节性(Seasonality): 固定周期的波动 3. 周期性(Cyclicity): 不固定周期的波动 4. 随机性(Random): 不可预测的噪声
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 时间特征提取与分析
# =============================================================================
# 本代码块演示如何从日期时间字段中提取时间特征(年、月、日、星期、小时等)。
# 时间特征提取是时间序列分析的基础,有助于发现周期性模式和季节性规律。
# ==================== 确保日期格式正确 ====================
data_clean['下单日期'] = pd.to_datetime(data_clean['order_date']) # 转换为datetime类型
# ==================== 提取时间特征 ====================
data_clean['年'] = data_clean['下单日期'].dt.year # 提取年份
data_clean['月'] = data_clean['下单日期'].dt.month # 提取月份(1-12)
data_clean['日'] = data_clean['下单日期'].dt.day # 提取日(1-31)
data_clean['星期'] = data_clean['下单日期'].dt.dayofweek # 提取星期(0=周一,6=周日)
data_clean['小时'] = data_clean['下单日期'].dt.hour # 提取小时(0-23)
# ==================== 按月统计 ====================
monthly_sales = data_clean.groupby('月')['sales_amount'].agg([ # 按月份分组统计
'sum', # 总销售额
'count' # 订单数
])
monthly_sales.columns = ['月销售额', '月订单数'] # 重命名列
# ==================== 输出统计结果 ====================
print('月度销售统计:') # 打印标题
print(monthly_sales) # 打印月度统计表
# ==================== 可视化月度趋势 ====================
fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 创建1×2子图布局
monthly_sales['月销售额'].plot(ax=axes[0], marker='o', linewidth=2) # 绘制折线图
axes[0].set_title('月销售额趋势', fontsize=14) # 设置标题
axes[0].set_xlabel('月份', fontsize=12) # 设置x轴标签
axes[0].set_ylabel('销售额(元)', fontsize=12) # 设置y轴标签
axes[0].grid(True, alpha=0.3) # 显示网格
monthly_sales['月订单数'].plot(kind='bar', ax=axes[1], color='coral', alpha=0.7) # 绘制柱状图
axes[1].set_title('月订单数分布', fontsize=14) # 设置标题
axes[1].set_xlabel('月份', fontsize=12) # 设置x轴标签
axes[1].set_ylabel('订单数', fontsize=12) # 设置y轴标签
axes[1].grid(axis='y', alpha=0.3) # 显示y轴网格
plt.tight_layout() # 调整布局
plt.show() # 显示图形28.5.2 周末效应分析
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 周末效应分析
# =============================================================================
# 本代码块演示如何分析工作日与周末的消费差异。周末效应分析可以帮助
# 企业制定营销策略和库存管理计划,例如周末增加营销投入或调整物流配送。
# ==================== 标记周末 ====================
# isin([5, 6])判断星期是否为5(周六)或6(周日)
data_clean['是否周末'] = data_clean['星期'].isin([5, 6]) # 创建周末标记列
# ==================== 周末vs工作日对比 ====================
weekend_comparison = data_clean.groupby('是否周末')['sales_amount'].agg([ # 按是否周末分组
'mean', # 平均销售额
'sum', # 总销售额
'count' # 订单数
])
weekend_comparison.index = ['工作日', '周末'] # 重命名索引
# ==================== 输出对比结果 ====================
print('周末效应分析:') # 打印标题
print(weekend_comparison) # 打印对比表
# ==================== 可视化对比 ====================
fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 创建1×2子图布局
# 子图1:平均销售额对比
weekend_comparison['mean'].plot(kind='bar', ax=axes[0], color=['skyblue', 'orange'], alpha=0.7) # 绘制柱状图
axes[0].set_title('平均销售额对比', fontsize=14) # 设置标题
axes[0].set_ylabel('平均销售额(元)', fontsize=12) # 设置y轴标签
axes[0].tick_params(axis='x', rotation=0) # x轴标签不旋转
axes[0].grid(axis='y', alpha=0.3) # 显示y轴网格
# 子图2:总销售额对比
weekend_comparison['sum'].plot(kind='bar', ax=axes[1], color=['skyblue', 'orange'], alpha=0.7) # 绘制柱状图
axes[1].set_title('总销售额对比', fontsize=14) # 设置标题
axes[1].set_ylabel('总销售额(元)', fontsize=12) # 设置y轴标签
axes[1].tick_params(axis='x', rotation=0) # x轴标签不旋转
axes[1].grid(axis='y', alpha=0.3) # 显示y轴网格
plt.tight_layout() # 调整布局
plt.show() # 显示图形28.6 多变量关系探索
28.6.1 价格与销量的关系
理论背景:价格弹性
价格弹性(Price Elasticity)衡量需求量对价格变化的敏感度:
\[ E_p = \frac{\%\Delta Q}{\%\Delta P} = \frac{\partial Q}{\partial P} \times \frac{P}{Q} \]
- |Ep| > 1: 富有弹性(奢侈品)
- |Ep| < 1: 缺乏弹性(必需品)
- |Ep| = 1: 单位弹性
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 价格与销量关系分析
# =============================================================================
# 本代码块演示如何分析产品价格与销量之间的关系。价格-销量关系是
# 定价策略的核心,通过相关性分析和可视化,可以评估产品的价格弹性。
# ==================== 按产品统计价格与销量 ====================
product_stats = data_clean.groupby('product_name').agg({ # 按产品名称分组
'sales_amount': 'sum', # 总销售额
'quantity': 'sum', # 总销量
'unit_price': 'mean' # 平均单价
})
product_stats['单价'] = product_stats['sales_amount'] / product_stats['quantity'] # 计算实际单价
# ==================== 计算相关系数 ====================
corr_coef = product_stats[['单价', 'quantity']].corr().iloc[0, 1] # 计算价格与销量的相关系数
print(f'价格与销量的相关系数: {corr_coef:.4f}') # 打印相关系数
# ==================== 可视化 ====================
fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 创建1×2子图布局
# 子图1:散点图
axes[0].scatter(product_stats['单价'], product_stats['quantity'], s=100, alpha=0.6) # 绘制散点图
axes[0].set_title(f'价格-销量散点图 (相关系数={corr_coef:.4f})', fontsize=14) # 设置标题
axes[0].set_xlabel('单价(元)', fontsize=12) # 设置x轴标签
axes[0].set_ylabel('销量', fontsize=12) # 设置y轴标签
axes[0].grid(True, alpha=0.3) # 显示网格
# 添加产品标签
for idx, row in product_stats.iterrows(): # 遍历每个产品
axes[0].annotate(idx, (row['单价'], row['quantity']), fontsize=8) # 添加产品名称标签
# 子图2:热力图
sns.heatmap(product_stats[['单价', 'quantity']].corr(), annot=True, cmap='coolwarm',
center=0, ax=axes[1], cbar_kws={'label': '相关系数'}) # 绘制相关性热力图
axes[1].set_title('相关性热力图', fontsize=14) # 设置标题
plt.tight_layout() # 调整布局
plt.show() # 显示图形28.6.2 产品关联分析
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
# =============================================================================
# 题目: 产品共现分析
# =============================================================================
# 本代码块演示如何分析产品之间的关联关系。产品共现分析可以发现哪些
# 产品经常被一起购买,为捆绑销售、推荐系统和货架摆放提供依据。
# ==================== 导入组合函数 ====================
from itertools import combinations # 导入combinations函数,用于生成组合
# ==================== 获取每个订单的产品列表 ====================
order_products = data_clean.groupby('order_id')['product_name'].apply(list) # 按订单分组,获取产品列表
# ==================== 统计产品对 ====================
co_occurrence = {} # 创建空字典,存储产品对的共现次数
for products in order_products: # 遍历每个订单的产品列表
for combo in combinations(sorted(products), 2): # 生成所有2个产品的组合
co_occurrence[combo] = co_occurrence.get(combo, 0) + 1 # 统计共现次数
# ==================== 转换为DataFrame ====================
co_df = pd.DataFrame(list(co_occurrence.items()), columns=['产品对', '共现次数']) # 转换为DataFrame
co_df = co_df.sort_values('共现次数', ascending=False).head(10) # 按共现次数降序排序,取前10
# ==================== 输出结果 ====================
print('Top 10产品组合:') # 打印标题
print(co_df) # 打印共现次数最高的10个产品对
# ==================== 可视化 ====================
plt.figure(figsize=(12, 6)) # 创建图形
plt.barh(range(len(co_df)), co_df['共现次数'], color='steelblue', alpha=0.7) # 绘制水平柱状图
plt.yticks(range(len(co_df)), [f'{p1} + {p2}' for p1, p2 in co_df['产品对']]) # 设置y轴标签
plt.xlabel('共现次数', fontsize=12) # 设置x轴标签
plt.title('产品共现分析(Top 10)', fontsize=14) # 设置标题
plt.grid(axis='x', alpha=0.3) # 显示x轴网格
plt.tight_layout() # 调整布局
plt.show() # 显示图形