使用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月外,每个月大笔的订单额都发生在月末,由此可见,月末的自行车销量较好,因此要考虑月末的营销手段。以及如何提升月初、月中的销售额。

  1. 每月销售金额直方图
    2015年

    2016年

    由图可知,2015年每个月的销售额差距不大,其中夏季销售较高,可以考虑夏季活动增多,对自行车的需求增大,也相应采取营销方案。2016年前4个月和前一年的销售状况差不多,但从5月开始,月销售额突然减少持续到了2016年12月,要考虑公司在4月底进行了什么不一样的改变,并做出总结与改正方案。
  2. 每月销售金额变化直线图
    2015年

    2016年

    对比2015年和2016年的折线图可知,2015年的销售额波动很大,没有稳定的销售方案。其中夏天销售额较高。且2016年的四月后开始有了突然的减少,可以考虑当年的行业情况。
  3. 2015年和2016年的销售额对比


由图可知,2016年较前一年销售额减少了不止一半,因此要考虑2016年发生了什么样的行业变化还是公司自身的问题,并作出相应的对策和方案。
9. 销量前十和最后十名的自行车的直方图。

由图可知。近两年后刹车、自行车脚踏板、后变速器销量非常低,可以考虑减少库存或采取相应的营销手段。另公路自行车、山地自行车、自行车车架销量很高,可以着重注意这些类别产品的销售。