使用pandas进行自行车销售记录的数据分析,看不懂你打我
本文数据和源代码见github:https://github.com/w1449550206/Pandas-Data-analysis-of-bicycle-sales-record-based-on-pandas.git
文章目录
原始数据
详细数据分析过程
思路
-
导入数据集;
-
检查数据基本属性;
-
修改个别表头、舍弃缺失的不完整数据行;
-
对时间这一列信息进行特殊处理,舍弃星期的信息,然后将时间这一列数据的格式转为时间格式pd.to_datetime();
-
“销售数量”、“应收金额”、“实收金额”这三列数据显然不可能有负数,我们要舍弃掉一些异常值的数据行;
-
对数据按时间升序排序,重置其索引;
-
计算商品的种类和商品的总销售数量;
-
计算总销售次数,月份数,月均销售次数;
-
计算总销售金额,平均每月销售金额,平均每单销售金额;
-
计算最大和最小日销售金额及对应的日期、最大和最小日销售数量及对应的日期;
-
画出各个月单独的每日销售金额直方图、每月销售金额直方图、每月销售金额变化直线图、销量前十和最后十名的自行车的直方图。
-
代码实现
代码
import os
import matplotlib.pyplot as plt
import pandas as pd
from pylab import mpl # 用于画图时显示中文字符
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 使用matplotlib画图时如果出现中文使用黑体字体
file_data = pd.read_excel("销售记录.xlsx") # 读取数据文件
file_data
# 获取文件名
def get_file_name(fl_data):
# items()函数以列表返回可遍历的(键, 值)元组数组
# globals()是一个字典,存储了所有的全局变量的名字和对应的变量的值
# 在这里我们通过判断输入的变量的值与globals()中存储的变量的值是否相等,相等就认为我们输入的变量就是这个globals()中存储的变量
# 这样我们就返回这个globals()中存储的变量的变量名,即key
for var_name, value in globals().items():
if value is fl_data:
return var_name
# 显示读入的文件数据的一些基本属性
def show_data_basic_description(fl_data):
describe_label_shape = get_file_name(file_data) + "文件的shape大小:"
print(describe_label_shape, file_data.shape)
describe_label_index = get_file_name(file_data) + "文件的索引开头和末尾:"
print(describe_label_index, file_data.index)
describe_label_table_head = get_file_name(file_data) + "文件的表头项:"
print(describe_label_table_head, file_data.columns)
describe_label_top_five_line = get_file_name(file_data) + "文件的内容:\n"
print(describe_label_top_five_line, file_data.head())
describe_label_data_type = get_file_name(file_data) + "文件的各项的数据类型:\n"
print(describe_label_data_type, file_data.dtypes)
print("文件预处理前的各项属性:")
show_data_basic_description(file_data)
# 其中一个列的表头重命名一下
file_data.rename(columns={
"下单日期": "销售时间"}, inplace=True)
# 删除所有有缺失数据的行
file_data = file_data.dropna(subset=['订单编号', '客户ID', '客户名称', '客户编号', '客户省份', '销售代表ID', '销售时间', '预计送货日期', '实际送货日期', '产品ID', '产品名称', '数量', '单价', '金额'], how="any")
file_data
# 删除无效时间的数据,没有
file_data = file_data.dropna(subset=["销售时间"], how="any")
file_data
# 处理异常值:“数量”、“单价”、“金额”这三列数据显然不可能有负数,因此要去掉不合理的数据
pop = file_data.loc[:, "金额"] > 0
file_data = file_data.loc[pop, :]
file_data
# 对数据按时间排序,ascending=True表示升序排列
file_data = file_data.sort_values(by="销售时间", ascending=True)
file_data
# 重置数据的索引
file_data = file_data.reset_index(drop=True)
print("文件预处理后的各项属性:")
show_data_basic_description(file_data)
# 计算自行车的种类和自行车的总销售数量
def compute_commodity_type_and_sum(fl_data):
# 去除重复的自行车种类
no_duplicate_file_data = fl_data.drop_duplicates(subset=["产品ID"])
# 计算自行车的种类
fl_data_total_consumption_type = no_duplicate_file_data.shape[0]
# 计算自行车的总销售数量
fl_data = fl_data.dropna(subset=["产品ID"], how="any")
fl_data_total_sum = fl_data.loc[:, "数量"].sum()
return fl_data_total_consumption_type, fl_data_total_sum
file_data_total_consumption_type, file_data_total_sum = compute_commodity_type_and_sum(file_data)
print("销售自行车的种类:{} 销售自行车的数量:{}".format(file_data_total_consumption_type, int(file_data_total_sum)))
# 计算总销售次数,月份数,月均销售次数,月均销售次数 = 总销售次数 / 月份数
def compute_consumption_num(fl_data):
# 先删除重复的数据,因为一个人一次消费可能买好几种自行车
no_duplicate_file_data = fl_data.drop_duplicates(subset=["销售时间", "客户ID"])
# 计算总销售次数
fl_data_total_consumption_num = no_duplicate_file_data.shape[0]
# 计算月份数
# 先给上面删除了重复数据的文件排序,重新组织索引
no_duplicate_file_data = no_duplicate_file_data.sort_values(by="销售时间", ascending=True)
no_duplicate_file_data = no_duplicate_file_data.reset_index(drop=True)
# 最早购买时间,最晚购买时间,计算天数和月份数
start_time = no_duplicate_file_data.loc[0, "销售时间"]
end_time = no_duplicate_file_data.loc[fl_data_total_consumption_num - 1, "销售时间"]
fl_data_days_count = (end_time - start_time).days
# //表示除以后返回商的整数部分
fl_data_months_count = fl_data_days_count // 30
# 计算月均销售次数,用总销售次数除以月数
fl_data_per_month_average_consumption_num = fl_data_total_consumption_num / fl_data_months_count
return fl_data_total_consumption_num, fl_data_months_count, fl_data_per_month_average_consumption_num
total_consumption_num, months_count, per_month_average_consumption_num = compute_consumption_num(file_data)
print("总销售次数为:{} 月份数为:{} 月均销售次数为:{:.1f}".format(total_consumption_num, months_count, per_month_average_consumption_num))
#计算总销售金额,平均每月销售金额,平均每单销售金额
def compute_consumption_money(fl_data, ttl_consumption_num, mth_count):
# 计算月均销售金额,先计算总销售金额,再除以月数
fl_data_total_consumption_money = fl_data.loc[:, "金额"].sum()
fl_data_per_month_consumption_money = fl_data_total_consumption_money / mth_count
# 计算平均每单销售金额,用总销售金额除以总销售次数
fl_data_per_sale_consumption_money = fl_data_total_consumption_money / ttl_consumption_num
return fl_data_total_consumption_money, fl_data_per_month_consumption_money, fl_data_per_sale_consumption_money
total_consumption_money, per_month_consumption_money, per_sale_consumption_money = compute_consumption_money(file_data,
total_consumption_num,
months_count)
print("总销售金额为:{:.2f} 月均销售金额为:{:.2f} 平均每单销售金额为:{:.2f}".format(total_consumption_money, per_month_consumption_money,
per_sale_consumption_money))
# 最大和最小销售金额的对应的日期
def max_and_min_consumption_money_day(fl_data):
fl_data_money = fl_data[['销售时间', '金额']]
fl_data_money.index = fl_data_money['销售时间']
fl_data_money_group_day = fl_data_money.groupby(fl_data_money.index)
fl_data_money_group_day = fl_data_money_group_day.sum()
# 按求最大销售金额和最小销售金额及对应的日期
fl_data_money_group_day = fl_data_money_group_day.sort_values(by='金额', ascending=False)
max_money_fl_data_group_day = fl_data_money_group_day.iloc[0:1, :]
fl_data_money_group_day = fl_data_money_group_day.sort_values(by='金额', ascending=True)
min_money_fl_data_group_day = fl_data_money_group_day.iloc[0:1, :]
return max_money_fl_data_group_day, min_money_fl_data_group_day
max_sum_group_day, min_sum_group_day = max_and_min_consumption_money_day(file_data)
print("最大每日销售金额及对应的日期:\n", max_sum_group_day)
print("最小每日销售金额及对应的日期:\n", min_sum_group_day)
# 最大和最小销售数量的对应的日期
def max_and_min_consumption_sum_day(fl_data):
fl_data_sum = fl_data[['销售时间', '数量']]
fl_data_sum.index = fl_data_sum['销售时间']
fl_data_sum_group_day = fl_data_sum.groupby(fl_data_sum.index)
fl_data_sum_group_day = fl_data_sum_group_day.sum()
# 按求最大销售金额和最小销售金额及对应的日期
fl_data_sum_group_day = fl_data_sum_group_day.sort_values(by='数量', ascending=False)
max_sum_fl_data_group_day = fl_data_sum_group_day.iloc[0:1, :]
fl_data_sum_group_day = fl_data_sum_group_day.sort_values(by='数量', ascending=True)
min_sum_fl_data_group_day = fl_data_sum_group_day.iloc[0:1, :]
return max_sum_fl_data_group_day, min_sum_fl_data_group_day
max_sum_group_day, min_sum_group_day = max_and_min_consumption_sum_day(file_data)
print("最大每日销售数量及对应的日期:\n", max_sum_group_day)
print("最小每日销售数量及对应的日期:\n", min_sum_group_day)
# 在使用matplotlib画图时使用的数据是file_data的copy,防止画图时影响file_data的数据
file_data_copy = file_data.copy()
# 对文件数据按时间升序排序,并在排序后重置索引
def sort_data_value_as_time(fl_data):
# 对数据按时间排序,ascending=True表示升序排列
fl_data = fl_data.sort_values(by="销售时间", ascending=True)
# 重置数据的索引,重置后索引变成按时间排序后的索引了
fl_data = fl_data.reset_index(drop=True)
return fl_data
file_data_copy
# 画各个月单独的每日销售金额直方图
def draw_and_save_per_day_per_month_consumption_bar(fl_data):
# 对文件数据按时间升序排序,并在排序后重置索引
fl_data = sort_data_value_as_time(fl_data)
# 如果图片保存路径不存在则创建
if not os.path.exists("./count_image/"):
os.mkdir("./count_image")
fl_data_money = fl_data[['销售时间', '金额']]
fl_data_money.index = fl_data_money['销售时间']
# 画图
for index in range(1, 13, 1):
month_index = "2015-" + str(index)
per_mont_fl_data_money = fl_data_money[month_index]
per_mont_fl_data_money_day_group = per_mont_fl_data_money.groupby(per_mont_fl_data_money.index.day)
per_mont_fl_data_money_day_group = per_mont_fl_data_money_day_group.sum()
per_mont_fl_data_money_day_group.plot(figsize=(12, 8), kind='bar')
image_title = str(index) + "月每日销售金额直方图"
plt.title(image_title)
plt.xlabel('销售时间')
plt.ylabel('金额')
save_path = "./count_image/per_day_consumption_month_" +str(index) + "_bar_2015年.jpg"
plt.savefig(save_path)
plt.close()
draw_and_save_per_day_per_month_consumption_bar(file_data_copy)
# 画各个月单独的每日销售金额直方图
def draw_and_save_per_day_per_month_consumption_bar(fl_data):
# 对文件数据按时间升序排序,并在排序后重置索引
fl_data = sort_data_value_as_time(fl_data)
# 如果图片保存路径不存在则创建
if not os.path.exists("./count_image/"):
os.mkdir("./count_image")
fl_data_money = fl_data[['销售时间', '金额']]
fl_data_money.index = fl_data_money['销售时间']
# 画图
for index in range(1, 13, 1):
month_index = "2016-" + str(index)
per_mont_fl_data_money = fl_data_money[month_index]
per_mont_fl_data_money_day_group = per_mont_fl_data_money.groupby(per_mont_fl_data_money.index.day)
per_mont_fl_data_money_day_group = per_mont_fl_data_money_day_group.sum()
per_mont_fl_data_money_day_group.plot(figsize=(12, 8), kind='bar')
image_title = str(index) + "月每日销售金额直方图"
plt.title(image_title)
plt.xlabel('销售时间')
plt.ylabel('金额')
save_path = "./count_image/per_day_consumption_month_" +str(index) + "_bar_2016年.jpg"
plt.savefig(save_path)
plt.close()
draw_and_save_per_day_per_month_consumption_bar(file_data_copy)
# 画出按月份的销售金额直方图
def draw_and_save_per_month_consumption_bar(fl_data):
# 对文件数据按时间升序排序,并在排序后重置索引
fl_data = sort_data_value_as_time(fl_data)
# 如果图片保存路径不存在则创建
if not os.path.exists("./count_image/"):
os.mkdir("./count_image")
fl_data_money = fl_data[['销售时间', '金额']]
fl_data_money.index = fl_data_money['销售时间']
# 如果图片保存路径不存在则创建
for year_index in ['2015','2016']:
per_year_fl_data_money = fl_data_money[year_index]
fl_data_money_month_group = per_year_fl_data_money.groupby(per_year_fl_data_money.index.month)
fl_data_money_month_group_count = fl_data_money_month_group.sum()
fl_data_money_month_group_count.plot(figsize=(12, 8), kind='bar')
plt.title('每月销售金额直方图')
plt.xlabel('月份')
plt.ylabel('销售金额')
plt.legend(loc="best")
plt.savefig("./count_image/per_month_consumption_bar_"+year_index+".jpg")
plt.close()
draw_and_save_per_month_consumption_bar(file_data_copy)
# 画出按年的销售金额直方图
def draw_and_save_per_year_consumption_bar(fl_data):
# 对文件数据按时间升序排序,并在排序后重置索引
fl_data = sort_data_value_as_time(fl_data)
# 如果图片保存路径不存在则创建
if not os.path.exists("./count_image/"):
os.mkdir("./count_image")
fl_data_money = fl_data[['销售时间', '金额']]
fl_data_money.index = fl_data_money['销售时间']
# 如果图片保存路径不存在则创建
per_year_fl_data_money=fl_data_money
fl_data_money_year_group = per_year_fl_data_money.groupby(per_year_fl_data_money.index.year)
fl_data_money_year_group_count = fl_data_money_year_group.sum()
fl_data_money_year_group_count.plot(figsize=(12, 8), kind='bar')
plt.title('每年销售金额直方图')
plt.xlabel('年份')
plt.ylabel('销售金额')
plt.legend(loc="best")
plt.savefig("./count_image/per_year_consumption_bar.jpg")
plt.close()
draw_and_save_per_year_consumption_bar(file_data_copy)
# 画出按月份的销售金额变化直线图
def draw_and_save_per_month_consumption_line(fl_data):
# 对文件数据按时间升序排序,并在排序后重置索引
fl_data = sort_data_value_as_time(fl_data)
# 如果图片保存路径不存在则创建
if not os.path.exists("./count_image/"):
os.mkdir("./count_image")
fl_data_money = fl_data[['销售时间', '金额']]
fl_data_money.index = fl_data_money['销售时间']
# 如果图片保存路径不存在则创建
for year_index in ['2015','2016']:
per_year_fl_data_money = fl_data_money[year_index]
fl_data_money_month_group = per_year_fl_data_money.groupby(per_year_fl_data_money.index.month)
fl_data_money_month_group_count = fl_data_money_month_group.sum()
# 画出按月份的销售金额变化直方图
plt.figure(figsize=(12, 8))
plt.plot(fl_data_money_month_group_count['金额'])
plt.title('每月销售金额变化图')
plt.ylabel('金额')
plt.savefig("./count_image/per_month_consumption_line_"+year_index+".jpg")
plt.close()
draw_and_save_per_month_consumption_line(file_data_copy)
# 画出销量前十和最后十名的自行车的直方图
def draw_and_save_first_ten_medicine_bar(fl_data):
# 统计各种自行车的销售数量
medicine_data = fl_data[['产品名称', '数量']]
medicine_data_group = medicine_data.groupby('产品名称')[['数量']]
medicine_data_group_count = medicine_data_group.sum()
# 对统计的每种自行车销售数量进行降序排序
medicine_data_group_count = medicine_data_group_count.sort_values(by='数量', ascending=False)
# 截取销售数量最多和最少的十种自行车
top_ten_medicine = medicine_data_group_count.iloc[0:10, :]
under_ten_medicine = medicine_data_group_count.iloc[-11:-1, :]
# 最后十名升序排序
under_ten_medicine = under_ten_medicine.sort_values(by="数量", ascending=True)
# 画图,kind='bar'表示用条形图
top_ten_medicine.plot(figsize=(12, 8), kind='bar')
plt.title('前十名销量的自行车')
plt.xlabel('自行车种类')
plt.ylabel('数量')
plt.legend(loc="best")
plt.savefig("./count_image/top_ten_medicine_sale_graph.jpg")
plt.close()
under_ten_medicine.plot(figsize=(12, 8), kind='bar')
plt.title('最后十名销量的自行车')
plt.xlabel('自行车种类')
plt.ylabel('销售数量')
plt.legend(loc="best")
plt.savefig("./count_image/under_ten_medicine_sale_graph.jpg")
plt.close()
draw_and_save_first_ten_medicine_bar(file_data_copy)
ok
数据分析报告
2015年至2016年自行车销售数据分析报告
1. 商品的种类和商品的总销售数量;
近两年,销售自行车的种类:59 销售自行车的数量:100155。
2. 总销售次数,月份数,月均销售次数;
总销售次数为:6554 月份数为:24 月均销售次数为:273.1
3. 总销售金额,平均每月销售金额,平均每单销售金额;
总销售金额为:70299898.66 月均销售金额为:2929162.44 平均每单销售金额为:10726.26
4. 最大和最小日销售金额及对应的日期、最大和最小日销售数量及对应的日期;
5. 各个月单独的每日销售金额直方图
2015年
2016年
由图可知,2015年以及2016年,除2015年的1月外,每个月大笔的订单额都发生在月末,由此可见,月末的自行车销量较好,因此要考虑月末的营销手段。以及如何提升月初、月中的销售额。
- 每月销售金额直方图
2015年
2016年
由图可知,2015年每个月的销售额差距不大,其中夏季销售较高,可以考虑夏季活动增多,对自行车的需求增大,也相应采取营销方案。2016年前4个月和前一年的销售状况差不多,但从5月开始,月销售额突然减少持续到了2016年12月,要考虑公司在4月底进行了什么不一样的改变,并做出总结与改正方案。 - 每月销售金额变化直线图
2015年
2016年
对比2015年和2016年的折线图可知,2015年的销售额波动很大,没有稳定的销售方案。其中夏天销售额较高。且2016年的四月后开始有了突然的减少,可以考虑当年的行业情况。 - 2015年和2016年的销售额对比
由图可知,2016年较前一年销售额减少了不止一半,因此要考虑2016年发生了什么样的行业变化还是公司自身的问题,并作出相应的对策和方案。
9. 销量前十和最后十名的自行车的直方图。
由图可知。近两年后刹车、自行车脚踏板、后变速器销量非常低,可以考虑减少库存或采取相应的营销手段。另公路自行车、山地自行车、自行车车架销量很高,可以着重注意这些类别产品的销售。