24  药品销售数据分析

24.1 引言药品销售数据分析的价值

医院药品销售分析: - 库存管理: 优化药品采购 - 处方分析: 医生开药习惯 - 医保分析: 社保使用情况 - 销售预测: 季节性需求

24.2 数据预处理

列表 24.1
# 注:朝阳医院2018年销售数据.xlsx数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import pandas as pd  # 导入Pandas数据分析库
import numpy as np  # 导入NumPy数值计算库
import seaborn as sns  # 导入Seaborn可视化库
import matplotlib.pyplot as plt  # 导入Matplotlib绑图库
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数

#数据读取
df = pd.read_excel("朝阳医院2018年销售数据.xlsx")

#数据预览与预处理
## 查看数据维度
print(df.shape)
print(df.head())  # 输出前几行数据

## 查看数据信息  
print(df.info())

df['社保卡号']=df['社保卡号'].astype(str)  # 转换数据类型
df['商品编码']=df['商品编码'].astype(str)  # 转换数据类型
df['购药日期']=df['购药时间'].str.split(pat=' ',expand=True)[0]  # 对字符串列进行处理
df['购药星期']=df['购药时间'].str.split(pat=' ',expand=True)[1]  # 对字符串列进行处理

## 查看各列的缺失值
df.isnull().sum()

## 缺失值处理
## 直接删除
df.dropna(axis=0,inplace=True)

## 查看重复值数量
df.duplicated().sum()

#探索性数据分析
##任务1:销售情况最好的药品是?最差的药品是?
##提示:通过对药品进行分组聚合统计,在此处我们评定销售情况好的标准是销售数量总和。之后取出前n个药品进行可视化展示

df_sum=df.groupby('商品名称')['实收金额'].sum().reset_index()  # 按指定列分组聚合
print(df_sum.nlargest(2,'实收金额')) #用于获取实收金额列中最大的两个值所对应的行。
print(df_sum.nsmallest(2,'实收金额')) #用于获取实收金额列中最小的两个值所对应的行。

df_sum_n10=df_sum.nlargest(10,'实收金额')  # 筛选出排名前10的记录
plt.figure(figsize=(15,10))  # 创建图形画布
plt.grid()  # 显示网格线
plt.bar(df_sum_n10['商品名称'],df_sum_n10['实收金额'])  # 绑制柱状图
plt.savefig("1.png")  # 保存图形至文件
plt.close()  # 关闭文件或连接

#任务2:整体销售额的时间变化趋势
##数据集中存在时间数据,但是不够简洁,所以需要先对时间数据进行处理:

df["购药时间_月"] = df["购药时间"].map(lambda x:x.split(" ")[0].split("-")[1]) ##购药日期所属月份   
df["购药时间_星期"] = df["购药时间"].map(lambda x:x.split(" ")[1]) ##购药当天是星期几? 

#对时间数据处理完成之后,就可以进行时间维度上的销售情况分析,与任务1一致,评定指标也选择为销售数量之和。

# 在下方开始你的分析
df['购药日期_月']=df['购药日期'].str.split(pat='-',expand=True)[1]
plt.figure(figsize=(15,10))  # 创建图形画布
plt.grid()  # 显示网格线
sns.lineplot(x='购药日期_月',y='实收金额',data=df,estimator='sum')  # 绑制折线图
plt.savefig("2.png")  # 保存图形至文件
plt.close()  # 关闭文件或连接

plt.figure(figsize=(15,10))  # 创建图形画布
plt.grid()  # 显示网格线
sns.lineplot(x='购药星期',y='实收金额',data=df,estimator='sum')  # 绑制折线图
plt.savefig("3.png")  # 保存图形至文件
plt.close()  # 关闭文件或连接

#任务3:各月份卖的最好的药品是那些?
values1=df['购药日期_月'].unique()
values1=pd.Series(values1)  # 创建Series序列values1
for value in values1:  # 遍历values1中的每个value
    df_value=df[df['购药日期_月']==value]  # 提取购药日期_月列作为df_value变量
  df_print=df_value.groupby(['购药日期_月','商品名称'])['实收金额'].sum().nlargest(1).reset_index()  # 按指定列分组聚合
    print(df_print)  # 输出数据框数据

#任务4:人们是否会更愿意购买可使用社保减免的药品?
df['差额']=df['应收金额']-df['实收金额']
df['是否社保减免药品']=df['差额'].apply(lambda x : '是' if x!=0 else '否')  # 定义匿名函数df['是否社保减免药品']
df['是否社保减免药品'].value_counts(normalize=True)  # 统计是否社保减免药品列各取值的频率分布

