数据分析实战 - 2 订单销售数据分析(pandas 进阶)
题目来源:和鲸社区的题目推荐:
刷题源链接(用于直接fork运行
https://www.heywhale.com/mw/project/6527b5560259478972ea87ed
刷题准备
请依次运行这部分的代码(下方4个代码块),完成刷题前的数据准备
数据准备:
链接:https://pan.baidu.com/s/1F7iyHys1edRaOTR0LzxiYQ?pwd=m1ve
提取码:m1ve
–来自百度网盘超级会员V4的分享
导包及读取数据
import pandas as pd
# 如遇到OSerror,可以稍等十几秒,等待数据加载完成即可读取df = pd.read_csv("./data/order.csv")
df.head()
| 订单日期 | 年份 | 订单数量 | 产品ID | 客户ID | 交易类型 | 销售区域ID | 销售大区 | 国家 | 区域 | 产品类别 | 产品型号名称 | 产品名称 | 产品成本 | 利润 | 单价 | 销售金额 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2016/1/1 | 2016 | 1 | 528 | 14432BA | 1 | 4 | 西南区 | 中国 | 大中华区 | 配件 | Rawlings Heart of THE Hide-11.5 | 棒球手套 | 500.0 | 1199.0 | 1699.0 | 1699.0 |
| 1 | 2016/1/2 | 2016 | 1 | 528 | 18741BA | 1 | 4 | 西南区 | 中国 | 大中华区 | 配件 | Rawlings Heart of THE Hide-11.5 | 棒球手套 | 500.0 | 1199.0 | 1699.0 | 1699.0 |
| 2 | 2016/1/2 | 2016 | 1 | 528 | 27988BA | 1 | 4 | 西南区 | 中国 | 大中华区 | 配件 | Rawlings Heart of THE Hide-11.5 | 棒球手套 | 500.0 | 1199.0 | 1699.0 | 1699.0 |
| 3 | 2016/1/5 | 2016 | 1 | 528 | 25710BA | 1 | 4 | 西南区 | 中国 | 大中华区 | 配件 | Rawlings Heart of THE Hide-11.5 | 棒球手套 | 500.0 | 1199.0 | 1699.0 | 1699.0 |
| 4 | 2016/1/6 | 2016 | 1 | 528 | 14999BA | 1 | 4 | 西南区 | 中国 | 大中华区 | 配件 | Rawlings Heart of THE Hide-11.5 | 棒球手套 | 500.0 | 1199.0 | 1699.0 | 1699.0 |
数据预处理
# 删除重复值
# df.drop_duplicates(inplace=True)
df.drop_duplicates(inplace = True)
# 查看数据信息
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 60391 entries, 0 to 60397
Data columns (total 17 columns):# Column Non-Null Count Dtype
--- ------ -------------- ----- 0 订单日期 60391 non-null object 1 年份 60391 non-null int64 2 订单数量 60391 non-null int64 3 产品ID 60391 non-null int64 4 客户ID 60391 non-null object 5 交易类型 60391 non-null int64 6 销售区域ID 60391 non-null int64 7 销售大区 60391 non-null object 8 国家 60391 non-null object 9 区域 60391 non-null object 10 产品类别 60391 non-null object 11 产品型号名称 60391 non-null object 12 产品名称 60391 non-null object 13 产品成本 60391 non-null float6414 利润 60391 non-null float6415 单价 60391 non-null float6416 销售金额 60391 non-null float64
dtypes: float64(4), int64(5), object(8)
memory usage: 8.3+ MB
数据集中无缺失值,因此不需要进行缺失值处理
开始刷题
任务1
主管想了解 2013-2016 年各年份的总销售额情况,需要你统计各年份的销售金额,并依据年份降序输出各年的总销售额。
# 你的代码# 观察年份
df['年份'].unique() # array([2016, 2015, 2014, 2013])
# 直接根据年份根据销售金额统计总销售额
gmv_by_year = df.groupby(by = ['年份'])['销售金额'].sum().reset_index().sort_values(by = ['年份'],ascending = False)
gmv_by_year
| 年份 | 销售金额 | |
|---|---|---|
| 3 | 2016 | 22390338.42 |
| 2 | 2015 | 16063868.36 |
| 1 | 2014 | 182951.85 |
| 0 | 2013 | 106308.27 |
任务2
主管想了解在 2013-2016 年各月份的订单数量都有多少?
需要你新增一列数据记录每笔订单的购买年份及月份信息,其值需从订单日期中拆分出来,之后再统计各月份的订单数量并输出订单数量最多的月份,同时并分析订单数量的时间变化趋势。
思考:订单数量是否可以直接使用
sum()方法直接统计? 回答:订单数量只有1,意味着是每一单一行数据,sum()会漏掉空值,如果存在的空值的话,单纯计数使用size()会更好一些。
df['订单数量'].unique()
array([1], dtype=int64)
# 你的代码# 抽取月份信息
df['月份'] = pd.to_datetime(df['订单日期']).dt.strftime("%m")
# 分年分月汇总数据
gmv_by_month = df.groupby(by = ['年份','月份'])['销售金额'].size().reset_index().sort_values(by = ['年份'],ascending = False)
gmv_by_month.columns = ['年份','月份','订单数量']
gmv_by_month.head()
| 年份 | 月份 | 订单数量 | |
|---|---|---|---|
| 36 | 2016 | 07 | 2209 |
| 35 | 2016 | 06 | 5544 |
| 34 | 2016 | 05 | 5515 |
| 33 | 2016 | 04 | 5088 |
| 32 | 2016 | 03 | 4706 |
# 使用idxmax()找到订单数量最多的行
max_order_index = gmv_by_month['订单数量'].idxmax()
# 根据上一步找到的索引,使用loc定位这一行
max_order_row = gmv_by_month.loc[max_order_index]
# 输出结果
print(f"订单数量最多的年月为{max_order_row['年份']}-{max_order_row['月份']},订单数量为{max_order_row['订单数量']}")
订单数量最多的年月为2016-06,订单数量为5544
任务3*
主管想了解 2016 年各个国家的订单数量、销售额及利润详情;
需要你统计在 2016年间各国的订单数量、销售额及利润并依据年份输出结果。
# 你的代码
# 指定年份,分国家的订单数量汇总,销售额求和,利润求和
country_2016 = df[df['年份']==2016].groupby(by = ['国家'])['订单数量','销售金额','利润'].sum().reset_index()
country_2016
C:\Users\chengyuanting\AppData\Local\Temp\ipykernel_14224\3368580504.py:3: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.country_2016 = df[df['年份']==2016].groupby(by = ['国家'])['订单数量','销售金额','利润'].sum().reset_index()
| 国家 | 订单数量 | 销售金额 | 利润 | |
|---|---|---|---|---|
| 0 | 中国 | 11631 | 8127428.30 | 3525236.48 |
| 1 | 中国台湾 | 3579 | 2361700.11 | 969703.64 |
| 2 | 中国澳门 | 6757 | 4361739.64 | 1783523.75 |
| 3 | 中国香港 | 3061 | 1976666.78 | 803100.02 |
| 4 | 新加坡 | 2975 | 2083231.41 | 838558.78 |
| 5 | 韩国 | 4260 | 3479572.18 | 1538321.46 |
任务4*
主管想了解在 2013-2016 年间哪些产品所带来的利润最高;
需要你统计不同产品(产品名称)的订单数量,并根据该产品所属类别(产品类别)分别输出不同类别产品订单数量最多的前 3 种产品。
# 你的代码
# 根据产品类别,产品名称分组,汇总订单数量。根据订单数量降序取前三
goods_grouped = df.groupby(by = ['产品类别','产品名称'])['订单数量'].sum().reset_index()
goods_grouped
| 产品类别 | 产品名称 | 订单数量 | |
|---|---|---|---|
| 0 | 服装 | 击打手套 | 1430 |
| 1 | 服装 | 帽子 | 2190 |
| 2 | 服装 | 捕手护具 | 562 |
| 3 | 服装 | 棒球服 | 3332 |
| 4 | 服装 | 皮带 | 1019 |
| 5 | 服装 | 袜子 | 568 |
| 6 | 球 | 垒球 | 2167 |
| 7 | 球 | 硬式棒球 | 8068 |
| 8 | 球 | 软式棒球 | 4970 |
| 9 | 配件 | 三角网架 | 2121 |
| 10 | 配件 | 垒垫 | 908 |
| 11 | 配件 | 头盔 | 6439 |
| 12 | 配件 | 打击T座 | 249 |
| 13 | 配件 | 棒球手套 | 17327 |
| 14 | 配件 | 球棒与球棒袋 | 7980 |
| 15 | 配件 | 球网 | 328 |
| 16 | 配件 | 装备包 | 733 |
# 接着获取每个产品类别的前三订单数量的产品# 已产品类别继续分组,直接在分组的结果上应用nlargest()
goods_top3 = goods_grouped.groupby(by = ['产品类别']).apply(lambda x:x.nlargest(3,'订单数量')).reset_index(drop = True)
goods_top3
| 产品类别 | 产品名称 | 订单数量 | |
|---|---|---|---|
| 0 | 服装 | 棒球服 | 3332 |
| 1 | 服装 | 帽子 | 2190 |
| 2 | 服装 | 击打手套 | 1430 |
| 3 | 球 | 硬式棒球 | 8068 |
| 4 | 球 | 软式棒球 | 4970 |
| 5 | 球 | 垒球 | 2167 |
| 6 | 配件 | 棒球手套 | 17327 |
| 7 | 配件 | 球棒与球棒袋 | 7980 |
| 8 | 配件 | 头盔 | 6439 |
说明:
1:apply函数中的x在此处代表dataframe的每个分组
2:nlargest()使用方法举例:
df.nlargest(3, '订单数量') df['订单数量'].nlargest(3)
任务5 (方法一:直接写逻辑,重点理解apply的用法)
主管想了解在面向中国的订单中,各月份的订单数量以及利润是怎样的情况;
需要你统计面向中国的各月份订单数量及利润,并同时输出这个订单数量和利润的同比增长及环比增长。
# 你的代码
# 筛选中国的数据,按月汇总订单数量及利润
china_describe = df[df['国家']=='中国'].groupby(by = ['年份','月份'])['订单数量','利润'].sum().reset_index()
china_describe.head() # 观察数据
C:\Users\chengyuanting\AppData\Local\Temp\ipykernel_14224\3318811943.py:3: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.china_describe = df[df['国家']=='中国'].groupby(by = ['年份','月份'])['订单数量','利润'].sum().reset_index()
| 年份 | 月份 | 订单数量 | 利润 | |
|---|---|---|---|---|
| 0 | 2013 | 07 | 41 | 1710.0 |
| 1 | 2013 | 08 | 43 | 1890.0 |
| 2 | 2013 | 09 | 50 | 2160.0 |
| 3 | 2013 | 10 | 56 | 2380.0 |
| 4 | 2013 | 11 | 60 | 2640.0 |
china_describe
| 年份 | 月份 | 订单数量 | 利润 | |
|---|---|---|---|---|
| 0 | 2013 | 07 | 41 | 1710.00 |
| 1 | 2013 | 08 | 43 | 1890.00 |
| 2 | 2013 | 09 | 50 | 2160.00 |
| 3 | 2013 | 10 | 56 | 2380.00 |
| 4 | 2013 | 11 | 60 | 2640.00 |
| 5 | 2013 | 12 | 91 | 3930.00 |
| 6 | 2014 | 01 | 67 | 2810.00 |
| 7 | 2014 | 02 | 34 | 1300.00 |
| 8 | 2014 | 03 | 73 | 3170.00 |
| 9 | 2014 | 04 | 76 | 3220.00 |
| 10 | 2014 | 05 | 93 | 3850.00 |
| 11 | 2014 | 06 | 82 | 3580.00 |
| 12 | 2014 | 07 | 96 | 1857.00 |
| 13 | 2014 | 08 | 98 | 1942.00 |
| 14 | 2014 | 09 | 50 | 976.00 |
| 15 | 2014 | 10 | 48 | 927.00 |
| 16 | 2014 | 11 | 58 | 1200.00 |
| 17 | 2014 | 12 | 86 | 1800.00 |
| 18 | 2015 | 01 | 61 | 1220.00 |
| 19 | 2015 | 02 | 62 | 1215.00 |
| 20 | 2015 | 03 | 51 | 1050.00 |
| 21 | 2015 | 04 | 72 | 1418.00 |
| 22 | 2015 | 05 | 93 | 2017.00 |
| 23 | 2015 | 06 | 86 | 1802.00 |
| 24 | 2015 | 07 | 440 | 90558.85 |
| 25 | 2015 | 08 | 1369 | 461863.99 |
| 26 | 2015 | 09 | 1309 | 396373.18 |
| 27 | 2015 | 10 | 1504 | 509619.29 |
| 28 | 2015 | 11 | 1543 | 528140.77 |
| 29 | 2015 | 12 | 1921 | 568262.25 |
| 30 | 2016 | 01 | 1580 | 486949.53 |
| 31 | 2016 | 02 | 1653 | 506787.84 |
| 32 | 2016 | 03 | 1686 | 491470.15 |
| 33 | 2016 | 04 | 1800 | 541664.95 |
| 34 | 2016 | 05 | 2104 | 598067.76 |
| 35 | 2016 | 06 | 1988 | 569919.58 |
| 36 | 2016 | 07 | 820 | 330376.67 |
df[:1]['订单数量']
0 1
Name: 订单数量, dtype: int64
df[:1]['订单数量'].values
array([1], dtype=int64)
df[:1]['订单数量'].values[0]
1
# 计算上述两个维度的同比增长以及环比增长
def calculate_yoy(row, column_name):"""它需要当前行和列名称作为参数,并查找去年同月的数据。如果找到了去年的数据,它将计算增长率;否则返回 None。"""last_year = china_describe[(china_describe['年份'] == row['年份'] - 1) & (china_describe['月份'] == row['月份'])]if last_year.empty:return Nonereturn (row[column_name] - last_year[column_name].values[0]) / last_year[column_name].values[0] * 100def calculate_mom(row, column_name):last_month_year = row['年份']if row['月份'] == '01':last_month_month = '12'last_month_year -= 1else:last_month_month = str(int(row['月份']) - 1).zfill(2) # zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。last_month = china_describe[(china_describe['年份'] == last_month_year) & (china_describe['月份'] == last_month_month)]if last_month.empty:return Nonereturn (row[column_name] - last_month[column_name].values[0]) / last_month[column_name].values[0] * 100china_describe['订单数量同比'] = china_describe.apply(lambda row: calculate_yoy(row, '订单数量'), axis=1)
china_describe['订单数量环比'] = china_describe.apply(lambda row: calculate_mom(row, '订单数量'), axis=1)
china_describe['利润同比'] = china_describe.apply(lambda row: calculate_yoy(row, '利润'), axis=1)
china_describe['利润环比'] = china_describe.apply(lambda row: calculate_mom(row, '利润'), axis=1)
china_describe
| 年份 | 月份 | 订单数量 | 利润 | 订单数量同比 | 订单数量环比 | 利润同比 | 利润环比 | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2013 | 07 | 41 | 1710.00 | NaN | NaN | NaN | NaN |
| 1 | 2013 | 08 | 43 | 1890.00 | NaN | 4.878049 | NaN | 10.526316 |
| 2 | 2013 | 09 | 50 | 2160.00 | NaN | 16.279070 | NaN | 14.285714 |
| 3 | 2013 | 10 | 56 | 2380.00 | NaN | 12.000000 | NaN | 10.185185 |
| 4 | 2013 | 11 | 60 | 2640.00 | NaN | 7.142857 | NaN | 10.924370 |
| 5 | 2013 | 12 | 91 | 3930.00 | NaN | 51.666667 | NaN | 48.863636 |
| 6 | 2014 | 01 | 67 | 2810.00 | NaN | -26.373626 | NaN | -28.498728 |
| 7 | 2014 | 02 | 34 | 1300.00 | NaN | -49.253731 | NaN | -53.736655 |
| 8 | 2014 | 03 | 73 | 3170.00 | NaN | 114.705882 | NaN | 143.846154 |
| 9 | 2014 | 04 | 76 | 3220.00 | NaN | 4.109589 | NaN | 1.577287 |
| 10 | 2014 | 05 | 93 | 3850.00 | NaN | 22.368421 | NaN | 19.565217 |
| 11 | 2014 | 06 | 82 | 3580.00 | NaN | -11.827957 | NaN | -7.012987 |
| 12 | 2014 | 07 | 96 | 1857.00 | 134.146341 | 17.073171 | 8.596491 | -48.128492 |
| 13 | 2014 | 08 | 98 | 1942.00 | 127.906977 | 2.083333 | 2.751323 | 4.577275 |
| 14 | 2014 | 09 | 50 | 976.00 | 0.000000 | -48.979592 | -54.814815 | -49.742533 |
| 15 | 2014 | 10 | 48 | 927.00 | -14.285714 | -4.000000 | -61.050420 | -5.020492 |
| 16 | 2014 | 11 | 58 | 1200.00 | -3.333333 | 20.833333 | -54.545455 | 29.449838 |
| 17 | 2014 | 12 | 86 | 1800.00 | -5.494505 | 48.275862 | -54.198473 | 50.000000 |
| 18 | 2015 | 01 | 61 | 1220.00 | -8.955224 | -29.069767 | -56.583630 | -32.222222 |
| 19 | 2015 | 02 | 62 | 1215.00 | 82.352941 | 1.639344 | -6.538462 | -0.409836 |
| 20 | 2015 | 03 | 51 | 1050.00 | -30.136986 | -17.741935 | -66.876972 | -13.580247 |
| 21 | 2015 | 04 | 72 | 1418.00 | -5.263158 | 41.176471 | -55.962733 | 35.047619 |
| 22 | 2015 | 05 | 93 | 2017.00 | 0.000000 | 29.166667 | -47.610390 | 42.242595 |
| 23 | 2015 | 06 | 86 | 1802.00 | 4.878049 | -7.526882 | -49.664804 | -10.659395 |
| 24 | 2015 | 07 | 440 | 90558.85 | 358.333333 | 411.627907 | 4776.620894 | 4925.463374 |
| 25 | 2015 | 08 | 1369 | 461863.99 | 1296.938776 | 211.136364 | 23682.903708 | 410.015299 |
| 26 | 2015 | 09 | 1309 | 396373.18 | 2518.000000 | -4.382761 | 40512.006148 | -14.179674 |
| 27 | 2015 | 10 | 1504 | 509619.29 | 3033.333333 | 14.896868 | 54875.112190 | 28.570578 |
| 28 | 2015 | 11 | 1543 | 528140.77 | 2560.344828 | 2.593085 | 43911.730833 | 3.634376 |
| 29 | 2015 | 12 | 1921 | 568262.25 | 2133.720930 | 24.497732 | 31470.125000 | 7.596740 |
| 30 | 2016 | 01 | 1580 | 486949.53 | 2490.163934 | -17.751171 | 39813.895902 | -14.309013 |
| 31 | 2016 | 02 | 1653 | 506787.84 | 2566.129032 | 4.620253 | 41610.933333 | 4.073997 |
| 32 | 2016 | 03 | 1686 | 491470.15 | 3205.882353 | 1.996370 | 46706.680952 | -3.022505 |
| 33 | 2016 | 04 | 1800 | 541664.95 | 2400.000000 | 6.761566 | 38099.220733 | 10.213194 |
| 34 | 2016 | 05 | 2104 | 598067.76 | 2162.365591 | 16.888889 | 29551.351512 | 10.412859 |
| 35 | 2016 | 06 | 1988 | 569919.58 | 2211.627907 | -5.513308 | 31527.057714 | -4.706520 |
| 36 | 2016 | 07 | 820 | 330376.67 | 86.363636 | -58.752515 | 264.819860 | -42.031002 |
提问:为什么上述计算中取值要使用.values[0]取值呢?
回答:# 因为使用apply对每一行数据进行计算,得到的去年数据也是一行,不能使用df[column_name][index]这种方式取值。
索引过程示例:
df[:1]['订单数量'] '''
output:
0 1
Name: 订单数量, dtype: int64
''' df[:1]['订单数量'].values # output : array([1]) df[:1]['订单数量'].values[0] # output : 1 ;正确的取值方式
提问:apply函数应用在DataFrame的行或列中,也可以应用到单独一个Series的每个元素中,我如何判断此处是针对行还是列,还是行的某个值?
回答:当您在 pandas 中使用 apply 函数,是否应用于行还是列主要取决于参数 axis 的值。
axis=0或者不指定axis:函数会应用于每一列,即针对列进行操作。axis=1:函数会应用于每一行,即针对行进行操作。
在我们之前的代码中:
df['订单数量同比'] = df.apply(lambda row: calculate_yoy_corrected(row, '订单数量'), axis=1)
我们指定了 axis=1,这意味着 apply 函数将 lambda 函数应用于数据帧的每一行。lambda 函数会将整行数据(即当前行的所有列值)作为参数传递给 calculate_yoy_corrected 函数,这就是为什么我们可以在 calculate_yoy_corrected 函数内部访问行的 ‘年份’ 和 ‘月份’ 列的值。
如果您将 apply 函数应用于一个 Series,那么该函数会应用于 Series 的每个元素。例如:
df['年份'].apply(lambda x: x + 1)
上述代码会将每个 ‘年份’ 值加 1。
总之,要确定 apply 是应用于行还是列,您可以查看 axis 参数的值。如果 apply 是直接应用于一个 Series,那么它会应用于该 Series 的每个元素。
提问2:lambda函数在处理数据的时候一行一行读取,但是计算是根据整体的数据计算的吗,因为涉及环比同比计算不可能只根据一行数据计算
您是对的。当 apply 函数与 lambda 函数一起使用并设置为 axis=1 时,lambda 函数确实会逐行处理数据。但是,当我们在 lambda 函数内部调用其他函数(如 calculate_yoy 或 calculate_mom)时,那些函数可以访问并使用整个数据框 df 来进行计算。
例如,在我们的 calculate_mom 函数中:
last_month = df[(df['年份'] == last_month_year) & (df['月份'] == last_month_month)]
这里,我们使用整个 df 数据框来查找上个月的数据,尽管 apply 函数正在处理当前行。
因此,虽然 lambda 函数逐行处理数据,但它内部调用的函数可以使用整个数据框进行计算。这正是我们能够计算环比和同比的原因,因为这些计算需要查看其他月份的数据。
任务5 (方法二:简便方法,推荐)
需要注意的是,时间需要升序排列,这样计算出来的才是正确的
# 筛选中国的数据,按月汇总订单数量及利润
china_describe2 = df[df['国家']=='中国'].groupby(by = ['年份','月份'])['订单数量','利润'].sum().reset_index()
china_describe2.head() # 观察数据
C:\Users\chengyuanting\AppData\Local\Temp\ipykernel_14224\3967723160.py:2: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.china_describe2 = df[df['国家']=='中国'].groupby(by = ['年份','月份'])['订单数量','利润'].sum().reset_index()
| 年份 | 月份 | 订单数量 | 利润 | |
|---|---|---|---|---|
| 0 | 2013 | 07 | 41 | 1710.0 |
| 1 | 2013 | 08 | 43 | 1890.0 |
| 2 | 2013 | 09 | 50 | 2160.0 |
| 3 | 2013 | 10 | 56 | 2380.0 |
| 4 | 2013 | 11 | 60 | 2640.0 |
china_describe2['订单数量环比'] = china_describe2['订单数量'].pct_change().fillna(0)*100
china_describe2['利润环比'] = china_describe2['利润'].pct_change().fillna(0)*100
china_describe2['订单数量同比'] = china_describe2.groupby(by = ['月份'])['订单数量'].pct_change().fillna(0)*100
china_describe2['利润同比'] = china_describe2.groupby(by = ['月份'])['利润'].pct_change().fillna(0)*100
china_describe2
| 年份 | 月份 | 订单数量 | 利润 | 订单数量环比 | 利润环比 | 订单数量同比 | 利润同比 | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2013 | 07 | 41 | 1710.00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 1 | 2013 | 08 | 43 | 1890.00 | 4.878049 | 10.526316 | 0.000000 | 0.000000 |
| 2 | 2013 | 09 | 50 | 2160.00 | 16.279070 | 14.285714 | 0.000000 | 0.000000 |
| 3 | 2013 | 10 | 56 | 2380.00 | 12.000000 | 10.185185 | 0.000000 | 0.000000 |
| 4 | 2013 | 11 | 60 | 2640.00 | 7.142857 | 10.924370 | 0.000000 | 0.000000 |
| 5 | 2013 | 12 | 91 | 3930.00 | 51.666667 | 48.863636 | 0.000000 | 0.000000 |
| 6 | 2014 | 01 | 67 | 2810.00 | -26.373626 | -28.498728 | 0.000000 | 0.000000 |
| 7 | 2014 | 02 | 34 | 1300.00 | -49.253731 | -53.736655 | 0.000000 | 0.000000 |
| 8 | 2014 | 03 | 73 | 3170.00 | 114.705882 | 143.846154 | 0.000000 | 0.000000 |
| 9 | 2014 | 04 | 76 | 3220.00 | 4.109589 | 1.577287 | 0.000000 | 0.000000 |
| 10 | 2014 | 05 | 93 | 3850.00 | 22.368421 | 19.565217 | 0.000000 | 0.000000 |
| 11 | 2014 | 06 | 82 | 3580.00 | -11.827957 | -7.012987 | 0.000000 | 0.000000 |
| 12 | 2014 | 07 | 96 | 1857.00 | 17.073171 | -48.128492 | 134.146341 | 8.596491 |
| 13 | 2014 | 08 | 98 | 1942.00 | 2.083333 | 4.577275 | 127.906977 | 2.751323 |
| 14 | 2014 | 09 | 50 | 976.00 | -48.979592 | -49.742533 | 0.000000 | -54.814815 |
| 15 | 2014 | 10 | 48 | 927.00 | -4.000000 | -5.020492 | -14.285714 | -61.050420 |
| 16 | 2014 | 11 | 58 | 1200.00 | 20.833333 | 29.449838 | -3.333333 | -54.545455 |
| 17 | 2014 | 12 | 86 | 1800.00 | 48.275862 | 50.000000 | -5.494505 | -54.198473 |
| 18 | 2015 | 01 | 61 | 1220.00 | -29.069767 | -32.222222 | -8.955224 | -56.583630 |
| 19 | 2015 | 02 | 62 | 1215.00 | 1.639344 | -0.409836 | 82.352941 | -6.538462 |
| 20 | 2015 | 03 | 51 | 1050.00 | -17.741935 | -13.580247 | -30.136986 | -66.876972 |
| 21 | 2015 | 04 | 72 | 1418.00 | 41.176471 | 35.047619 | -5.263158 | -55.962733 |
| 22 | 2015 | 05 | 93 | 2017.00 | 29.166667 | 42.242595 | 0.000000 | -47.610390 |
| 23 | 2015 | 06 | 86 | 1802.00 | -7.526882 | -10.659395 | 4.878049 | -49.664804 |
| 24 | 2015 | 07 | 440 | 90558.85 | 411.627907 | 4925.463374 | 358.333333 | 4776.620894 |
| 25 | 2015 | 08 | 1369 | 461863.99 | 211.136364 | 410.015299 | 1296.938776 | 23682.903708 |
| 26 | 2015 | 09 | 1309 | 396373.18 | -4.382761 | -14.179674 | 2518.000000 | 40512.006148 |
| 27 | 2015 | 10 | 1504 | 509619.29 | 14.896868 | 28.570578 | 3033.333333 | 54875.112190 |
| 28 | 2015 | 11 | 1543 | 528140.77 | 2.593085 | 3.634376 | 2560.344828 | 43911.730833 |
| 29 | 2015 | 12 | 1921 | 568262.25 | 24.497732 | 7.596740 | 2133.720930 | 31470.125000 |
| 30 | 2016 | 01 | 1580 | 486949.53 | -17.751171 | -14.309013 | 2490.163934 | 39813.895902 |
| 31 | 2016 | 02 | 1653 | 506787.84 | 4.620253 | 4.073997 | 2566.129032 | 41610.933333 |
| 32 | 2016 | 03 | 1686 | 491470.15 | 1.996370 | -3.022505 | 3205.882353 | 46706.680952 |
| 33 | 2016 | 04 | 1800 | 541664.95 | 6.761566 | 10.213194 | 2400.000000 | 38099.220733 |
| 34 | 2016 | 05 | 2104 | 598067.76 | 16.888889 | 10.412859 | 2162.365591 | 29551.351512 |
| 35 | 2016 | 06 | 1988 | 569919.58 | -5.513308 | -4.706520 | 2211.627907 | 31527.057714 |
| 36 | 2016 | 07 | 820 | 330376.67 | -58.752515 | -42.031002 | 86.363636 | 264.819860 |
提问:计算同比使用了groupby,这样经过分组计算的数据,和直接计算的数据放一起不会造成对应不上的麻烦吗
回答:伟大的观察!
当您将’ groupby() ‘与’ pct_change() ‘等操作结合使用时,结果数据仍然保留其原始顺序和索引,并且该操作应用于每个组。因此,即使在分组并应用’ pct_change() '方法之后,数据的顺序仍然保持不变,并且结果与原始数据正确地对齐。
在前面的代码中:
df['订单数量_YoY '] = df.groupby(“月份”)(“订单数量的].pct_change () .fillna (0) * 100
事情是这样的:
- 数据按“月”进行分组。
- 在每个月份组中,’ pct_change() '方法计算同比变化。
- 结果保留了原始DataFrame的顺序和索引。
- 计算同比变化分配给新列”订单数量_YoY”。
因此,数据中没有不匹配或不对齐。计算值与DataFrame中的原始行正确对齐。
提问:如果分组后跟着聚合函数比如sum也会不变吗,或者说.fillna函数很重要,即使使用了聚合函数,因为会被填充数字,也不会担心顺序对不上的情况
回答:你的问题涉及到熊猫行为的几个细微差别。让我们来分析一下:
- ** ’ groupby() ‘后面跟着转换(如’ pct_change() ')😗*
当您使用’ groupby() ‘和’ pct_change() '等转换函数时,生成的Series或DataFrame将保持与原始序列相同的形状和索引。这确保了转换后的数据与原始数据保持一致。
- ** ’ groupby() ‘后面跟着聚合(如’ sum() ')😗*
当您使用’ groupby() ‘后面跟着’ sum() ‘这样的聚合函数时,生成的Series或DataFrame通常更小(除非使用’ as_index=False '),因为它将数据聚合到组中。这意味着如果没有进一步处理,结果数据可能不会直接与原始数据对齐。
- ** ’ .fillna() '的作用:**
’ .fillna() ‘方法用于处理缺失值(nan)。在前面的代码中,它的主要作用是在计算’ pct_change() '时处理由每组中的第一个值产生的nan。它不会直接影响数据的形状或对齐方式。然而,重要的是要使用它来确保丢失的值得到适当的处理。
总结:
-对于像’ pct_change() ‘这样的转换,数据在’ groupby() ‘之后保持其形状,确保对齐。
-对于像’ sum() '这样的聚合,数据可能不会保持其原始形状,从而导致潜在的不对齐。
- ’ .fillna() '方法对于处理nan是必不可少的,但它本身并不影响数据对齐。
如果您不确定如何转换数据,那么在每一步检查结果或使用较小的数据子集来测试和理解转换总是一个很好的做法。
任务5 (方法三:方法二的进一步优化,推荐方法)
计算同比增长率和环比增长率,使用pct_change这个函数可以用来计算百分比变化率,当periods=1计算的是环比增长率,当periods=12计算的是同比增长率
# 筛选中国的数据,按月汇总订单数量及利润
china_describe3 = df[df['国家']=='中国'].groupby(by = ['年份','月份'])['订单数量','利润'].sum().reset_index()
china_describe3.head() # 观察数据
# 计算同环比
china_describe3['订单数量环比'] = china_describe3['订单数量'].pct_change()*100
china_describe3['利润环比'] = china_describe3['利润'].pct_change()*100
china_describe3['订单数量同比'] = china_describe3['订单数量'].pct_change(periods = 12)*100
china_describe3['利润同比'] = china_describe3['利润'].pct_change(periods = 12)*100
china_describe3
C:\Users\chengyuanting\AppData\Local\Temp\ipykernel_14224\2071208107.py:2: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.china_describe3 = df[df['国家']=='中国'].groupby(by = ['年份','月份'])['订单数量','利润'].sum().reset_index()
| 年份 | 月份 | 订单数量 | 利润 | 订单数量环比 | 利润环比 | 订单数量同比 | 利润同比 | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2013 | 07 | 41 | 1710.00 | NaN | NaN | NaN | NaN |
| 1 | 2013 | 08 | 43 | 1890.00 | 4.878049 | 10.526316 | NaN | NaN |
| 2 | 2013 | 09 | 50 | 2160.00 | 16.279070 | 14.285714 | NaN | NaN |
| 3 | 2013 | 10 | 56 | 2380.00 | 12.000000 | 10.185185 | NaN | NaN |
| 4 | 2013 | 11 | 60 | 2640.00 | 7.142857 | 10.924370 | NaN | NaN |
| 5 | 2013 | 12 | 91 | 3930.00 | 51.666667 | 48.863636 | NaN | NaN |
| 6 | 2014 | 01 | 67 | 2810.00 | -26.373626 | -28.498728 | NaN | NaN |
| 7 | 2014 | 02 | 34 | 1300.00 | -49.253731 | -53.736655 | NaN | NaN |
| 8 | 2014 | 03 | 73 | 3170.00 | 114.705882 | 143.846154 | NaN | NaN |
| 9 | 2014 | 04 | 76 | 3220.00 | 4.109589 | 1.577287 | NaN | NaN |
| 10 | 2014 | 05 | 93 | 3850.00 | 22.368421 | 19.565217 | NaN | NaN |
| 11 | 2014 | 06 | 82 | 3580.00 | -11.827957 | -7.012987 | NaN | NaN |
| 12 | 2014 | 07 | 96 | 1857.00 | 17.073171 | -48.128492 | 134.146341 | 8.596491 |
| 13 | 2014 | 08 | 98 | 1942.00 | 2.083333 | 4.577275 | 127.906977 | 2.751323 |
| 14 | 2014 | 09 | 50 | 976.00 | -48.979592 | -49.742533 | 0.000000 | -54.814815 |
| 15 | 2014 | 10 | 48 | 927.00 | -4.000000 | -5.020492 | -14.285714 | -61.050420 |
| 16 | 2014 | 11 | 58 | 1200.00 | 20.833333 | 29.449838 | -3.333333 | -54.545455 |
| 17 | 2014 | 12 | 86 | 1800.00 | 48.275862 | 50.000000 | -5.494505 | -54.198473 |
| 18 | 2015 | 01 | 61 | 1220.00 | -29.069767 | -32.222222 | -8.955224 | -56.583630 |
| 19 | 2015 | 02 | 62 | 1215.00 | 1.639344 | -0.409836 | 82.352941 | -6.538462 |
| 20 | 2015 | 03 | 51 | 1050.00 | -17.741935 | -13.580247 | -30.136986 | -66.876972 |
| 21 | 2015 | 04 | 72 | 1418.00 | 41.176471 | 35.047619 | -5.263158 | -55.962733 |
| 22 | 2015 | 05 | 93 | 2017.00 | 29.166667 | 42.242595 | 0.000000 | -47.610390 |
| 23 | 2015 | 06 | 86 | 1802.00 | -7.526882 | -10.659395 | 4.878049 | -49.664804 |
| 24 | 2015 | 07 | 440 | 90558.85 | 411.627907 | 4925.463374 | 358.333333 | 4776.620894 |
| 25 | 2015 | 08 | 1369 | 461863.99 | 211.136364 | 410.015299 | 1296.938776 | 23682.903708 |
| 26 | 2015 | 09 | 1309 | 396373.18 | -4.382761 | -14.179674 | 2518.000000 | 40512.006148 |
| 27 | 2015 | 10 | 1504 | 509619.29 | 14.896868 | 28.570578 | 3033.333333 | 54875.112190 |
| 28 | 2015 | 11 | 1543 | 528140.77 | 2.593085 | 3.634376 | 2560.344828 | 43911.730833 |
| 29 | 2015 | 12 | 1921 | 568262.25 | 24.497732 | 7.596740 | 2133.720930 | 31470.125000 |
| 30 | 2016 | 01 | 1580 | 486949.53 | -17.751171 | -14.309013 | 2490.163934 | 39813.895902 |
| 31 | 2016 | 02 | 1653 | 506787.84 | 4.620253 | 4.073997 | 2566.129032 | 41610.933333 |
| 32 | 2016 | 03 | 1686 | 491470.15 | 1.996370 | -3.022505 | 3205.882353 | 46706.680952 |
| 33 | 2016 | 04 | 1800 | 541664.95 | 6.761566 | 10.213194 | 2400.000000 | 38099.220733 |
| 34 | 2016 | 05 | 2104 | 598067.76 | 16.888889 | 10.412859 | 2162.365591 | 29551.351512 |
| 35 | 2016 | 06 | 1988 | 569919.58 | -5.513308 | -4.706520 | 2211.627907 | 31527.057714 |
| 36 | 2016 | 07 | 820 | 330376.67 | -58.752515 | -42.031002 | 86.363636 | 264.819860 |
任务6
主管想了解面向 中国 的订单中,哪个销售大区在 2016 年的订单数量以及利润最高?
需要你统计输出其结果,同时,主管还想知道不同销售大区的订单数量最多的产品、最少的产品分别是哪几类(产品类型),需要你统计并输出其结果。
# 你的代码
# 输出中国地区各销售大区2016年的订单数量以及最高利润
china_bigarea_2016 = df[(df['国家']=='中国') & (df['年份']==2016)].groupby(by = ['销售大区'])['订单数量','利润'].sum().reset_index()
print(china_bigarea_2016,'\n')
print(f"中国2016年{china_bigarea_2016['销售大区'][china_bigarea_2016['订单数量'].idxmax()]}的订单数量最高,订单数量为:{china_bigarea_2016['订单数量'][china_bigarea_2016['订单数量'].idxmax()]};\n{china_bigarea_2016['销售大区'][china_bigarea_2016['利润'].idxmax()]}的利润最高,利润为:{china_bigarea_2016['利润'][china_bigarea_2016['利润'].idxmax()]}",'\n')# 不同销售大区订单数量最多/最少的产品类型是哪些
china_bigarea_2016_2 = df[(df['国家']=='中国') & (df['年份']==2016)].groupby(by = ['销售大区','产品类别'])['订单数量'].sum().reset_index()
# print(china_bigarea_2016_2,'\n')
# 不同销售大区订单数量最多的产品类型
china_bigarea_2016_2_max = china_bigarea_2016_2.groupby(by = ['销售大区']).apply(lambda x:x.nlargest(1,'订单数量')).reset_index(drop = True)
print(china_bigarea_2016_2_max,'\n')
# 不同销售大区订单数量最少的产品类型
china_bigarea_2016_2_min = china_bigarea_2016_2.groupby(by = ['销售大区']).apply(lambda x:x.nsmallest(1,'订单数量')).reset_index(drop = True)
print(china_bigarea_2016_2_min,'\n')
销售大区 订单数量 利润
0 东北区 15 6529.89
1 东南区 30 7592.59
2 中部 10 4207.80
3 西北区 4952 1583580.11
4 西南区 6624 1923326.09 中国2016年西南区的订单数量最高,订单数量为:6624;
西南区的利润最高,利润为:1923326.09 销售大区 产品类别 订单数量
0 东北区 配件 11
1 东南区 配件 20
2 中部 配件 8
3 西北区 配件 3316
4 西南区 配件 4273 销售大区 产品类别 订单数量
0 东北区 球 1
1 东南区 球 4
2 中部 服装 2
3 西北区 球 750
4 西南区 服装 1139
C:\Users\chengyuanting\AppData\Local\Temp\ipykernel_14224\973851953.py:3: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.china_bigarea_2016 = df[(df['国家']=='中国') & (df['年份']==2016)].groupby(by = ['销售大区'])['订单数量','利润'].sum().reset_index()
任务7
主管想了解在所有订单中,服装类(产品类别)产品的整体订单数量月变化趋势如何,需要你统计并依据年份输出各月份的订单数量。
# 你的代码
# 获取服装品类的数据
cloth_cat_by_month = df[df['产品类别']=='服装'].groupby(by = ['年份','月份'])['订单数量'].sum().reset_index()
cloth_cat_by_month
| 年份 | 月份 | 订单数量 | |
|---|---|---|---|
| 0 | 2015 | 07 | 179 |
| 1 | 2015 | 08 | 642 |
| 2 | 2015 | 09 | 684 |
| 3 | 2015 | 10 | 689 |
| 4 | 2015 | 11 | 677 |
| 5 | 2015 | 12 | 837 |
| 6 | 2016 | 01 | 776 |
| 7 | 2016 | 02 | 772 |
| 8 | 2016 | 03 | 772 |
| 9 | 2016 | 04 | 838 |
| 10 | 2016 | 05 | 848 |
| 11 | 2016 | 06 | 951 |
| 12 | 2016 | 07 | 436 |
# 绘图观察数据变化趋势
import pandas as pd
import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# Convert 年份 and 月份 to a single datetime column
cloth_cat_by_month['年月'] = pd.to_datetime(cloth_cat_by_month['年份'].astype(str) + '-' + cloth_cat_by_month['月份'].astype(str) )# Plot
plt.figure(figsize=(12, 6))
plt.plot(cloth_cat_by_month['年月'], cloth_cat_by_month['订单数量'], marker='o', linestyle='-')# Adding data labels
for idx, row in cloth_cat_by_month.iterrows():plt.annotate(row['订单数量'], (row['年月'], row['订单数量']), textcoords="offset points", xytext=(0,5), ha='center')plt.title('服装订单数量随时间变化趋势')
plt.xlabel('年月')
plt.ylabel('订单数量')
plt.grid(True)
plt.tight_layout() # 自动调整子图的位置,在某些情况下,如果不使用tight_layout(), 图形元素可能会重叠或被剪裁。
plt.show()

任务8
基于任务7的输出结果,主管需要你统计在各年份订单数量最低的月份里,在 服装类(产品类别)的产品中,哪些产品(产品名称)的订单是最少的?
# 你的代码# 获取服装名称的数据
cloth_goods_by_month = df[df['产品类别']=='服装'].groupby(by = ['年份','月份','产品名称'])['订单数量'].sum().reset_index()
# 获取7月数据,因为7月订单数量最低
cloth_goods_by_month_7 = cloth_goods_by_month[cloth_goods_by_month['月份']=='07']
# 获取订单数量最低的产品名称
cloth_goods_by_month_7.groupby(by = ['年份','月份']).apply(lambda x : x.nsmallest(1,'订单数量')).reset_index(drop = True)
| 年份 | 月份 | 产品名称 | 订单数量 | |
|---|---|---|---|---|
| 0 | 2015 | 07 | 皮带 | 5 |
| 1 | 2016 | 07 | 袜子 | 25 |
# 任务8补充,如果绘图数据展示不好分辨,还是需要判断订单数量最小的年月
# 筛选数据
cloth_goods_by_month_2 = df[df['产品类别']=='服装'].groupby(by = ['年份','月份'])['订单数量'].sum().reset_index()
# 找按年分组订单数量最小的索引
cloth_min_index = cloth_goods_by_month_2.groupby(by = ['年份'])['订单数量'].idxmin()
# 输出结果
cloth_min = cloth_goods_by_month_2.loc[cloth_min_index]
cloth_min
| 年份 | 月份 | 订单数量 | |
|---|---|---|---|
| 0 | 2015 | 07 | 179 |
| 12 | 2016 | 07 | 436 |
任务9
主管想知道在面向 中国 的 大中华区域 的订单中不同用户的购物特点是怎样的;
需要你先依据 客户ID 统计每位客户的购物次数及购物花费总金额,并在此基础计算每位客户的平均购物花费。
# 你的代码
# 该区域数据
dzh_of_china = df[(df['国家']=='中国') & (df['区域']=='大中华区')]
# 统计购物次数以及花费总金额
dzh_of_china_describe = dzh_of_china.groupby(by = ['客户ID'])['订单数量','销售金额'].sum().reset_index()
dzh_of_china_describe
# 该区域客户总数
dzh_kehu_counts = len(dzh_of_china_describe)
# 每位客户的平均花费
dzh_of_china_describe['平均购物花销'] = dzh_of_china_describe['销售金额']/ dzh_of_china_describe['订单数量']
dzh_of_china_describe
C:\Users\chengyuanting\AppData\Local\Temp\ipykernel_14224\4058715340.py:5: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.dzh_of_china_describe = dzh_of_china.groupby(by = ['客户ID'])['订单数量','销售金额'].sum().reset_index()
| 客户ID | 订单数量 | 销售金额 | 平均购物花销 | |
|---|---|---|---|---|
| 0 | 13033BA | 5 | 6965.90 | 1393.180000 |
| 1 | 13034BA | 5 | 3986.80 | 797.360000 |
| 2 | 13035BA | 6 | 4026.70 | 671.116667 |
| 3 | 13036BA | 3 | 442.99 | 147.663333 |
| 4 | 13037BA | 3 | 3902.99 | 1300.996667 |
| ... | ... | ... | ... | ... |
| 7814 | 31370BA | 2 | 1418.90 | 709.450000 |
| 7815 | 31371BA | 1 | 129.00 | 129.000000 |
| 7816 | 31373BA | 1 | 129.00 | 129.000000 |
| 7817 | 31374BA | 3 | 1458.80 | 486.266667 |
| 7818 | 31375BA | 1 | 129.00 | 129.000000 |
7819 rows × 4 columns
任务10
根据任务9所计算的结果,主管想更加直观的了解不同客户的购物特点,
需要你新增两列分别对客户购物次数以及平均购物花费进行分组划分,针对购物次数可以划分为"高"、"中"、"低"等共三种频次;
针对平均购物花费可将其划分为 "高"、"中"、"低"三种消费等级;
之后再将两列结果进行合并,经过排列组合形成如 "高高"、"高中"等共九种用户购物特征;
最后再统计不同特征的用户最喜欢购买的产品(产品类别)是哪些,并将结果输出。
任务10 方法一:均等分箱
# 定义箱子
bins_order = [0,dzh_of_china_describe['订单数量'].quantile(0.33),dzh_of_china_describe['订单数量'].quantile(0.66),dzh_of_china_describe['订单数量'].max()+1]
labels_order = ['低','中','高']bins_cost = [0,dzh_of_china_describe['平均购物花销'].quantile(0.33),dzh_of_china_describe['平均购物花销'].quantile(0.66),dzh_of_china_describe['平均购物花销'].max()+1]
labels_cost = ['低','中','高']# 为消费频次等级和购物等级两列
dzh_of_china_describe['购物频次'] = pd.cut(dzh_of_china_describe['订单数量'],bins = bins_order,labels = labels_order,right = False)
dzh_of_china_describe['消费等级'] = pd.cut(dzh_of_china_describe['平均购物花销'],bins = bins_cost,labels = labels_cost,right = False)# 合并两种购物特征
dzh_of_china_describe['购物特征'] = dzh_of_china_describe['购物频次'].astype('str') + dzh_of_china_describe['消费等级'].astype('str')# 将购物特征合并到大众华区的数据上
merged_df = pd.merge(dzh_of_china,dzh_of_china_describe[['客户ID','购物特征']],on = '客户ID')# 输出偏好产品
# favorite_products = merged_df.groupby(by = ['购物特征'])['产品类别'].apply(lambda x : x.value_counts().nlargest(1))
favorite_products = merged_df.groupby(by = ['购物特征'])['产品类别'].apply(lambda x : x.value_counts().idxmax())
favorite_products
购物特征
中中 配件
中低 球
中高 配件
低中 配件
低低 球
低高 配件
高中 配件
高低 球
高高 配件
Name: 产品类别, dtype: object
cut函数的right参数决定了区间的闭合方式。
- 如果
right=True(默认值):则每个区间的右边是闭合的,也就是说,区间会是这样的形式:[a, b]、[b, c]、[c, d] 等。 - 如果
right=False:则每个区间的左边是闭合的,也就是说,区间会是这样的形式:(a, b]、(b, c]、(c, d] 等。
具体例子:
考虑一个简单的数据集:[1, 2, 3, 4, 5]和一个区间边界[1, 3, 5]。
- 使用
right=True,数据将被分箱为:[1, 3] 和 [3, 5]。此时,数字3将属于第二个箱子。 - 使用
right=False,数据将被分箱为:(1, 3] 和 (3, 5]。此时,数字3将属于第一个箱子。
因此,right参数的值会影响数据点如何被分配到不同的箱子中。
任务10 方法二:聚类分箱
dzh_of_china_describe2 = dzh_of_china_describe[['客户ID','订单数量','销售金额','平均购物花销']].copy()
dzh_of_china_2 = dzh_of_china.copy()
from sklearn.cluster import KMeans# Using K-means for clustering binning on "订单数量"
kmeans_order = KMeans(n_clusters=3, random_state=0).fit(dzh_of_china_describe2[['订单数量']])
dzh_of_china_describe2['购物频次_cluster'] = kmeans_order.labels_# Map the cluster labels to the desired labels: '低', '中', '高'
order_centers = kmeans_order.cluster_centers_.flatten()
order_label_map = {i: '低' if center == min(order_centers) else '高' if center == max(order_centers) else '中' for i, center in enumerate(order_centers)}
dzh_of_china_describe2['购物频次'] = dzh_of_china_describe2['购物频次_cluster'].map(order_label_map)# Drop the cluster label column
dzh_of_china_describe2.drop('购物频次_cluster', axis=1, inplace=True)dzh_of_china_describe2
C:\Users\chengyuanting\AppData\Roaming\Python\Python39\site-packages\sklearn\cluster\_kmeans.py:1416: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warningsuper()._check_params_vs_input(X, default_n_init=10)
| 客户ID | 订单数量 | 销售金额 | 平均购物花销 | 购物频次 | |
|---|---|---|---|---|---|
| 0 | 13033BA | 5 | 6965.90 | 1393.180000 | 高 |
| 1 | 13034BA | 5 | 3986.80 | 797.360000 | 高 |
| 2 | 13035BA | 6 | 4026.70 | 671.116667 | 高 |
| 3 | 13036BA | 3 | 442.99 | 147.663333 | 中 |
| 4 | 13037BA | 3 | 3902.99 | 1300.996667 | 中 |
| ... | ... | ... | ... | ... | ... |
| 7814 | 31370BA | 2 | 1418.90 | 709.450000 | 低 |
| 7815 | 31371BA | 1 | 129.00 | 129.000000 | 低 |
| 7816 | 31373BA | 1 | 129.00 | 129.000000 | 低 |
| 7817 | 31374BA | 3 | 1458.80 | 486.266667 | 中 |
| 7818 | 31375BA | 1 | 129.00 | 129.000000 | 低 |
7819 rows × 5 columns
# Using K-means for clustering binning on "平均购物花销"
kmeans_cost = KMeans(n_clusters=3, random_state=0).fit(dzh_of_china_describe2[['平均购物花销']])
dzh_of_china_describe2['消费等级_cluster'] = kmeans_cost.labels_# Map the cluster labels to the desired labels: '低', '中', '高'
cost_centers = kmeans_cost.cluster_centers_.flatten()
cost_label_map = {i: '低' if center == min(cost_centers) else '高' if center == max(cost_centers) else '中' for i, center in enumerate(cost_centers)}
dzh_of_china_describe2['消费等级'] = dzh_of_china_describe2['消费等级_cluster'].map(cost_label_map)# Drop the cluster label column
dzh_of_china_describe2.drop('消费等级_cluster', axis=1, inplace=True)dzh_of_china_describe2
C:\Users\chengyuanting\AppData\Roaming\Python\Python39\site-packages\sklearn\cluster\_kmeans.py:1416: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warningsuper()._check_params_vs_input(X, default_n_init=10)
| 客户ID | 订单数量 | 销售金额 | 平均购物花销 | 购物频次 | 消费等级 | |
|---|---|---|---|---|---|---|
| 0 | 13033BA | 5 | 6965.90 | 1393.180000 | 高 | 中 |
| 1 | 13034BA | 5 | 3986.80 | 797.360000 | 高 | 中 |
| 2 | 13035BA | 6 | 4026.70 | 671.116667 | 高 | 中 |
| 3 | 13036BA | 3 | 442.99 | 147.663333 | 中 | 低 |
| 4 | 13037BA | 3 | 3902.99 | 1300.996667 | 中 | 中 |
| ... | ... | ... | ... | ... | ... | ... |
| 7814 | 31370BA | 2 | 1418.90 | 709.450000 | 低 | 中 |
| 7815 | 31371BA | 1 | 129.00 | 129.000000 | 低 | 低 |
| 7816 | 31373BA | 1 | 129.00 | 129.000000 | 低 | 低 |
| 7817 | 31374BA | 3 | 1458.80 | 486.266667 | 中 | 低 |
| 7818 | 31375BA | 1 | 129.00 | 129.000000 | 低 | 低 |
7819 rows × 6 columns
# 合并两种购物特征
dzh_of_china_describe2['购物特征'] = dzh_of_china_describe2['购物频次'].astype('str') + dzh_of_china_describe2['消费等级'].astype('str')# 将购物特征合并到大众华区的数据上
merged_df2 = pd.merge(dzh_of_china,dzh_of_china_describe2[['客户ID','购物特征']],on = '客户ID')# 输出偏好产品
# favorite_products2 = merged_df2.groupby(by = ['购物特征'])['产品类别'].apply(lambda x : x.value_counts().nlargest(1))
favorite_products2 = merged_df2.groupby(by = ['购物特征'])['产品类别'].apply(lambda x : x.value_counts().idxmax())
favorite_products2
购物特征
中中 配件
中低 配件
中高 配件
低中 配件
低低 配件
低高 配件
高中 配件
高低 配件
高高 配件
Name: 产品类别, dtype: object
提问:上述聚类分箱时,是怎么保证高的数据不会打错高中低标签呢
回答:
当我们使用K-means聚类进行分箱时,算法会为我们的数据找到三个中心(假设我们选择了3个簇)。这三个中心分别代表了数据的三个“平均”或“典型”值。为了将这三个中心与“低”、“中”和“高”标签正确地匹配起来,我们执行了以下操作:
- 获取这三个中心的值。
- 找到这三个值中的最小值、最大值和中间值。
- 将最小值映射到“低”,将最大值映射到“高”,将中间值映射到“中”。
通过这种方式,我们确保了正确地标记了每个数据点的标签。
这是上述代码的相关部分,用于映射簇标签到我们的“低”、“中”和“高”标签:
order_centers = kmeans_order.cluster_centers_.flatten()
order_label_map = {i: '低' if center == min(order_centers) else '高' if center == max(order_centers) else '中' for i, center in enumerate(order_centers)}
df['购物频次'] = df['购物频次_cluster'].map(order_label_map)
在这里,order_centers是我们得到的三个中心的值,然后我们使用字典推导式为每个中心创建一个映射,最后使用map函数应用这个映射到数据框中的簇标签上。
相关文章:
数据分析实战 - 2 订单销售数据分析(pandas 进阶)
题目来源:和鲸社区的题目推荐: 刷题源链接(用于直接fork运行 https://www.heywhale.com/mw/project/6527b5560259478972ea87ed 刷题准备 请依次运行这部分的代码(下方4个代码块),完成刷题前的数据准备 …...
测试服务器端口是否开通,计算退休时间
本案例知识点 netstat -tuln | grep 80 nestat 目前主机打开的网络服务端口,-tuln目前主机启动的服务,如图 报错说参数太多,仔细检查发现if后的中括号内,变量少双引号导致,改完之后运行显示22,25端口开放࿰…...
Prometheus接入AlterManager配置企业微信告警(基于K8S环境部署)
文章目录 一、创建企业微信机器人二、配置AlterManager告警发送至企业微信三、Prometheus接入AlterManager配置四、部署PrometheusAlterManager(放到一个Pod中)五、测试告警 注意:请基于 PrometheusGrafana监控K8S集群(基于K8S环境部署)文章之上做本次实验。 一、创…...
11.1 Linux 设备树
一、什么是设备树? 设备树(Device Tree),描述设备树的文件叫做 DTS(DeviceTree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息: 树的主干就是系统总线, IIC 控制器、 GPIO 控制…...
万宾科技管网水位监测助力智慧城市的排水系统
以往如果要了解城市地下排水管网的水位变化,需要依靠人工巡检或者排查的方式,这不仅加大了人员的工作量,而且也为市政府带来了更多的工作难题。比如人员监管监测不到位或无法远程监控等情况,都会降低市政府对排水管网的管理能力&a…...
Glide transform CircleCrop()圆图,Kotlin
Glide transform CircleCrop()圆图,Kotlin import android.os.Bundle import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import com.bumptech.glide.load.resource.bitmap.CircleCropclass MainActivity : AppCompatActivity() {o…...
从NetSuite Payment Link杂谈财务自动化、数字化转型
最近在进行信息化的理论学习,让我有机会跳开软件功能,用更加宏大的视野,来审视我们在哪里,我们要到哪去。 在过去20多年,我们的财务软件经历了电算化、网络化、目前处于自动化、智能化阶段。从NetSuite这几年的功能发…...
1.UML面向对象类图和关系
文章目录 4种静态结构图类图类的表示类与类之间的关系依赖关系(Dependency)关联关系(Association)聚合(Aggregation)组合(Composition)实现(Realization)继承/泛化(Inheritance/Generalization)常用的UML工具reference欢迎访问个人网络日志🌹🌹知行空间🌹🌹 4种静态结构…...
JAVA小说小程序系统是怎样开发的
随着移动互联网的普及,小说阅读已经成为人们休闲娱乐的重要方式之一。为了满足广大读者的需求,我们开发了一款基于JAVA编程语言的小说小程序系统。本系统旨在提供一种便捷、高效、有趣的阅读体验,让用户能够随时随地阅读最新、最热门的小说。…...
【深度学习】pytorch——Tensor(张量)详解
笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~ pytorch——Tensor 简介创建Tensortorch.Tensor( )和torch.tensor( )的区别torch.Tensor( )torch.tensor( ) tensor可以是一个数(标量)、一维数组(向量)、二维数组&…...
装修服务预约小程序的内容如何
大小装修不断,市场中大小品牌也比较多,对需求客户来说,可以线下咨询也可以线上寻找品牌,总是可以找到满意的服务公司,而对装修公司来说如今线下流量匮乏,很多东西也难以通过线下方式承载,更需要…...
easypoi 导出Excel 使用总结
easypoi 导出Excel 导出Excel需要设置标题,且标题是多行,标题下面是列表头 设置表格标题 ExportParams headExportParams new ExportParams();StringBuilder buffer new StringBuilder("");buffer.append("1、课程名称:....…...
MySQL性能优化的最佳20条经验
概述 关于数据库的性能,这并不只是DBA才需要担心的事。当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能。下面讲下MySQL性能优化的一些点。 1. 为查询缓存优化你的查询 大多数的MySQL服务器…...
【Liunx基础】之指令(一)
【Liunx基础】之指令(一) 1.ls指令2.pwd命令3.cd指令4.touch指令5.mkdir指令(重要)6.rmdir指令与rm指令(重要)7.man指令(重要)8.cp指令(重要) 📃博客主页: 小…...
jQuery案例专题
jQuery案例专题 本学期主要担任的课程是js和jQuery,感觉用到的有一些案例挺有意思的,就对其进行了一下整理。 目录: 电影院的幕帘特效 手风琴特效 星光闪烁 网页轮播图 1.电影院的幕帘特效代码如下 html <!DOCTYPE html > <html…...
【Linux】服务器间免登陆访问
准备两台服务器,服务器A,服务器B 在服务器A中实现免登陆服务器B 进入服务器A操作 进入目录/root/.ssh cd /root/.ssh秘钥对使用默认文件名 生成秘钥对,在输入秘钥文件时直接回车则会使用默认文件名:id_rsa ssh-keygen -t rsa…...
【信息安全原理】——IP及路由安全(学习笔记)
目录 🕒 1. IPv4协议及其安全性分析🕒 2. IPsec(IP Security)🕘 2.1 IPsec安全策略🕤 2.1.1 安全关联(Security Association, SA)🕤 2.1.2 安全策略(Security…...
【jvm】虚拟机之本地方法栈
目录 一、说明二、注意 一、说明 1. Java虚拟机栈用于管理Javaj法的调用,而本地方法栈用于管理本地方法的调用。 2. 本地方法栈,也是线程私有的。 3. 允许被实现成固定或者是可动态扩展的内存大小。 (在内存溢出方面是相同) 4. 如果线程请求分…...
『CV学习笔记』图像超分辨率等图像处理任务中的评价指标PSNR(峰值信噪比)
图像超分辨率等图像处理任务中的评价指标PSNR(峰值信噪比) 文章目录 一. PSNR(峰值信噪比)1.1. 定义1.2. 作用1.3. 例子1.4 . PSNR评价标准二. 参考文献一. PSNR(峰值信噪比) 1.1. 定义 峰值信噪比(Peak Signal-to-Noise Ratio, PSNR)是图像超分辨率等图像处理任务中常用的一…...
【51nod 连续区间】 题解(序列分治)
题目描述 区间内的元素元素排序后 任意相邻两个元素值差为 1 1 1 的区间称为“连续区间”。 如 3 , 1 , 2 3,1,2 3,1,2 是连续区间, 3 , 1 , 4 3,1,4 3,1,4 不是连续区间。 给出一个 1 ∼ n 1 \sim n 1∼n 的排列,问有多少连续区间。 …...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
