import pandas as pd
data = pd.read_csv('tmall_order_report.csv')
data.head() # 退款金额应该就是客户退货后,返还给客户的退款金额
订单编号 | 总金额 | 买家实际支付金额 | 收货地址 | 订单创建时间 | 订单付款时间 | 退款金额 | |
---|---|---|---|---|---|---|---|
0 | 1 | 178.8 | 0.0 | 上海 | 2020-02-21 00:00:00 | NaN | 0.0 |
1 | 2 | 21.0 | 21.0 | 内蒙古自治区 | 2020-02-20 23:59:54 | 2020-02-21 00:00:02 | 0.0 |
2 | 3 | 37.0 | 0.0 | 安徽省 | 2020-02-20 23:59:35 | NaN | 0.0 |
3 | 4 | 157.0 | 157.0 | 湖南省 | 2020-02-20 23:58:34 | 2020-02-20 23:58:44 | 0.0 |
4 | 5 | 64.8 | 0.0 | 江苏省 | 2020-02-20 23:57:04 | 2020-02-20 23:57:11 | 64.8 |
data.info() # 数据集情况 28010 条,6个字段
<class 'pandas.core.frame.DataFrame'> RangeIndex: 28010 entries, 0 to 28009 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 订单编号 28010 non-null int64 1 总金额 28010 non-null float64 2 买家实际支付金额 28010 non-null float64 3 收货地址 28010 non-null object 4 订单创建时间 28010 non-null object 5 订单付款时间 24087 non-null object 6 退款金额 28010 non-null float64 dtypes: float64(3), int64(1), object(3) memory usage: 1.5+ MB
data.columns = data.columns.str.strip() # 列名有空格,需要处理下
data.columns
Index(['订单编号', '总金额', '买家实际支付金额', '收货地址', '订单创建时间', '订单付款时间', '退款金额'], dtype='object')
data[data.duplicated()].count() # 没有完全重复的数据
订单编号 0 总金额 0 买家实际支付金额 0 收货地址 0 订单创建时间 0 订单付款时间 0 退款金额 0 dtype: int64
data.isnull().sum() # 付款时间存在空值,表示订单未付款
订单编号 0 总金额 0 买家实际支付金额 0 收货地址 0 订单创建时间 0 订单付款时间 3923 退款金额 0 dtype: int64
data['收货地址'] = data['收货地址'].str.replace('自治区|维吾尔|回族|壮族|省', '') # 对省份做个清洗,便于可视化
data['收货地址'].unique()
array(['上海', '内蒙古', '安徽', '湖南', '江苏', '浙江', '天津', '北京', '四川', '贵州', '辽宁', '河南', '广西', '广东', '福建', '海南', '江西', '甘肃', '河北', '黑龙江', '云南', '重庆', '山西', '吉林', '山东', '陕西', '湖北', '青海', '新疆', '宁夏', '西藏'], dtype=object)
result = {}
result['总订单数'] = data['订单编号'].count()
result['已完成订单数'] = data['订单编号'][data['订单付款时间'].notnull()].count()
result['未付款订单数'] = data['订单编号'][data['订单付款时间'].isnull()].count()
result['退款订单数'] = data['订单编号'][data['退款金额'] > 0].count()
result['总订单金额'] = data['总金额'][data['订单付款时间'].notnull()].sum()
result['总退款金额'] = data['退款金额'][data['订单付款时间'].notnull()].sum()
result['总实际收入金额'] = data['买家实际支付金额'][data['订单付款时间'].notnull()].sum()
result
{'总订单数': 28010, '已完成订单数': 24087, '未付款订单数': 3923, '退款订单数': 5646, '总订单金额': 2474823.0700000003, '总退款金额': 572335.9199999999, '总实际收入金额': 1902487.1500000001}
from pyecharts import options as opts
from pyecharts.charts import Map, Bar, Line
from pyecharts.components import Table
from pyecharts.options import ComponentTitleOpts
from pyecharts.faker import Faker
table = Table()
headers = ['总订单数', '总订单金额', '已完成订单数', '总实际收入金额', '退款订单数', '总退款金额', '成交率', '退货率']
rows = [
[
result['总订单数'], f"{result['总订单金额']/10000:.2f} 万", result['已完成订单数'], f"{result['总实际收入金额']/10000:.2f} 万",
result['退款订单数'], f"{result['总退款金额']/10000:.2f} 万",
f"{result['已完成订单数']/result['总订单数']:.2%}",
f"{result['退款订单数']/result['已完成订单数']:.2%}",
]
]
table.add(headers, rows)
table.set_global_opts(
title_opts=ComponentTitleOpts(title='整体情况')
)
table.render_notebook()
整体情况
总订单数 | 总订单金额 | 已完成订单数 | 总实际收入金额 | 退款订单数 | 总退款金额 | 成交率 | 退货率 |
---|---|---|---|---|---|---|---|
28010 | 247.48 万 | 24087 | 190.25 万 | 5646 | 57.23 万 | 85.99% | 23.44% |
result2 = data[data['订单付款时间'].notnull()].groupby('收货地址').agg({'订单编号':'count'})
result21 = result2.to_dict()['订单编号']
c = (
Map()
.add("订单量", [*result21.items()], "china", is_map_symbol_show=False)
.set_series_opts(label_opts=opts.LabelOpts(is_show=True))
.set_global_opts(
title_opts=opts.TitleOpts(title='地区分布'),
visualmap_opts=opts.VisualMapOpts(max_=1000),
)
)
c.render_notebook()
data['订单创建时间'] = pd.to_datetime(data['订单创建时间'])
data['订单付款时间'] = pd.to_datetime(data['订单付款时间'])
result31 = data.groupby(data['订单创建时间'].apply(lambda x: x.strftime("%Y-%m-%d"))).agg({'订单编号':'count'}).to_dict()['订单编号']
c = (
Line()
.add_xaxis(list(result31.keys()))
.add_yaxis("订单量", list(result31.values()))
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
markpoint_opts=opts.MarkPointOpts(
data=[
opts.MarkPointItem(type_="max", name="最大值"),
]
),
)
.set_global_opts(title_opts=opts.TitleOpts(title="每日订单量走势"))
)
c.render_notebook()
从上图来看,2月份上半月由于受新冠疫情影响,订单量比较少,随着复工开展,下半月的订单量增长明显。
result32 = data.groupby(data['订单创建时间'].apply(lambda x: x.strftime("%H"))).agg({'订单编号':'count'}).to_dict()['订单编号']
x = [*result32.keys()]
y = [*result32.values()]
c = (
Bar()
.add_xaxis(x)
.add_yaxis("订单量", y)
.set_global_opts(title_opts=opts.TitleOpts(title="每小时订单量走势"))
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
markpoint_opts=opts.MarkPointOpts(
data=[
opts.MarkPointItem(type_="max", name="峰值"),
opts.MarkPointItem(name="第二峰值", coord=[x[15], y[15]], value=y[15]),
opts.MarkPointItem(name="第三峰值", coord=[x[10], y[10]], value=y[10]),
]
),
)
)
c.render_notebook()
从每小时订单量走势来看,一天中有3个高峰期(10点、15点、21点),其中21点-22点之间是一天中订单量最多的时候,这个结果和之前 1 亿条淘宝用户行为数据分析 的结果是一致的。对于卖家的指导意义就是,为了提高订单量,高峰期时应该尽量保证客服的回复速度,尤其是晚上21点-22点之间,所以很多做电商的基本都有夜班。
s = data['订单付款时间'] - data['订单创建时间']
s[s.notnull()].apply(lambda x: x.seconds / 60 ).mean() # 从下单到付款的平均耗时为 7.7 分钟
7.7399046511949745
import pandas as pd
data2 = pd.read_csv('双十一淘宝美妆数据.csv')
data2.head()
update_time | id | title | price | sale_count | comment_count | 店名 | |
---|---|---|---|---|---|---|---|
0 | 2016/11/14 | A18164178225 | CHANDO/自然堂 雪域精粹纯粹滋润霜50g 补水保湿 滋润水润面霜 | 139.0 | 26719.0 | 2704.0 | 自然堂 |
1 | 2016/11/14 | A18177105952 | CHANDO/自然堂凝时鲜颜肌活乳液120ML 淡化细纹补水滋润专柜正品 | 194.0 | 8122.0 | 1492.0 | 自然堂 |
2 | 2016/11/14 | A18177226992 | CHANDO/自然堂活泉保湿修护精华水(滋润型135ml 补水控油爽肤水 | 99.0 | 12668.0 | 589.0 | 自然堂 |
3 | 2016/11/14 | A18178033846 | CHANDO/自然堂 男士劲爽控油洁面膏 100g 深层清洁 男士洗面奶 | 38.0 | 25805.0 | 4287.0 | 自然堂 |
4 | 2016/11/14 | A18178045259 | CHANDO/自然堂雪域精粹纯粹滋润霜(清爽型)50g补水保湿滋润霜 | 139.0 | 5196.0 | 618.0 | 自然堂 |
data2.info() # 数据集情况 28010 条,6个字段
<class 'pandas.core.frame.DataFrame'> RangeIndex: 27598 entries, 0 to 27597 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 update_time 27598 non-null object 1 id 27598 non-null object 2 title 27598 non-null object 3 price 27598 non-null float64 4 sale_count 25244 non-null float64 5 comment_count 25244 non-null float64 6 店名 27598 non-null object dtypes: float64(3), object(4) memory usage: 1.5+ MB
data2[data2.duplicated()].count() # 有86条完全重复数据
update_time 86 id 86 title 86 price 86 sale_count 82 comment_count 82 店名 86 dtype: int64
data2.drop_duplicates(inplace=True) # 删除重复数据
data2.reset_index(drop=True, inplace=True) # 重建索引
data2.isnull().sum() # 查看空值 ,销售数量和评论数有空值
update_time 0 id 0 title 0 price 0 sale_count 2350 comment_count 2350 店名 0 dtype: int64
data2.fillna(0, inplace=True) # 空值填充
data2['update_time'] = pd.to_datetime(data2['update_time']).apply(lambda x: x.strftime("%Y-%m-%d")) # 日期格式化,便于统计
data2[data2['sale_count']>0].sort_values(by=['sale_count']).head() # 从数据来看,sale_count 是销售量
update_time | id | title | price | sale_count | comment_count | 店名 | |
---|---|---|---|---|---|---|---|
27042 | 2016-11-05 | A541190557158 | Herborist/佰草集新美肌梦幻曲面贴膜3片 保湿补水 | 1.0 | 1.0 | 0.0 | 佰草集 |
1494 | 2016-11-10 | A538981087285 | 【双II预售】资生堂 新透白色控霜 30ml | 390.0 | 1.0 | 0.0 | 资生堂 |
24148 | 2016-11-09 | A540190519057 | 【娇兰盛典】腮红亲密容和肌肤 裸妆感 自然持久玫瑰闰色腮红 | 420.0 | 1.0 | 0.0 | 娇兰 |
24147 | 2016-11-09 | A540189922026 | 【娇兰盛典】丝柔蜜粉饼 营造细致透明妆感 柔滑细腻贴肤美颜 | 480.0 | 1.0 | 1.0 | 娇兰 |
16974 | 2016-11-05 | A541166044768 | L'OREAL欧莱雅卓韵霜时尚魅棕系列染发霜 富含炫闪因子蜜茶棕红棕 | 79.0 | 1.0 | 0.0 | 欧莱雅 |
data2['sale_amount'] = data2['price'] * data2['sale_count'] # 增加一列销售额
data2[data2['sale_count']>0].sort_values(by=['sale_count'])
update_time | id | title | price | sale_count | comment_count | 店名 | sale_amount | |
---|---|---|---|---|---|---|---|---|
27042 | 2016-11-05 | A541190557158 | Herborist/佰草集新美肌梦幻曲面贴膜3片 保湿补水 | 1.0 | 1.0 | 0.0 | 佰草集 | 1.0 |
1494 | 2016-11-10 | A538981087285 | 【双II预售】资生堂 新透白色控霜 30ml | 390.0 | 1.0 | 0.0 | 资生堂 | 390.0 |
24148 | 2016-11-09 | A540190519057 | 【娇兰盛典】腮红亲密容和肌肤 裸妆感 自然持久玫瑰闰色腮红 | 420.0 | 1.0 | 0.0 | 娇兰 | 420.0 |
24147 | 2016-11-09 | A540189922026 | 【娇兰盛典】丝柔蜜粉饼 营造细致透明妆感 柔滑细腻贴肤美颜 | 480.0 | 1.0 | 1.0 | 娇兰 | 480.0 |
16974 | 2016-11-05 | A541166044768 | L'OREAL欧莱雅卓韵霜时尚魅棕系列染发霜 富含炫闪因子蜜茶棕红棕 | 79.0 | 1.0 | 0.0 | 欧莱雅 | 79.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
17470 | 2016-11-10 | A24304992630 | 德国妮维雅男士洗面奶控油祛痘印保湿去黑头去油清洁面乳液护肤品 | 42.0 | 1827562.0 | 200154.0 | 妮维雅 | 76757604.0 |
17339 | 2016-11-11 | A24304992630 | 2瓶更划算*妮维雅男士洗面奶控油祛痘印保湿去黑头去油洁面乳护肤 | 35.0 | 1886100.0 | 199532.0 | 妮维雅 | 66013500.0 |
17228 | 2016-11-12 | A24304992630 | 德国妮维雅男士洗面奶控油祛痘印保湿去黑头去油清洁面乳液护肤品 | 37.9 | 1920083.0 | 199062.0 | 妮维雅 | 72771145.7 |
17126 | 2016-11-13 | A24304992630 | 德国妮维雅男士洗面奶控油祛痘印保湿去黑头去油清洁面乳液护肤品 | 37.9 | 1921582.0 | 198774.0 | 妮维雅 | 72827957.8 |
17026 | 2016-11-14 | A24304992630 | 德国妮维雅男士洗面奶控油祛痘印保湿去黑头去油清洁面乳液护肤品 | 42.0 | 1923160.0 | 197949.0 | 妮维雅 | 80772720.0 |
24398 rows × 8 columns
result = data2.groupby('update_time').agg({'sale_count':'sum'}).to_dict()['sale_count']
c = (
Line()
.add_xaxis(list(result.keys()))
.add_yaxis("销售量", list(result.values()))
.set_series_opts(
areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
label_opts=opts.LabelOpts(is_show=False),
markpoint_opts=opts.MarkPointOpts(
data=[
opts.MarkPointItem(type_="max", name="最大值"),
opts.MarkPointItem(type_="min", name="最小值"),
opts.MarkPointItem(type_="average", name="平均值"),
]
),
)
.set_global_opts(title_opts=opts.TitleOpts(title="每日整体销售量走势"))
)
c.render_notebook()
dts = list(data2['update_time'].unique())
dts.reverse()
dts
['2016-11-05', '2016-11-06', '2016-11-07', '2016-11-08', '2016-11-09', '2016-11-10', '2016-11-11', '2016-11-12', '2016-11-13', '2016-11-14']
from pyecharts import options as opts
from pyecharts.charts import Map, Timeline, Bar, Line, Pie
from pyecharts.components import Table
from pyecharts.options import ComponentTitleOpts
tl = Timeline()
tl.add_schema(
# is_auto_play=True,
is_loop_play=False,
play_interval=500,
)
for dt in dts:
item = data2[data2['update_time'] <= dt].groupby('店名').agg({'sale_count': 'sum', 'sale_amount': 'sum'}).sort_values(by='sale_count', ascending=False)[:10].sort_values(by='sale_count').to_dict()
bar = (
Bar()
.add_xaxis([*item['sale_count'].keys()])
.add_yaxis("销售量", [round(val/10000,2) for val in item['sale_count'].values()], label_opts=opts.LabelOpts(position="right", formatter='{@[1]/} 万'))
.add_yaxis("销售额", [round(val/10000/10000,2) for val in item['sale_amount'].values()], label_opts=opts.LabelOpts(position="right", formatter='{@[1]/} 亿元'))
.reversal_axis()
.set_global_opts(
title_opts=opts.TitleOpts("累计销售量排行 TOP10")
)
)
tl.add(bar, dt)
tl.render_notebook()
item = data2.groupby('店名').agg({'sale_count': 'sum'}).sort_values(by='sale_count', ascending=False)[:10].to_dict()['sale_count']
item = {k: round(v/10000, 2) for k, v in item.items()}
c = (
Pie()
.add("销量", [*item.items()])
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c} 万({d}%)"))
)
c.render_notebook()
item = data2.groupby('店名').agg({'price': 'mean'}).sort_values(by='price', ascending=False)[:20].sort_values(by='price').to_dict()
c = (
Bar()
.add_xaxis([*item['price'].keys()])
.add_yaxis("销售量", [round(v, 2) for v in item['price'].values()], label_opts=opts.LabelOpts(position="right"))
.reversal_axis()
.set_global_opts(
title_opts=opts.TitleOpts("平均价格排行 TOP20")
)
)
c.render_notebook()
import pandas as pd
fact_order = pd.read_excel('日化.xlsx', sheet_name='销售订单表')
dim_product = pd.read_excel('日化.xlsx', sheet_name='商品信息表')
dim_product.head()
商品编号 | 商品名称 | 商品小类 | 商品大类 | 销售单价 | |
---|---|---|---|---|---|
0 | X001 | 商品1 | 面膜 | 护肤品 | 121 |
1 | X002 | 商品2 | 面膜 | 护肤品 | 141 |
2 | X003 | 商品3 | 面膜 | 护肤品 | 168 |
3 | X004 | 商品4 | 面膜 | 护肤品 | 211 |
4 | X005 | 商品5 | 面膜 | 护肤品 | 185 |
dim_product.describe()
销售单价 | |
---|---|
count | 122.000000 |
mean | 156.155738 |
std | 58.454619 |
min | 56.000000 |
25% | 102.250000 |
50% | 158.000000 |
75% | 210.750000 |
max | 253.000000 |
dim_product[dim_product.duplicated()].count() # 没有完全重复的数据
商品编号 0 商品名称 0 商品小类 0 商品大类 0 销售单价 0 dtype: int64
dim_product[dim_product['商品编号'].duplicated()].count() # ID 唯一没有重复
商品编号 0 商品名称 0 商品小类 0 商品大类 0 销售单价 0 dtype: int64
dim_product.isnull().sum() # 没有空值
商品编号 0 商品名称 0 商品小类 0 商品大类 0 销售单价 0 dtype: int64
fact_order.head()
订单编码 | 订单日期 | 客户编码 | 所在区域 | 所在省份 | 所在地市 | 商品编号 | 订购数量 | 订购单价 | 金额 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | D31313 | 2019-05-16 00:00:00 | S22796 | 东区 | 浙江省 | 台州市 | X091 | 892 | 214 | 190888.0 |
1 | D21329 | 2019-05-14 00:00:00 | S11460 | 东区 | 安徽省 | 宿州市 | X005 | 276 | 185 | 51060.0 |
2 | D22372 | 2019-08-26 00:00:00 | S11101 | 北区 | 山西省 | 忻州市 | X078 | 1450 | 116 | 168200.0 |
3 | D31078 | 2019-04-08 00:00:00 | S10902 | 北区 | 吉林省 | 延边朝鲜族自治州 | X025 | 1834 | 102 | 187068.0 |
4 | D32470 | 2019-04-11 00:00:00 | S18696 | 北区 | 北京市 | 北京市 | X010 | 887 | 58 | 51446.0 |
fact_order.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 31452 entries, 0 to 31451 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 订单编码 31452 non-null object 1 订单日期 31452 non-null object 2 客户编码 31452 non-null object 3 所在区域 31450 non-null object 4 所在省份 31450 non-null object 5 所在地市 31452 non-null object 6 商品编号 31451 non-null object 7 订购数量 31450 non-null object 8 订购单价 31448 non-null object 9 金额 31448 non-null float64 dtypes: float64(1), object(9) memory usage: 2.4+ MB
fact_order[fact_order.duplicated()].count() # 没有完全重复的数据
订单编码 6 订单日期 6 客户编码 6 所在区域 6 所在省份 6 所在地市 6 商品编号 6 订购数量 6 订购单价 6 金额 6 dtype: int64
fact_order.drop_duplicates(inplace=True) # 删除重复数据
fact_order.reset_index(drop=True, inplace=True) # 重建索引
fact_order.isnull().sum() # 查看空值,有几条数据缺失
订单编码 0 订单日期 0 客户编码 0 所在区域 2 所在省份 2 所在地市 0 商品编号 1 订购数量 2 订购单价 4 金额 4 dtype: int64
fact_order.fillna(method='bfill', inplace=True) # 空值填充
fact_order.fillna(method='ffill', inplace=True) # 空值填充
fact_order.isnull().sum() # 查看空值,有几条数据缺失
订单编码 0 订单日期 0 客户编码 0 所在区域 0 所在省份 0 所在地市 0 商品编号 0 订购数量 0 订购单价 0 金额 0 dtype: int64
fact_order['订单日期'] = fact_order['订单日期'].apply(lambda x: pd.to_datetime(x, format='%Y#%m#%d') if isinstance(x, str) else x)
fact_order[fact_order['订单日期'] > '2021-01-01'] # 有一条脏数据
订单编码 | 订单日期 | 客户编码 | 所在区域 | 所在省份 | 所在地市 | 商品编号 | 订购数量 | 订购单价 | 金额 | |
---|---|---|---|---|---|---|---|---|---|---|
20797 | D26533 | 2050-06-09 | S21396 | 北区 | 河北省 | 石家庄市 | X022 | 759 | 158 | 119922.0 |
fact_order = fact_order[fact_order['订单日期'] < '2021-01-01'] # 过滤掉脏数据
fact_order['订单日期'].max(), fact_order['订单日期'].min() # 数据区间在 2019-01-01 到 2019-09-30 之间
(Timestamp('2019-09-30 00:00:00'), Timestamp('2019-01-01 00:00:00'))
fact_order['订购数量'] = fact_order['订购数量'].apply(lambda x: x.strip('个') if isinstance(x, str) else x).astype('int')
fact_order['订购单价'] = fact_order['订购单价'].apply(lambda x: x.strip('元') if isinstance(x, str) else x).astype('float')
fact_order['金额'] = fact_order['金额'].astype('float')
fact_order.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 31445 entries, 0 to 31445 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 订单编码 31445 non-null object 1 订单日期 31445 non-null datetime64[ns] 2 客户编码 31445 non-null object 3 所在区域 31445 non-null object 4 所在省份 31445 non-null object 5 所在地市 31445 non-null object 6 商品编号 31445 non-null object 7 订购数量 31445 non-null int32 8 订购单价 31445 non-null float64 9 金额 31445 non-null float64 dtypes: datetime64[ns](1), float64(2), int32(1), object(6) memory usage: 2.5+ MB
fact_order['所在省份'] = fact_order['所在省份'].str.replace('自治区|维吾尔|回族|壮族|省|市', '') # 对省份做个清洗,便于可视化
fact_order['所在省份'].unique()
array(['浙江', '安徽', '山西', '吉林', '北京', '云南', '广东', '广西', '内蒙古', '新疆', '湖北', '江苏', '甘肃', '四川', '河南', '福建', '陕西', '辽宁', '山东', '江西', '重庆', '河北', '湖南', '上海', '贵州', '天津', '海南', '宁夏', '黑龙江'], dtype=object)
fact_order['客户编码'] = fact_order['客户编码'].str.replace('编号', '')
from pyecharts import options as opts
from pyecharts.charts import Map, Bar, Line
from pyecharts.components import Table
from pyecharts.options import ComponentTitleOpts
from pyecharts.faker import Faker
fact_order['订单月份'] = fact_order['订单日期'].apply(lambda x: x.month)
item = fact_order.groupby('订单月份').agg({'订购数量': 'sum', '金额': 'sum'}).to_dict()
x = [f'{key} 月' for key in item['订购数量'].keys()]
y1 = [round(val/10000, 2) for val in item['订购数量'].values()]
y2 = [round(val/10000/10000, 2) for val in item['金额'].values()]
c = (
Bar()
.add_xaxis(x)
.add_yaxis("订购数量(万件)", y1, is_selected=False)
.add_yaxis("金额(亿元)", y2)
.set_global_opts(title_opts=opts.TitleOpts(title="每月订购情况"))
.set_series_opts(
label_opts=opts.LabelOpts(is_show=True),
)
)
c.render_notebook()
item = fact_order.groupby('所在地市').agg({'订购数量': 'sum'}).sort_values(by='订购数量', ascending=False)[:20].sort_values(by='订购数量').to_dict()['订购数量']
c = (
Bar()
.add_xaxis([*item.keys()])
.add_yaxis("订购量", [round(v/10000, 2) for v in item.values()], label_opts=opts.LabelOpts(position="right", formatter='{@[1]/} 万'))
.reversal_axis()
.set_global_opts(
title_opts=opts.TitleOpts("订购数量排行 TOP20")
)
)
c.render_notebook()
order = pd.merge(fact_order, dim_product, on='商品编号',how='inner') # 表关联
order
订单编码 | 订单日期 | 客户编码 | 所在区域 | 所在省份 | 所在地市 | 商品编号 | 订购数量 | 订购单价 | 金额 | 订单月份 | 商品名称 | 商品小类 | 商品大类 | 销售单价 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | D31313 | 2019-05-16 | S22796 | 东区 | 浙江 | 台州市 | X091 | 892 | 214.0 | 190888.0 | 5 | 商品91 | 粉底 | 彩妆 | 214 |
1 | D26674 | 2019-05-01 | S15128 | 东区 | 江苏 | 南通市 | X091 | 1133 | 214.0 | 242462.0 | 5 | 商品91 | 粉底 | 彩妆 | 214 |
2 | D23381 | 2019-09-22 | S17133 | 东区 | 江苏 | 宿迁市 | X091 | 1136 | 214.0 | 243104.0 | 9 | 商品91 | 粉底 | 彩妆 | 214 |
3 | D29060 | 2019-09-10 | S14106 | 东区 | 江苏 | 常州市 | X091 | 544 | 214.0 | 116416.0 | 9 | 商品91 | 粉底 | 彩妆 | 214 |
4 | D21234 | 2019-07-03 | S17197 | 东区 | 湖北 | 十堰市 | X091 | 342 | 214.0 | 73188.0 | 7 | 商品91 | 粉底 | 彩妆 | 214 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
31439 | D30482 | 2019-06-05 | S11033 | 东区 | 浙江 | 金华市 | X118 | 551 | 238.0 | 131138.0 | 6 | 商品118 | 蜜粉 | 彩妆 | 238 |
31440 | D29542 | 2019-05-01 | S12446 | 东区 | 江苏 | 南通市 | X118 | 165 | 238.0 | 39270.0 | 5 | 商品118 | 蜜粉 | 彩妆 | 238 |
31441 | D24798 | 2019-06-26 | S16170 | 南区 | 福建 | 泉州市 | X118 | 62 | 238.0 | 14756.0 | 6 | 商品118 | 蜜粉 | 彩妆 | 238 |
31442 | D31831 | 2019-08-13 | S22214 | 北区 | 黑龙江 | 佳木斯市 | X118 | 795 | 238.0 | 189210.0 | 8 | 商品118 | 蜜粉 | 彩妆 | 238 |
31443 | D28890 | 2019-07-21 | S15066 | 西区 | 四川 | 南充市 | X118 | 148 | 238.0 | 35224.0 | 7 | 商品118 | 蜜粉 | 彩妆 | 238 |
31444 rows × 15 columns
order.groupby(['商品大类','商品小类']).agg({'订购数量': 'sum'}).sort_values(by=['商品大类', '订购数量'], ascending=[True, False])
订购数量 | ||
---|---|---|
商品大类 | 商品小类 | |
彩妆 | 口红 | 2013024 |
粉底 | 1188621 | |
睫毛膏 | 587399 | |
眼影 | 296599 | |
蜜粉 | 45534 | |
护肤品 | 面膜 | 5451914 |
面霜 | 4566905 | |
爽肤水 | 3523687 | |
眼霜 | 3350743 | |
隔离霜 | 2488124 | |
防晒霜 | 2388610 | |
洁面乳 | 1928020 |
item = fact_order.groupby('所在省份').agg({'订购数量': 'sum'}).to_dict()['订购数量']
c = (
Map()
.add("订购数量", [*item.items()], "china", is_map_symbol_show=False)
.set_series_opts(label_opts=opts.LabelOpts(is_show=True))
.set_global_opts(
title_opts=opts.TitleOpts(title='省份分布'),
visualmap_opts=opts.VisualMapOpts(max_=1000000),
)
)
c.render_notebook()
RFM 模型是衡量客户价值和客户创利能力的重要工具和手段,其中由3个要素构成了数据分析最好的指标,分别是:
设定一个计算权重,比如 R-Recency 20% F-Frequency 30% M-Money 50% ,最后通过这个权重进行打分,量化客户价值,后续还可以基于分数进一步打标签,用来指导二次营销的策略。
data_rfm = fact_order.groupby('客户编码').agg({'订单日期': 'max', '订单编码': 'count', '金额': 'sum'})
data_rfm.columns = ['最近一次购买时间', '消费频率', '消费金额']
data_rfm['R'] = data_rfm['最近一次购买时间'].rank(pct=True) # 转化为排名 百分比,便于后续切片
data_rfm['F'] = data_rfm['消费频率'].rank(pct=True)
data_rfm['M'] = data_rfm['消费金额'].rank(pct=True)
data_rfm.sort_values(by='R', ascending=False)
最近一次购买时间 | 消费频率 | 消费金额 | R | F | M | |
---|---|---|---|---|---|---|
客户编码 | ||||||
S11609 | 2019-09-30 | 42 | 7326027.0 | 0.980148 | 0.796399 | 0.903970 |
S19828 | 2019-09-30 | 21 | 2642275.0 | 0.980148 | 0.356879 | 0.306556 |
S17166 | 2019-09-30 | 17 | 3627037.0 | 0.980148 | 0.261311 | 0.478301 |
S22925 | 2019-09-30 | 31 | 3449117.0 | 0.980148 | 0.591413 | 0.457987 |
S10469 | 2019-09-30 | 30 | 4198071.0 | 0.980148 | 0.570175 | 0.564174 |
... | ... | ... | ... | ... | ... | ... |
S16503 | 2019-04-07 | 14 | 1682893.0 | 0.004617 | 0.198061 | 0.146814 |
S17547 | 2019-03-14 | 10 | 1784531.0 | 0.003232 | 0.087258 | 0.163435 |
S20864 | 2019-03-14 | 8 | 1118752.0 | 0.003232 | 0.039243 | 0.047091 |
S11908 | 2019-03-09 | 9 | 1552311.0 | 0.001847 | 0.060942 | 0.125577 |
S11611 | 2019-03-03 | 10 | 1487966.0 | 0.000923 | 0.087258 | 0.113573 |
1083 rows × 6 columns
data_rfm['score'] = data_rfm['R'] * 20 + data_rfm['F'] * 30 + data_rfm['M'] * 50
data_rfm['score'] = data_rfm['score'].round(1)
data_rfm.sort_values(by='score', ascending=False)
最近一次购买时间 | 消费频率 | 消费金额 | R | F | M | score | |
---|---|---|---|---|---|---|---|
客户编码 | |||||||
S17476 | 2019-09-30 | 69 | 10325832.0 | 0.980148 | 0.986611 | 0.987073 | 98.6 |
S22326 | 2019-09-30 | 62 | 10074609.0 | 0.980148 | 0.973223 | 0.984303 | 98.0 |
S11581 | 2019-09-28 | 79 | 10333668.0 | 0.918283 | 0.996768 | 0.987996 | 97.7 |
S12848 | 2019-09-29 | 66 | 9673572.0 | 0.944598 | 0.980609 | 0.980609 | 97.3 |
S19095 | 2019-09-26 | 81 | 11031632.0 | 0.864728 | 0.999077 | 0.996307 | 97.1 |
... | ... | ... | ... | ... | ... | ... | ... |
S12690 | 2019-05-07 | 7 | 917233.0 | 0.012927 | 0.022622 | 0.024931 | 2.2 |
S11176 | 2019-06-09 | 7 | 614134.0 | 0.036011 | 0.022622 | 0.009234 | 1.9 |
S18379 | 2019-07-05 | 4 | 400195.0 | 0.071099 | 0.003232 | 0.004617 | 1.7 |
S13259 | 2019-06-01 | 6 | 645925.0 | 0.025854 | 0.011542 | 0.011080 | 1.4 |
S12463 | 2019-04-11 | 7 | 345919.0 | 0.005540 | 0.022622 | 0.000923 | 0.8 |
1083 rows × 7 columns
根据这个分数结果,我们可以对客户打上一些标签,比如大于 80 分的,标志为优质客户,在资源有限的情况下,可以优先服务好优质客户。