Project P01: 朝阳医院销售数据分析¶
本项目通过分析医院销售数据,掌握数据清洗、探索性分析、可视化等核心技能。
📋 项目背景¶
业务场景: 朝阳医院是一家综合性医疗机构,希望通过分析药品销售数据优化库存管理、发现销售趋势、提升运营效率。
数据来源: 医院信息系统(HIS)导出的销售明细数据,包含50万条销售记录,时间跨度2022-2024年。
业务目标: 1. 清洗数据,处理缺失值和异常值 2. 分析销售趋势,识别畅销和滞销药品 3. 按类别、时间维度进行多维度分析 4. 生成可视化报告,辅助管理层决策
🎯 学习目标¶
完成本项目后,你将掌握: - ✅ 数据清洗: 处理缺失值、重复值、异常值的实用方法 - ✅ 探索性分析: 使用Pandas进行分组聚合、透视表分析 - ✅ 时间序列: 分析销售趋势,按月/季度/年度汇总 - ✅ 数据可视化: 使用Matplotlib/Seaborn创建专业图表 - ✅ 指标计算: 销售额、增长率、同比环比等业务指标
📊 数据说明¶
数据集基本信息¶
- 文件名:
hospital_sales.csv - 数据路径:
data/stage3/hospital_sales.csv - 文件大小: 52MB
- 记录数: 500,000 条
- 字段数: 18 个
- 时间范围: 2022-01-01 至 2024-12-31
字段说明¶
| 字段名 | 数据类型 | 说明 | 示例值 |
|---|---|---|---|
| order_id | string | 订单编号 | ORD20220101001 |
| order_date | datetime | 订单日期 | 2022-01-15 |
| product_name | string | 药品名称 | 阿莫西林胶囊 |
| category | string | 药品分类 | 抗生素 |
| quantity | integer | 销售数量 | 50 |
| unit_price | float | 单价(元) | 12.50 |
| total_amount | float | 总金额(元) | 625.00 |
| customer_type | string | 客户类型 | 个人/机构 |
| department | string | 科室 | 内科 |
| doctor_name | string | 医生姓名 | 张医生 |
| manufacturer | string | 生产厂家 | XX制药 |
| batch_number | string | 批次号 | B202201 |
| expiry_date | datetime | 有效期 | 2025-01-01 |
| payment_method | string | 支付方式 | 医保/自费 |
| discount_rate | float | 折扣率 | 0.95 |
| sales_rep | string | 销售代表 | 李代表 |
| region | string | 地区 | 北京市朝阳区 |
| notes | string | 备注 | (可能为空) |
数据质量¶
- 缺失值: 0.5% (主要在notes字段)
- 重复值: 0.2% (少量重复订单)
- 异常值: 1% (单价或数量异常)
- 数据类型: 需要转换order_date和expiry_date为日期类型
🚀 快速开始¶
环境要求¶
- Python 3.9+
- 依赖包: pandas, numpy, matplotlib, seaborn
安装依赖¶
# 在项目根目录
pip install -e ".[stage3]"
# 或在P01目录使用pyproject.toml
cd docs/stage3/projects/p01-healthcare
pip install -e .
运行分析¶
方法1: Python脚本¶
# 运行完整分析
python src/analyze.py
# 指定自定义配置
python src/analyze.py --config configs/custom.yaml
# 仅生成图表
python src/analyze.py --visualize-only
方法2: Jupyter Notebook¶
📂 项目结构¶
p01-healthcare/
├── README.md # 项目说明文档
├── pyproject.toml # 依赖配置
├── src/
│ ├── __init__.py
│ ├── analyze.py # 主分析脚本
│ ├── data_loader.py # 数据加载模块
│ ├── cleaner.py # 数据清洗模块
│ └── visualizer.py # 可视化模块
├── notebooks/
│ └── analysis.ipynb # 交互式分析笔记本
├── configs/
│ └── default.yaml # 默认配置
├── tests/
│ └── test_analyze.py # 单元测试
└── outputs/ # 输出目录(自动生成)
├── figures/ # 图表
├── reports/ # 分析报告
└── processed_data/ # 清洗后数据
🔍 分析流程¶
Step 1: 数据加载与初步检查¶
import pandas as pd
# 加载数据
df = pd.read_csv('data/stage3/hospital_sales.csv')
# 基本信息
print(df.info())
print(df.describe())
print(df.head())
检查内容: - 数据形状(行数、列数) - 数据类型是否正确 - 缺失值统计 - 基础统计信息
Step 2: 数据清洗¶
处理缺失值:
# 检查缺失值
print(df.isnull().sum())
# 填充或删除缺失值
df['notes'].fillna('无备注', inplace=True)
df.dropna(subset=['product_name', 'total_amount'], inplace=True)
处理重复值:
# 检查重复订单
duplicates = df.duplicated(subset=['order_id'])
print(f"重复订单数: {duplicates.sum()}")
# 删除重复
df.drop_duplicates(subset=['order_id'], keep='first', inplace=True)
处理异常值:
# 异常单价检测(使用IQR方法)
Q1 = df['unit_price'].quantile(0.25)
Q3 = df['unit_price'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记或删除异常值
outliers = df[(df['unit_price'] < lower_bound) | (df['unit_price'] > upper_bound)]
print(f"异常值数量: {len(outliers)}")
数据类型转换:
# 转换日期字段
df['order_date'] = pd.to_datetime(df['order_date'])
df['expiry_date'] = pd.to_datetime(df['expiry_date'])
# 提取日期特征
df['year'] = df['order_date'].dt.year
df['month'] = df['order_date'].dt.month
df['quarter'] = df['order_date'].dt.quarter
df['day_of_week'] = df['order_date'].dt.dayofweek
Step 3: 探索性数据分析(EDA)¶
总体销售情况:
# 总销售额
total_sales = df['total_amount'].sum()
print(f"总销售额: {total_sales:,.2f} 元")
# 平均单价
avg_price = df['unit_price'].mean()
print(f"平均单价: {avg_price:.2f} 元")
# 总订单数
total_orders = len(df)
print(f"总订单数: {total_orders:,}")
按类别分析:
# 各类别销售额
category_sales = df.groupby('category')['total_amount'].sum().sort_values(ascending=False)
print(category_sales)
# 各类别销售占比
category_ratio = category_sales / total_sales * 100
print(category_ratio)
时间趋势分析:
# 按月汇总销售额
monthly_sales = df.groupby(df['order_date'].dt.to_period('M'))['total_amount'].sum()
print(monthly_sales)
# 同比增长率
yearly_sales = df.groupby('year')['total_amount'].sum()
growth_rate = yearly_sales.pct_change() * 100
print(f"年度增长率:\n{growth_rate}")
畅销药品TOP10:
# 按销售额排名
top10_products = df.groupby('product_name')['total_amount'].sum().sort_values(ascending=False).head(10)
print(top10_products)
# 按销量排名
top10_volume = df.groupby('product_name')['quantity'].sum().sort_values(ascending=False).head(10)
print(top10_volume)
Step 4: 数据可视化¶
销售趋势图:
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 月度销售趋势
plt.figure(figsize=(12, 6))
monthly_sales.plot(kind='line', marker='o')
plt.title('月度销售趋势', fontsize=16)
plt.xlabel('月份')
plt.ylabel('销售额(元)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('outputs/figures/monthly_trend.png', dpi=300)
plt.show()
类别占比饼图:
plt.figure(figsize=(10, 8))
category_sales.plot(kind='pie', autopct='%1.1f%%', startangle=90)
plt.title('各类别销售额占比', fontsize=16)
plt.ylabel('')
plt.tight_layout()
plt.savefig('outputs/figures/category_pie.png', dpi=300)
plt.show()
TOP10药品柱状图:
plt.figure(figsize=(12, 6))
top10_products.plot(kind='barh')
plt.title('销售额TOP10药品', fontsize=16)
plt.xlabel('销售额(元)')
plt.ylabel('药品名称')
plt.tight_layout()
plt.savefig('outputs/figures/top10_products.png', dpi=300)
plt.show()
热力图:
# 按月份和类别的销售热力图
pivot_table = df.pivot_table(
values='total_amount',
index='month',
columns='category',
aggfunc='sum'
)
plt.figure(figsize=(12, 8))
sns.heatmap(pivot_table, annot=True, fmt='.0f', cmap='YlOrRd')
plt.title('月度-类别销售热力图', fontsize=16)
plt.tight_layout()
plt.savefig('outputs/figures/heatmap.png', dpi=300)
plt.show()
Step 5: 生成报告¶
# 生成汇总报告
report = f"""
# 朝阳医院销售数据分析报告
## 数据概览
- 分析周期: {df['order_date'].min()} 至 {df['order_date'].max()}
- 订单总数: {len(df):,}
- 总销售额: {total_sales:,.2f} 元
- 平均订单金额: {df['total_amount'].mean():.2f} 元
## 主要发现
1. 销售额最高的类别是: {category_sales.index[0]} ({category_sales.iloc[0]:,.2f}元)
2. 畅销药品TOP3: {', '.join(top10_products.index[:3])}
3. 年度同比增长率: {growth_rate.iloc[-1]:.2f}%
## 建议
1. 加强畅销药品库存管理
2. 关注滞销类别,考虑促销或调整采购
3. 根据季节性趋势优化库存策略
"""
with open('outputs/reports/summary_report.md', 'w', encoding='utf-8') as f:
f.write(report)
print("报告已生成: outputs/reports/summary_report.md")
📈 评估指标¶
本项目的完成质量通过以下指标评估:
| 指标 | 目标值 | 说明 |
|---|---|---|
| 数据清洗 | 100% | 无缺失值、重复值、异常值 |
| 分析维度 | ≥5个 | 时间、类别、药品、科室、地区 |
| 可视化图表 | ≥6个 | 趋势图、饼图、柱状图、热力图等 |
| 指标计算 | ≥8个 | 销售额、增长率、占比、TOP10等 |
| 代码质量 | 通过测试 | 函数化、注释清晰、可复现 |
💡 扩展思考¶
完成基础分析后,可以尝试以下进阶任务:
- 时间序列预测: 使用ARIMA或Prophet预测未来销售趋势
- 客户细分: 基于RFM模型分析客户价值
- 关联规则: 使用Apriori算法发现药品关联销售模式
- 异常检测: 识别异常订单和销售行为
- 交互式仪表盘: 使用Plotly Dash创建动态仪表盘
🐛 常见问题¶
Q1: 数据文件找不到?¶
A: 确保已运行python scripts/data/download-stage3.py下载数据,或检查路径是否正确。
Q2: 中文显示为方框?¶
A: 配置matplotlib中文字体:
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'Songti SC']
plt.rcParams['axes.unicode_minus'] = False
Q3: 内存不足?¶
A: 使用分块读取:
chunks = pd.read_csv('data/stage3/hospital_sales.csv', chunksize=50000)
for chunk in chunks:
process(chunk)
Q4: 如何保存清洗后的数据?¶
A: 使用Parquet格式节省空间:
📚 参考资源¶
✅ 检查清单¶
完成项目前,请确认: - [ ] 成功加载数据并检查基本信息 - [ ] 完成数据清洗(缺失值、重复值、异常值) - [ ] 计算至少8个业务指标 - [ ] 生成至少6个可视化图表 - [ ] 创建汇总分析报告 - [ ] 代码通过测试并能复现结果 - [ ] 理解每一步的业务含义
项目完成时间: 预计2-4小时
难度等级: ⭐⭐ 入门
前置知识: Pandas基础、Matplotlib基础
下一个项目: P02 - 电商用户画像
最后更新: 2025-11-12 维护者: py_ai_tutorial团队