Python药店销售数据分析实战项目
简介:本项目通过Python对药店销售数据进行全流程分析,涵盖数据预处理、探索、清洗、趋势分析、关联挖掘、客户聚类及销售预测。项目包含数据集、源码和说明文档,适合用于课程设计与大型作业。学习者将掌握Pandas、NumPy、Matplotlib、Seaborn等工具的使用,提升数据建模与商业洞察力,为零售决策提供数据支持。 
1. Python数据分析流程概述
数据分析是挖掘数据价值的核心过程,Python凭借其丰富的库和简洁语法,成为数据分析领域的主流工具。本章将概述使用Python进行数据分析的完整流程,从数据获取、清洗、探索、建模到可视化呈现,构建对药店销售数据的系统性理解。
我们将重点介绍Pandas用于数据处理、NumPy进行数值运算、Matplotlib与Seaborn实现数据可视化等关键技术,结合药店销售场景,帮助读者理解不同工具在实际问题中的应用逻辑。通过本章学习,可为后续深入分析销售趋势、客户行为与门店运营打下坚实基础。
2. 药店销售数据集字段解析
2.1 数据集来源与结构
2.1.1 数据来源与采集方式
药店销售数据集的来源通常包括内部业务系统、第三方数据平台、API接口或历史报表文件。这些数据可能来自ERP系统、POS销售终端、库存管理系统,或是通过爬虫程序从公开渠道获取。采集方式上,常见的方法包括:
- 数据库导出 :如从MySQL、PostgreSQL等数据库中导出CSV或Excel格式。
- API接口获取 :通过RESTful API或GraphQL接口获取结构化数据。
- 日志文件解析 :从销售系统日志中提取关键字段。
- 爬虫抓取 :利用Scrapy、BeautifulSoup等工具从网页抓取数据。
以数据库导出为例,我们可以使用如下SQL语句将销售数据导出为CSV格式:
SELECT * FROM sales_records
INTO OUTFILE '/var/lib/mysql-files/sales_data.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
逻辑分析与参数说明:
- SELECT * FROM sales_records :从 sales_records 表中提取所有记录。
- INTO OUTFILE :指定导出文件路径。
- FIELDS TERMINATED BY ',' :字段以逗号分隔。
- ENCLOSED BY '"' :字符串使用双引号包裹。
- LINES TERMINATED BY '\n' :每行以换行符结束。
导出后,可以使用Python中的 pandas 库进行加载:
import pandas as pd
df_sales = pd.read_csv('sales_data.csv')
print(df_sales.head())
2.1.2 数据字段的命名规范与含义
药店销售数据集通常包含多个字段,命名规范通常采用 小写字母+下划线 的形式,如 sale_date 、 product_name 等。以下是常见的字段及其含义:
| 字段名 | 类型 | 含义说明 |
|---|---|---|
sale_id |
整数型 | 销售记录唯一标识 |
sale_date |
日期型 | 销售发生日期 |
product_id |
整数型 | 药品唯一标识 |
product_name |
字符串型 | 药品名称 |
price |
浮点型 | 药品单价 |
quantity |
整数型 | 销售数量 |
customer_id |
整数型 | 客户唯一标识 |
customer_type |
字符串型 | 客户类型(普通/会员) |
store_id |
整数型 | 门店编号 |
promotion |
布尔型 | 是否参与促销活动 |
这些字段构成了药店销售数据的基本结构,为后续分析提供了基础。
2.2 核心字段分析
2.2.1 销售记录字段(如时间、药品名称、价格、销量)
销售记录字段是数据分析的核心部分,主要包括时间、药品名称、价格、销量等。这些字段直接反映了销售行为的发生情况。
例如,我们可以使用 pandas 对销售时间进行解析和提取周几信息:
df_sales['sale_date'] = pd.to_datetime(df_sales['sale_date'])
df_sales['weekday'] = df_sales['sale_date'].dt.weekday_name
逻辑分析与参数说明:
- pd.to_datetime() :将字符串日期转换为标准时间格式。
- dt.weekday_name :提取销售发生的星期几。
通过分析销售时间,我们可以识别销售高峰期,为库存调度和促销安排提供依据。
2.2.2 客户信息字段(如客户类型、地理位置)
客户信息字段包括客户类型(如普通客户、会员)和地理位置(如省、市、区)。这些字段有助于分析客户行为和区域销售表现。
例如,我们可以统计不同客户类型的购买频次:
customer_type_counts = df_sales.groupby('customer_type')['sale_id'].count()
print(customer_type_counts)
逻辑分析与参数说明:
- groupby('customer_type') :按客户类型分组。
- count() :统计每种客户类型的销售记录数。
2.2.3 店铺运营字段(如门店编号、促销信息)
店铺运营字段包括门店编号、是否参与促销活动等。这些字段用于分析门店绩效和促销效果。
例如,我们可以计算每个门店的总销售额:
df_sales['total_sales'] = df_sales['price'] * df_sales['quantity']
store_sales = df_sales.groupby('store_id')['total_sales'].sum()
print(store_sales)
逻辑分析与参数说明:
- price * quantity :计算每条记录的销售额。
- groupby('store_id') :按门店编号分组。
- sum() :汇总每个门店的总销售额。
2.3 字段间关系建模
2.3.1 数据表的主键与外键设计
药店销售数据通常由多个表组成,如销售表、客户表、药品表、门店表等。这些表之间通过主键和外键建立关联。
- 主键(Primary Key) :唯一标识表中一条记录,如
sale_id、product_id。 - 外键(Foreign Key) :用于关联其他表的主键,如
product_id在销售表中作为外键关联药品表。
例如,在MySQL中定义销售表与药品表的关系:
CREATE TABLE sales_records (
sale_id INT PRIMARY KEY,
sale_date DATE,
product_id INT,
quantity INT,
price DECIMAL(10, 2),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
逻辑分析与参数说明:
- FOREIGN KEY (product_id) :定义外键。
- REFERENCES products(product_id) :关联药品表的主键。
2.3.2 多表关联逻辑与JOIN操作基础
在实际分析中,我们经常需要将多个表连接起来。常见的JOIN操作包括INNER JOIN、LEFT JOIN等。
例如,使用SQL将销售表与药品表进行INNER JOIN:
SELECT s.sale_id, p.product_name, s.quantity, s.price
FROM sales_records s
INNER JOIN products p ON s.product_id = p.product_id;
逻辑分析与参数说明:
- INNER JOIN :只返回两个表中匹配的记录。
- ON s.product_id = p.product_id :连接条件为药品ID相同。
在Python中,也可以使用 merge 函数进行JOIN操作:
df_merged = pd.merge(df_sales, df_products, on='product_id', how='inner')
逻辑分析与参数说明:
- on='product_id' :按药品ID连接。
- how='inner' :使用INNER JOIN方式。
2.4 数据字段的初步统计特征
2.4.1 各字段的分布情况
了解各字段的分布情况有助于识别数据特征和异常值。我们可以使用 describe() 函数快速获取数值型字段的统计信息:
print(df_sales.describe())
输出结果包括:
- count :记录数量
- mean :平均值
- std :标准差
- min/max :最小值/最大值
- 25%/50%/75% :分位数
对于非数值型字段,如药品名称,可以使用 value_counts() 查看分布:
print(df_sales['product_name'].value_counts())
2.4.2 常见异常值与无效数据识别
异常值可能包括价格为负、销量为零或极大值。我们可以使用箱线图(Boxplot)识别异常值:
import matplotlib.pyplot as plt
plt.boxplot(df_sales['price'])
plt.title('Price Distribution')
plt.ylabel('Price')
plt.show()
逻辑分析与参数说明:
- boxplot() :绘制箱线图。
- 异常值通常显示为图中的离群点。
对于无效数据,如药品名称为空,我们可以使用如下方式识别:
invalid_rows = df_sales[df_sales['product_name'].isnull()]
print(invalid_rows)
逻辑分析与参数说明:
- isnull() :检测空值。
- 返回包含空药品名称的记录。
数据字段关系流程图(Mermaid)
erDiagram
SALES_RECORDS ||--o{ PRODUCTS : "product_id"
SALES_RECORDS ||--o{ CUSTOMERS : "customer_id"
SALES_RECORDS ||--o{ STORES : "store_id"
PRODUCTS {
int product_id
string product_name
string category
}
CUSTOMERS {
int customer_id
string customer_type
string location
}
STORES {
int store_id
string city
string region
}
SALES_RECORDS {
int sale_id
date sale_date
int product_id
int customer_id
int store_id
float price
int quantity
boolean promotion
}
本章系统地介绍了药店销售数据集的字段构成、结构来源、核心字段分析、字段间关系建模以及初步统计特征。通过这些分析,我们不仅掌握了数据的基本情况,还为后续的数据清洗和建模打下了坚实基础。
3. 数据预处理与缺失值处理
在数据分析流程中,数据预处理是至关重要的一环。原始数据往往存在缺失值、异常值、格式不一致等问题,这些问题会严重影响后续分析的准确性与模型的性能。因此,本章将围绕药店销售数据集,系统讲解数据预处理的关键步骤,包括数据清洗、缺失值处理、数据类型转换与标准化,以及数据分组与聚合等内容,为后续的深入分析奠定坚实基础。
3.1 数据清洗流程
数据清洗是数据预处理的第一步,其主要目标是识别并修正数据集中存在的错误和不一致问题,包括重复记录、异常值、非法格式等。
3.1.1 重复数据的识别与删除
在药店销售数据中,重复记录可能来源于数据采集过程中的错误或系统故障。重复记录不仅浪费存储空间,还会导致统计结果偏高,影响分析的准确性。
操作步骤:
import pandas as pd
# 加载销售数据
df_sales = pd.read_csv('drug_sales.csv')
# 检查重复记录
duplicate_rows = df_sales[df_sales.duplicated()]
print(f"发现 {len(duplicate_rows)} 条重复记录")
# 删除重复记录
df_sales_cleaned = df_sales.drop_duplicates()
代码逻辑分析:
df_sales.duplicated()返回一个布尔序列,标记每一行是否为重复记录。drop_duplicates()函数默认保留第一次出现的记录,其余重复行被删除。- 若需根据特定字段去重,可传入
subset=['字段名']参数。
3.1.2 异常值的检测与处理
异常值是指明显偏离正常范围的数据点,可能由输入错误、测量误差或极端事件引起。在药店销售数据中,异常值常见于价格、销量等字段。
检测方法:
- Z-score 方法 :适用于正态分布数据。
- IQR 方法 :适用于非正态分布数据。
示例代码:
# 使用IQR法检测销量字段的异常值
Q1 = df_sales_cleaned['销量'].quantile(0.25)
Q3 = df_sales_cleaned['销量'].quantile(0.75)
IQR = Q3 - Q1
# 定义异常值范围
outlier_mask = (df_sales_cleaned['销量'] < (Q1 - 1.5 * IQR)) | (df_sales_cleaned['销量'] > (Q3 + 1.5 * IQR))
# 打印异常值
outliers = df_sales_cleaned[outlier_mask]
print(f"发现 {len(outliers)} 条销量异常记录")
参数说明:
quantile()用于计算分位数。IQR = Q3 - Q1是四分位距。- 通常认为超出
Q1 - 1.5*IQR和Q3 + 1.5*IQR的值为异常值。
处理策略:
- 删除异常记录 :适用于数据量充足且异常值比例较小的情况。
- 替换为上下限值 :适用于需要保留记录但修正极端值的情况。
- 使用中位数填充 :适用于异常值可视为缺失处理的情况。
3.2 缺失值的识别与处理
缺失值是数据分析中常见的问题,处理不当将影响模型的训练和预测效果。本节将介绍如何识别缺失值,并探讨多种处理策略。
3.2.1 缺失值的统计方法
首先需要识别数据集中哪些字段存在缺失值,并统计其缺失比例。
示例代码:
# 查看各字段缺失值数量
missing_values = df_sales_cleaned.isnull().sum()
# 计算缺失比例
missing_ratio = (missing_values / len(df_sales_cleaned)) * 100
# 合并输出
missing_df = pd.DataFrame({'缺失值数量': missing_values, '缺失比例(%)': missing_ratio})
print(missing_df)
输出示例:
| 字段名 | 缺失值数量 | 缺失比例(%) |
|---|---|---|
| 价格 | 23 | 0.15 |
| 客户类型 | 0 | 0.00 |
| 门店编号 | 5 | 0.03 |
逻辑分析:
isnull().sum()统计每列的缺失值数量。- 缺失比例有助于判断是否需要保留该字段进行处理。
3.2.2 缺失值的填充策略(均值、中位数、插值等)
处理缺失值的常见策略包括:
| 策略名称 | 适用场景 | 特点 |
|---|---|---|
| 均值填充 | 数值型数据,分布较均匀 | 快速,但可能引入偏差 |
| 中位数填充 | 存在极端值 | 对异常值不敏感 |
| 插值填充 | 时间序列或有序数据 | 适用于连续性数据 |
| 热卡填充 | 类别型数据 | 用相似记录填充 |
示例代码:
# 使用中位数填充价格字段缺失值
median_price = df_sales_cleaned['价格'].median()
df_sales_cleaned['价格'].fillna(median_price, inplace=True)
# 使用前向插值填充时间字段
df_sales_cleaned['销售时间'].fillna(method='ffill', inplace=True)
参数说明:
fillna()是填充缺失值的核心方法。method='ffill'表示使用前一个有效值进行填充(Forward Fill)。
3.2.3 删除缺失记录的适用场景
在缺失比例较低(如小于5%)且缺失随机分布的情况下,删除记录是一种简单有效的处理方式。
示例代码:
# 删除含有缺失值的行
df_sales_cleaned = df_sales_cleaned.dropna()
# 查看处理后数据量
print(f"处理后记录数:{len(df_sales_cleaned)}")
逻辑分析:
dropna()默认删除任何包含缺失值的行。- 若需删除整列缺失值,可使用
axis=1参数。
3.3 数据类型转换与标准化
数据类型不一致会导致计算错误或模型训练失败,标准化处理则有助于提升模型的收敛速度与准确性。
3.3.1 时间字段的格式统一
时间字段在数据分析中至关重要,统一格式是进行时间维度分析的前提。
示例代码:
# 将字符串转换为datetime格式
df_sales_cleaned['销售时间'] = pd.to_datetime(df_sales_cleaned['销售时间'])
# 提取日期部分
df_sales_cleaned['销售日期'] = df_sales_cleaned['销售时间'].dt.date
逻辑分析:
pd.to_datetime()将字符串转换为标准时间格式。dt.date提取日期部分,便于后续按日分组分析。
3.3.2 类别型数据的编码处理(One-Hot、Label Encoding)
机器学习模型无法直接处理字符串类型数据,需将其转换为数值形式。
编码方式对比:
| 方法 | 适用场景 | 特点 |
|---|---|---|
| Label Encoding | 有序类别(如等级) | 保留顺序信息 |
| One-Hot Encoding | 无序类别(如药品名称) | 避免引入顺序偏差 |
示例代码(One-Hot):
# 对药品名称进行One-Hot编码
df_encoded = pd.get_dummies(df_sales_cleaned, columns=['药品名称'])
# 查看新字段
print(df_encoded.columns)
逻辑分析:
pd.get_dummies()自动生成多个二进制字段。- 若类别较多,建议使用
drop_first=True避免多重共线性。
3.3.3 数值型数据的标准化与归一化
标准化(Z-score)和归一化(Min-Max)是常用的数值处理方法。
示例代码:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# 标准化
scaler = StandardScaler()
df_sales_cleaned['价格标准化'] = scaler.fit_transform(df_sales_cleaned[['价格']])
# 归一化
minmax_scaler = MinMaxScaler()
df_sales_cleaned['价格归一化'] = minmax_scaler.fit_transform(df_sales_cleaned[['价格']])
逻辑分析:
StandardScaler将数据转换为均值为0、方差为1的标准分布。MinMaxScaler将数据缩放到 [0, 1] 区间,适用于图像处理等场景。
3.4 数据分组与聚合
数据分组与聚合是探索性数据分析的重要工具,可以按时间、药品、客户等维度进行统计汇总。
3.4.1 按时间、药品、客户维度分组统计
示例代码:
# 按药品名称和销售日期分组,计算销量总和
grouped = df_sales_cleaned.groupby(['药品名称', '销售日期'])['销量'].sum().reset_index()
# 查看前10条结果
print(grouped.head(10))
逻辑分析:
groupby()按指定字段分组。sum()是聚合函数,还可使用mean(),count(),max()等。
3.4.2 分组聚合函数的使用方法
多聚合函数使用:
# 分组后计算多个统计量
grouped_multi = df_sales_cleaned.groupby('药品名称').agg(
总销量=('销量', 'sum'),
平均价格=('价格', 'mean'),
销售天数=('销售时间', 'nunique')
).reset_index()
print(grouped_multi)
逻辑分析:
agg()支持多字段多函数的灵活聚合。nunique()计算唯一值数量,用于统计销售天数。
数据分组与聚合流程图(mermaid)
graph TD
A[原始数据] --> B{选择分组字段}
B --> C[按药品名称分组]
B --> D[按销售日期分组]
B --> E[按客户类型分组]
C --> F[应用聚合函数]
D --> F
E --> F
F --> G[生成汇总表]
总结:
通过本章内容,我们系统学习了药店销售数据的预处理流程,包括数据清洗、缺失值处理、数据类型转换与标准化、以及分组聚合操作。这些步骤是确保后续分析结果准确、模型训练高效的关键环节。下一章我们将进入数据探索与描述性统计分析,进一步挖掘数据中的隐藏信息。
4. 数据探索与描述性统计分析
数据分析的探索阶段是整个流程中最关键的环节之一,它帮助我们从数据中获取初步洞察,理解变量的基本特性以及它们之间的关系。本章将围绕药店销售数据展开,从单变量分析、多变量相关性、时间维度销售趋势到地理位置影响等维度,进行系统性的探索性分析。通过统计量、图表可视化和逻辑推理,我们将揭示数据背后的规律,为后续的建模和预测提供基础支撑。
4.1 单变量分析
单变量分析关注的是单个变量的分布情况,通过计算其基本统计量,如均值、方差、标准差、中位数、极值等,来理解该变量的集中趋势和离散程度。在药店销售数据中,我们重点分析“药品销量”和“销售额”两个关键指标。
4.1.1 药品销量的基本统计量(均值、方差、分布)
药品销量是衡量药店运营效率的重要指标。我们使用Pandas对药品销量字段进行描述性统计。
import pandas as pd
# 假设df是加载好的销售数据
print(df['销量'].describe())
执行逻辑说明:
- df['销量'] :选取“销量”列数据。
- .describe() :计算并输出该列的基本统计量,包括计数、均值、标准差、最小值、25%分位数、中位数(50%)、75%分位数、最大值。
输出示例:
| 统计量 | 值 |
|---|---|
| count | 10000 |
| mean | 25.3 |
| std | 15.6 |
| min | 1 |
| 25% | 12 |
| 50% | 24 |
| 75% | 36 |
| max | 120 |
参数说明:
- mean :平均销量为25.3,表示平均每单药品销售数量。
- std :标准差为15.6,说明销量波动较大。
- min/max :销量最低为1,最高为120,说明存在极端值或促销活动导致的销量突增。
此外,我们还可以绘制销量的直方图以观察其分布形态:
import matplotlib.pyplot as plt
plt.hist(df['销量'], bins=30, edgecolor='black')
plt.title('药品销量分布')
plt.xlabel('销量')
plt.ylabel('频数')
plt.show()
执行逻辑说明:
- plt.hist() :绘制直方图,观察销量的分布。
- bins=30 :将销量划分为30个区间。
- edgecolor='black' :设置柱子边框颜色,提升可读性。
结论:
从直方图可以看出销量是否符合正态分布或偏态分布,这对后续建模时是否需要进行变换或使用非参数方法具有指导意义。
4.1.2 销售额的集中趋势与离散程度
销售额是另一个关键变量,它反映了药店的整体收入情况。我们同样使用描述性统计和可视化手段来分析其特性。
print(df['销售额'].describe())
输出示例:
| 统计量 | 值 |
|---|---|
| count | 10000 |
| mean | 150.2 |
| std | 90.5 |
| min | 5 |
| 25% | 70 |
| 50% | 140 |
| 75% | 210 |
| max | 1000 |
分析:
- 销售额均值为150.2元,说明平均每单销售金额为150元左右。
- 标准差为90.5,波动较大,可能存在大额订单或某些药品单价较高。
绘制销售额的箱线图可以识别异常值:
plt.boxplot(df['销售额'])
plt.title('销售额箱线图')
plt.ylabel('销售额(元)')
plt.show()
执行逻辑说明:
- plt.boxplot() :绘制箱线图,识别销售额中的异常值。
- 箱线图的“须”表示正常范围,点表示异常值。
结论:
若箱线图中出现大量异常点,说明销售额存在极端值,需进一步检查是否为输入错误或特殊促销活动。
4.2 多变量相关性分析
在数据分析中,变量之间的相关性分析有助于理解变量之间的关系,并为后续的建模提供变量选择依据。我们重点分析“销售量”与“销售额”的关系,以及“客户类型”与“购买频率”的关系。
4.2.1 销售量与销售额的相关性
销售量与销售额之间理论上存在强正相关性,因为销售额 = 单价 × 销量。我们使用Pandas计算皮尔逊相关系数来验证这一关系。
import seaborn as sns
corr = df[['销量', '销售额']].corr()
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('销量与销售额相关性热力图')
plt.show()
执行逻辑说明:
- corr() :计算销量与销售额之间的相关系数。
- sns.heatmap() :绘制热力图,展示变量之间的相关性。
输出示例:
| 变量 | 销量 | 销售额 |
|---|---|---|
| 销量 | 1.00 | 0.87 |
| 销售额 | 0.87 | 1.00 |
结论:
销量与销售额之间的皮尔逊相关系数为0.87,说明两者具有高度正相关性,验证了理论模型的合理性。
4.2.2 客户类型与购买频率的关系
客户类型通常分为“普通客户”、“会员客户”等,购买频率是指客户在一段时间内的购买次数。我们使用交叉表和柱状图来分析不同类型客户的购买频率分布。
import pandas as pd
import matplotlib.pyplot as plt
# 假设'客户类型'列为分类变量,'购买频率'为数值变量
cross_tab = pd.crosstab(index=df['客户类型'], columns=df['购买频率'])
cross_tab.plot(kind='bar', stacked=True)
plt.title('客户类型与购买频率交叉分布')
plt.xlabel('客户类型')
plt.ylabel('频数')
plt.show()
执行逻辑说明:
- pd.crosstab() :生成客户类型与购买频率的交叉表。
- plot(kind='bar') :绘制堆叠柱状图,比较不同类型客户的购买频率分布。
结论:
通过图表可以观察到不同客户类型在购买频率上的差异。例如,会员客户可能在高频购买群体中占比较高,这对营销策略制定具有指导意义。
4.3 时间维度的销售分析
时间维度是销售分析中不可或缺的变量,它可以帮助我们识别销售趋势、周期性波动和节假日效应。
4.3.1 每日销售总额的波动趋势
我们将数据按“日期”字段进行分组,计算每日销售总额,并绘制趋势图。
df['日期'] = pd.to_datetime(df['日期'])
daily_sales = df.groupby('日期')['销售额'].sum().reset_index()
plt.plot(daily_sales['日期'], daily_sales['销售额'], marker='o', linestyle='-')
plt.title('每日销售总额趋势')
plt.xlabel('日期')
plt.ylabel('销售总额(元)')
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
执行逻辑说明:
- pd.to_datetime() :将“日期”字段转换为时间类型。
- groupby('日期') :按日期分组计算每日销售总额。
- plt.plot() :绘制时间序列图,观察销售总额的波动趋势。
结论:
从趋势图中可以识别销售的上升或下降趋势,是否存在节假日效应或促销活动带来的高峰。
4.3.2 不同门店销售的周期性表现
我们还可以按门店编号进行分组,分析各门店的销售周期性表现。
import seaborn as sns
df['星期'] = df['日期'].dt.day_name()
grouped = df.groupby(['门店编号', '星期'])['销售额'].mean().reset_index()
sns.lineplot(data=grouped, x='星期', y='销售额', hue='门店编号')
plt.title('各门店按星期的平均销售额趋势')
plt.xlabel('星期')
plt.ylabel('平均销售额(元)')
plt.xticks(rotation=45)
plt.legend(title='门店编号')
plt.show()
执行逻辑说明:
- dt.day_name() :提取日期对应的星期名称。
- groupby(['门店编号', '星期']) :按门店和星期分组计算平均销售额。
- sns.lineplot() :绘制多门店的周销售趋势图,比较周期性表现。
结论:
某些门店可能在周末销售表现更佳,而另一些门店则在工作日表现更好,这对门店排班和库存管理具有指导意义。
4.4 地理位置与销售表现
地理位置信息在药店销售分析中具有重要意义,可以帮助识别销售热点区域和潜在增长点。
4.4.1 各地区销售额的分布情况
我们按地区分组计算销售额总和,并使用柱状图进行可视化。
region_sales = df.groupby('地区')['销售额'].sum().reset_index()
plt.bar(region_sales['地区'], region_sales['销售额'])
plt.title('各地区销售总额分布')
plt.xlabel('地区')
plt.ylabel('销售总额(元)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
执行逻辑说明:
- groupby('地区') :按地区分组计算销售额总和。
- plt.bar() :绘制柱状图,展示各地区销售额差异。
结论:
某些地区的销售额显著高于其他地区,可能与其人口密度、消费水平或门店密度有关。
4.4.2 热点区域识别与地图可视化
为了更直观地展示销售热点区域,我们可以使用地理信息系统(GIS)工具如 geopandas 或 folium 进行地图可视化。
import folium
from folium.plugins import MarkerCluster
# 假设df包含'纬度'和'经度'字段
m = folium.Map(location=[39.9042, 116.4074], zoom_start=10)
marker_cluster = MarkerCluster().add_to(m)
for i, row in df.iterrows():
folium.Marker(location=[row['纬度'], row['经度']], popup=f"销售额:{row['销售额']}元").add_to(marker_cluster)
m.save('sales_map.html')
执行逻辑说明:
- folium.Map() :创建地图对象。
- MarkerCluster() :创建标记集群,避免地图过载。
- folium.Marker() :为每条记录添加带销售额信息的标记。
- m.save() :保存地图为HTML文件,可在浏览器中查看。
结论:
地图可视化可以直观识别销售热点区域,辅助门店选址和市场拓展。
本章通过单变量统计、多变量相关性、时间维度分析和地理位置可视化等手段,系统性地探索了药店销售数据。这些分析为后续的建模和预测提供了坚实的数据基础,也为业务决策提供了有力支持。下一章我们将深入分析销售趋势的时间分组表现,包括按月和季度的销售波动分析。
5. 销售趋势按时间分组分析(月/季度)
5.1 时间序列数据构建
在药店销售数据分析中,时间维度是理解销售趋势的关键因素之一。为了构建时间序列数据,首先需要确保数据集中包含有效的时间字段,例如销售日期( sale_date )。我们可以使用Pandas库对时间字段进行解析并设置为索引,从而构建时间序列格式的数据集。
5.1.1 时间字段的解析与索引设置
以下是一个将字符串格式的时间字段转换为 datetime 类型,并将其设置为索引的示例代码:
import pandas as pd
# 假设数据集为 sales_df,包含 'sale_date' 字段
sales_df = pd.read_csv('pharmacy_sales.csv')
# 将 'sale_date' 转换为 datetime 类型
sales_df['sale_date'] = pd.to_datetime(sales_df['sale_date'])
# 设置 'sale_date' 为索引
sales_df.set_index('sale_date', inplace=True)
# 查看前5行数据
print(sales_df.head())
执行上述代码后, sale_date 列将被用作索引,便于后续进行时间维度的聚合和分析。
5.1.2 按月/季度聚合销售数据
在时间序列构建完成后,我们可以使用 resample 方法按月或季度对销售数据进行聚合。以销售额( sales_amount )为例:
# 按月聚合销售额
monthly_sales = sales_df['sales_amount'].resample('M').sum()
# 按季度聚合销售额
quarterly_sales = sales_df['sales_amount'].resample('Q').sum()
# 查看按月聚合结果
print(monthly_sales.head())
| 时间 | 月度销售额 |
|---|---|
| 2023-01-31 | 25000.00 |
| 2023-02-28 | 23500.50 |
| 2023-03-31 | 27800.75 |
| 2023-04-30 | 26000.25 |
| 2023-05-31 | 28900.00 |
通过上述操作,我们成功构建了按月和季度聚合的销售数据,为后续趋势分析打下基础。
5.2 月度与季度销售趋势分析
5.2.1 销售总量与销售额的月度变化
在构建了按月聚合的数据后,我们可以通过可视化手段分析销售总量和销售额的变化趋势。以下是一个使用Matplotlib绘制月度销售额变化趋势的示例:
import matplotlib.pyplot as plt
# 绘制月度销售额趋势图
plt.figure(figsize=(12, 6))
monthly_sales.plot(kind='line', title='Monthly Sales Trend (2023)')
plt.xlabel('Month')
plt.ylabel('Sales Amount')
plt.grid(True)
plt.show()
该图将展示销售额随时间的变化趋势,帮助我们识别是否存在增长或下降趋势。
5.2.2 季节性波动与节假日影响
药店销售数据往往受到季节性和节假日的影响。例如,冬季感冒药销量可能激增,春节前后保健品销售可能上升。我们可以将销售数据与节假日数据进行合并分析:
# 假设有一个节假日列表 holidays_df
holidays_df = pd.DataFrame({
'date': ['2023-01-01', '2023-02-11', '2023-04-05', '2023-06-22'],
'holiday': ['New Year', 'Spring Festival', 'Qingming Festival', 'Dragon Boat Festival']
})
# 将节假日日期转换为 datetime 类型
holidays_df['date'] = pd.to_datetime(holidays_df['date'])
# 提取节假日所在月份
monthly_sales['is_holiday_month'] = monthly_sales.index.to_period('M').isin(holidays_df['date'].dt.to_period('M'))
# 查看节假日月份的销售额
print(monthly_sales[monthly_sales['is_holiday_month']])
通过分析节假日月份的销售额,可以判断是否存在显著增长或下降,为营销策略提供依据。
5.3 药品类别的时间趋势分析
5.3.1 各类药品的月度销售变化
为了分析不同药品类别的销售趋势,我们需要对药品类别( product_category )与销售数据进行分组,并按月进行聚合:
# 按药品类别和月份分组汇总销售额
category_monthly_sales = sales_df.groupby([pd.Grouper(freq='M'), 'product_category'])['sales_amount'].sum().unstack()
# 查看前几行数据
print(category_monthly_sales.head())
| product_category | Antibiotics | Cough Medicine | Vitamins | Painkillers |
|---|---|---|---|---|
| sale_date | ||||
| 2023-01-31 | 8000.00 | 5000.00 | 6000.00 | 6000.00 |
| 2023-02-28 | 7500.00 | 5200.00 | 5800.00 | 5000.00 |
| 2023-03-31 | 8200.00 | 5100.00 | 6200.00 | 5300.00 |
5.3.2 高增长与低波动药品识别
我们可以计算每个药品类别销售额的月增长率,并识别出高增长类别:
# 计算每个药品类别的月增长率
growth_rates = category_monthly_sales.pct_change() * 100
# 查看增长率数据
print(growth_rates.tail())
通过分析增长率数据,可以识别出哪些药品类别在特定月份增长迅速,哪些表现稳定或波动较小,从而指导库存和促销策略。
5.4 预测趋势与可视化展示
5.4.1 使用移动平均法平滑趋势
为了消除短期波动,我们可以使用移动平均法对销售额趋势进行平滑处理:
# 计算3个月移动平均
monthly_sales_ma = monthly_sales.rolling(window=3).mean()
# 绘制原始数据与移动平均线
plt.figure(figsize=(12, 6))
monthly_sales.plot(kind='line', label='Original')
monthly_sales_ma.plot(kind='line', label='3-Month MA', color='red')
plt.title('Monthly Sales with Moving Average')
plt.xlabel('Month')
plt.ylabel('Sales Amount')
plt.legend()
plt.grid()
plt.show()
该图展示了原始销售趋势与平滑后的趋势线,有助于识别长期趋势方向。
5.4.2 利用Matplotlib/Seaborn绘制趋势图
使用Seaborn库可以更美观地展示趋势变化:
import seaborn as sns
# Seaborn样式设置
sns.set(style="whitegrid")
# 绘制趋势图
plt.figure(figsize=(12, 6))
sns.lineplot(x=monthly_sales.index, y=monthly_sales.values)
plt.title('Monthly Sales Trend')
plt.xlabel('Month')
plt.ylabel('Sales Amount')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
通过上述代码,我们可以生成清晰、美观的趋势图,用于展示销售数据的时间变化特征。
简介:本项目通过Python对药店销售数据进行全流程分析,涵盖数据预处理、探索、清洗、趋势分析、关联挖掘、客户聚类及销售预测。项目包含数据集、源码和说明文档,适合用于课程设计与大型作业。学习者将掌握Pandas、NumPy、Matplotlib、Seaborn等工具的使用,提升数据建模与商业洞察力,为零售决策提供数据支持。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)