#####总结
# 卖的最好的是 开博通
# 卖的最差的是 TG厄贝沙坦片
# 药品的销售数量与时间有关
# 药品的销售数量与是否使用社保有关

24.3 任务1销售情况最好的药品

列表 24.2
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:识别销售情况最好和最差的药品
# =============================================================================
# 本代码按药品名称分组,统计每种药品的销售总额,识别销售冠军和销售垫底
# 的药品。这对库存管理和采购决策至关重要,畅销品需要保证库存,
# 滞销品可以考虑促销或淘汰。

# ==================== 按药品分组统计销售金额 ====================
# 按商品名称分组,计算每组的销售金额总和
df_sum = df.groupby('商品名称')['实收金额'].sum().reset_index()
# groupby('商品名称')按药品名称分组
# ['实收金额']选择要聚合的列(实际收到的金额)
# .sum()计算每组销售金额的总和
# .reset_index()将分组索引转换为列,便于后续处理
# 结果是一个DataFrame,包含两列:商品名称、销售总额

# ==================== 销售最好的药品 ====================
# 提取销售金额最高的2种药品
print('销售最好的2种药品:')
print(df_sum.nlargest(2, '实收金额'))  # nlargest(2, '实收金额')找出销售金额前2名
# nlargest()方法返回指定列最大的n行
# 可以快速识别销售冠军和亚军

# ==================== 销售最差的药品 ====================
# 提取销售金额最低的2种药品
print('\n销售最差的2种药品:')
print(df_sum.nsmallest(2, '实收金额'))  # nsmallest(2, '实收金额')找出销售金额后2名
# nsmallest()方法返回指定列最小的n行
# 识别滞销品,考虑促销或淘汰

# ==================== 可视化TOP10 ====================
# 提取销售金额TOP10的药品
df_sum_n10 = df_sum.nlargest(10, '实收金额')  # 提取销售金额前10名

# 创建画布,尺寸为15x10英寸
plt.figure(figsize=(15, 10))  # 大尺寸画布便于显示长药品名称

# 绘制柱状图
plt.bar(df_sum_n10['商品名称'], df_sum_n10['实收金额'], color='steelblue')
# 第一个参数是x轴数据(药品名称)
# 第二个参数是y轴数据(销售金额)
# color='steelblue'设置柱子颜色为钢蓝色

# 设置图表标题
plt.title('销售金额TOP10药品', fontsize=16)  # 标题字体大小16

# 设置x轴标签
plt.xlabel('药品名称', fontsize=12)  # x轴标签字体大小12

# 设置y轴标签
plt.ylabel('销售金额(元)', fontsize=12)  # y轴标签字体大小12,注明单位

# 旋转x轴刻度标签,便于阅读长药品名称
plt.xticks(rotation=45, ha='right')  # rotation=45表示旋转45度
# ha='right'表示右对齐,避免标签重叠

# 添加水平网格线,便于读取数值
plt.grid(axis='y', alpha=0.3)  # axis='y'表示仅y轴方向的网格线

# 自动调整布局
plt.tight_layout()  # 防止标签被截断

# 显示图表
plt.show()  # 展示完整的柱状图

# ==================== 输出解读 ====================
# 销售情况分析提供多个维度的商业洞察:
#
# 1. 销售冠军药品:
#    - 开博通等销售最好的药品是医院的收入支柱
#    - 需要保证这些药品的库存,避免缺货
#    - 可以与供应商谈判获取更优惠的采购价格
#
# 2. 滞销药品:
#    - TG厄贝沙坦片等销售最差的药品占用库存资金
#    - 考虑减少库存或淘汰这些药品
#    - 如果是必需药,需要分析销售不佳的原因
#
# 3. TOP10可视化:
#    - 柱状图直观展示TOP10药品的销售差距
#    - 可以快速识别销售层级(第一梯队、第二梯队)
#    - 为库存管理提供数据支持
#
# 4. 业务决策:
#    - 增加畅销品的库存深度
#    - 对滞销品进行促销或下架
#    - 分析销售冠军的共性(疗效、价格、科室)
#    - 优化药品结构,提升整体效益

24.4 任务2时间变化趋势

列表 24.3
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:销售额的时间序列趋势分析
# =============================================================================
# 本代码分析整体销售额随时间的变化趋势,包括月度趋势和星期趋势。
# 时间序列分析可以识别季节性规律、周期性波动和异常点,为销售预测
# 和资源规划提供依据。

# ==================== 提取月份特征 ====================
# 从购药日期列中提取月份
df['购药日期_月'] = df['购药日期'].str.split(pat='-', expand=True)[1]
# str.split('-')按短横线分割日期字符串
# expand=True将分割结果展开为DataFrame
# [1]取第二列(月份部分),例如:"2018-01-01" → "01"

# ==================== 绘制月度销售趋势 ====================
# 创建画布,尺寸为15x10英寸
plt.figure(figsize=(15, 10))

# 使用Seaborn绘制月度销售趋势线图
sns.lineplot(x='购药日期_月', y='实收金额', data=df, estimator='sum', errorbar=None)
# x='购药日期_月'指定x轴数据(月份)
# y='实收金额'指定y轴数据(销售金额)
# data=df指定数据源
# estimator='sum'表示对每个月的销售额求和
# errorbar=None表示不显示误差条(置信区间)

# 设置图表标题
plt.title('各月销售额变化趋势', fontsize=16)  # 标题说明分析内容

# 设置x轴标签
plt.xlabel('月份', fontsize=12)  # x轴表示月份(1-12)

# 设置y轴标签
plt.ylabel('销售金额(元)', fontsize=12)  # y轴表示销售金额,注明单位

# 添加网格线,便于读取数值
plt.grid(True, alpha=0.3)  # alpha=0.3设置网格线透明度

# 自动调整布局
plt.tight_layout()  # 防止标签被截断

# 显示图表
plt.show()  # 展示完整的月度趋势图

# ==================== 绘制星期销售趋势 ====================
# 创建新的画布
plt.figure(figsize=(15, 10))

# 使用Seaborn绘制星期销售趋势线图
sns.lineplot(x='购药星期', y='实收金额', data=df, estimator='sum', errorbar=None)
# x='购药星期'指定x轴数据(星期几)
# y='实收金额'指定y轴数据(销售金额)
# estimator='sum'表示对每个星期的销售额求和
# errorbar=None不显示误差条

# 设置图表标题
plt.title('各星期销售额变化趋势', fontsize=16)

# 设置x轴标签
plt.xlabel('星期', fontsize=12)  # x轴表示星期(星期一至星期日)

# 设置y轴标签
plt.ylabel('销售金额(元)', fontsize=12)

# 添加网格线
plt.grid(True, alpha=0.3)

# 自动调整布局
plt.tight_layout()

# 显示图表
plt.show()  # 展示完整的星期趋势图

# ==================== 输出解读 ====================
# 时间趋势分析揭示销售的时间规律:
#
# 1. 月度趋势:
#    - 识别季节性规律:某些月份销售显著高于其他月份
#    - 可能原因:流感季(冬春)、过敏季(春季)、寒暑假(学生购药减少)
#    - 用途:根据预测调整库存,避免缺货或积压
#
# 2. 星期趋势:
#    - 工作日vs周末:工作日销售可能更高(上班族就诊)
#    - 周初vs周末:周一可能最高(周末积累的病人)
#    - 用途:合理安排医生排班和药房人手
#
# 3. 异常点:
#    - 突然的峰值:可能因疫情爆发、流感流行
#    - 突然的低谷:可能因节假日、医院停诊
#    - 需要结合外部事件分析
#
# 4. 业务决策:
#    - 库存管理:高峰月提前备货
#    - 人员安排:高峰日增加人手
#    - 营销策略:淡季推出促销活动
#    - 预测模型:基于历史数据预测未来销售

24.5 任务3各月份最佳药品

列表 24.4
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:识别每个月的销售冠军药品
# =============================================================================
# 本代码分析每个月销售最好的药品,识别季节性畅销品。不同季节可能
# 伴随不同的疾病,导致相关药品销售上升。这种分析可以指导季节性
# 备货和营销策略。

# ==================== 获取所有月份 ====================
# 提取购药日期_月列的所有唯一值(去重后的月份列表)
months = df['购药日期_月'].unique()  # unique()返回唯一值数组
# 例如:['01', '02', '03', ..., '12']

# ==================== 遍历每个月,找出最佳药品 ====================
for month in sorted(months):  # sorted()对月份排序,确保按1-12的顺序输出
    # 筛选该月份的所有销售记录
    df_month = df[df['购药日期_月'] == month]  # 布尔索引,筛选特定月份的行

    # 按月份和商品名称分组,计算销售金额,找出第一名
    df_best = df_month.groupby(['购药日期_月', '商品名称'])['实收金额'].sum().nlargest(1).reset_index()
    # groupby(['购药日期_月', '商品名称'])按月份和药品分组
    # ['实收金额']选择要聚合的列
    # .sum()计算每组的销售总额
    # .nlargest(1)提取销售金额最大的1行(即该月的销售冠军)
    # .reset_index()重置索引

    # 打印该月的最佳药品
    print(f'{month}月最佳药品:')
    print(df_best)  # 显示月份、药品名称、销售金额
    print()  # 打印空行,分隔不同月份的输出

# ==================== 输出解读 ====================
# 各月最佳药品分析揭示季节性用药规律:
#
# 1. 季节性疾病:
#    - 冬季(12-2月):感冒药、退烧药销售冠军
#    - 春季(3-5月):抗过敏药销售冠军
#    - 夏季(6-8月):消化药、防暑药销售冠军
#    - 秋季(9-11月):止咳药、抗生素销售冠军
#
# 2. 备货策略:
#    - 提前1个月备货季节性药品
#    - 避免高峰期缺货影响患者
#    - 减少非季节性药品的库存积压
#
# 3. 营销策略:
#    - 在季节到来前推出相关促销
#    - 制作季节性用药指南,指导患者
#    - 与医生沟通,提前处方调整
#
# 4. 采购决策:
#    - 与供应商签订季节性供货协议
#    - 确保高峰期的药品供应
#    - 争取批量采购的折扣

24.6 任务4社保减免分析

列表 24.5
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:分析社保减免对药品购买意愿的影响
# =============================================================================
# 本代码通过对比应收金额和实收金额,判断药品是否使用了社保减免,
# 并统计使用社保减免的药品占比。这有助于了解患者对医保政策的响应,
# 以及医保政策对药品销售的影响。

# ==================== 计算差额 ====================
# 计算应收金额和实收金额的差额
df['差额'] = df['应收金额'] - df['实收金额']
# 应收金额:药品原价
# 实收金额:患者实际支付的金额
# 差额:社保报销的金额
# 差额>0表示使用了社保减免,差额=0表示未使用

# ==================== 判断是否使用社保 ====================
# 根据差额判断是否使用社保减免
df['是否社保减免药品'] = df['差额'].apply(lambda x: '是' if x != 0 else '否')
# apply()函数对Series的每个元素应用指定函数
# lambda x: '是' if x != 0 else '否'是一个匿名函数
# 如果差额不为0,返回'是';否则返回'否'
# 结果是一个新的分类变量

# ==================== 统计社保减免比例 ====================
# 计算'是'和'否'的数量和占比
insurance_ratio = df['是否社保减免药品'].value_counts(normalize=True)
# value_counts()计算每个类别的数量
# normalize=True返回占比而非绝对数量
# 结果格式: {'是': 0.75, '否': 0.25}

# 打印社保减免药品的占比
print('社保减免药品占比:')
print(insurance_ratio)  # 显示'是'和'否'的占比

# 提取'是'的占比,并转换为百分比
print(f'\n使用社保减免的药品占比: {insurance_ratio.get("是", 0)*100:.1f}%')
# insurance_ratio.get("是", 0)获取'是'的占比,如果不存在则返回0
# * 100转换为百分比
# :.1f保留1位小数

# ==================== 输出解读 ====================
# 社保减免分析揭示医保政策的影响:
#
# 1. 占比解读:
#    - 如果社保减免占比很高(如75%以上),说明:
#      - 患者倾向于使用医保购买药品
#      - 医保政策覆盖面广,患者受益大
#      - 药品大部分在医保目录内
#
# 2. 患者行为:
#    - 价格敏感度:有医保减免时,患者更愿意购买
#    - 药品选择:优先选择医保目录内的药品
#    - 购买意愿:医保降低了经济负担,增加了用药需求
#
# 3. 医院策略:
#    - 药品采购:优先采购医保目录内的药品
#    - 处方管理:医生优先开医保药品,提高患者满意度
#    - 库存管理:医保药品库存要充足
#
# 4. 政策影响:
#    - 医保目录调整会影响药品销售
#    - 报销比例变化会影响患者选择
#    - 需要密切关注医保政策变化
#
# 5. 商业机会:
#    - 对于非医保药品,需要突出其独特价值
#    - 可以开发医保目录外的替代药品
#    - 提供自费患者的增值服务

## 分析结论

1. **销售冠军**: 开博通
2. **销售垫底**: TG厄贝沙坦片
3. **季节性**: 药品销售与时间相关
4. **医保影响**: 社保减免显著影响购买意愿