一、Pandas 的分组聚合
分组聚合原理:
1、使用 groupby 方法进行分组聚合
分组: 该方法提供的是分组聚合步骤中的拆分功能,能根据索引或字段对数据进行分组。其常 用参数与使用格式如下
DataFrame.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
groupby 参数说明
| 参数 | 说明 |
|---|---|
| by | 接收 list,string,mapping 或者 generator,用于确定进行分组的依据,无默认 |
| axis | 接收 int,表示操作的轴向,默认对行进行操作,默认为 0 |
| level | 接收 int 或者索引名,代表标签所在级别,默认为 None |
| as_index | 接收 boolearn,表示聚合后的聚合标签是否以 DataFrame 索引形式输出,默认 为 True |
| sort | 接收 boolearn,表示是否依据分组标签进行排序。默认为 True。 |
| group_keys | 接收 boolearn,表示是否显示分组标签的名称。默认为 True |
| squeeze | 接收 boolearn,表示是否在允许的情况下对返回数据进行降维,默认为 False |
groupby 方法的参数及其说明——by 参数的特别说明:
如果传入的是一个函数则对索引进行计算并分组
如果传入的是一个字典或者 Series,则字典或者 Series 的值用来做分组依据
如果传入一个 NumPy 数组,则数据的元素作为分组依据
如果传入的是字符串或者字符串列表,则使用这些字符串所代表的字段作为分组依据
聚合: 用 groupby 方法分组后的结果并不能直接查看,而是被存在内存中,输出的是内存地址, 实际上分组后的数据对象 groupby 类似 Series 与 DataFrame,是 pandas 提供的一种对象
groupby 对象常用的描述统计
| 函数 | 说明 | 函数 | 说明 |
|---|---|---|---|
| count | 计算分组的数目,包括缺失值 | cumcount | 对每个分组中组员进行 标记,0 至 n-1 |
| head | 返回每组的前 n 个值 | size | 返回每组的大小 |
| max | 返回每组最大值 | min | 返回每组最小值 |
| mean | 返回每组的均值 | std | 返回每组的标准差 |
| median | 返回每组的中位数 | sum | 返回每组的和 |
2、使用 agg 函数进行聚合数据
agg 参数理解
agg 方法支持对每个分组应用某函数,包括 Python 内置函数或自定义函数,同时这两 个方法能够也能够直接对 DataFrame 进行函数应用操作
DataFrame.agg(func, axis=0, *args, **kwargs)
agg 参数说明
| 参数 | 说明 |
|---|---|
| func | 接收 list、dict、function,表示应用于每行/每列的函数 |
| axis | 接收 0 或者 1,代表操作的轴向,默认为 0,表示行的方向 |
2、agg 方法求统计指标
创建学生信息表,使用 agg 方法进行统计指标
学生信息表
| cls_id | group_id | name | hight | score | |
|---|---|---|---|---|---|
| Stu_0 | A | 1 | zs | 170 | 99.0 |
| Stu_1 | A | 1 | ls | 172 | 98.0 |
| Stu_2 | A | 1 | ww | 185 | 97.5 |
| Stu_3 | A | 2 | zl | 190 | 96.0 |
| Stu_4 | A | 2 | xx | 173 | 89.0 |
| Stu_5 | A | 2 | yy | 156 | 85.0 |
| Stu_6 | B | 1 | zz | 162 | 99.0 |
| Stu_7 | B | 1 | oo | 165 | 78.0 |
| Stu_8 | B | 1 | pp | 189 | 89.0 |
| Stu_9 | B | 2 | ii | 178 | 88.0 |
| Stu_10 | B | 2 | tt | 175 | 92.0 |
| Stu_11 | B | 2 | 176 | 94.0 |
学生信息表数据字段说明
| 字段名称 | 说明 |
|---|---|
| cls_id | 班级 ID |
| group_id | 小组 ID |
| name | 姓名 |
| hight | 身高 |
| score | 考试成绩 |
代码实现:
import pandas as pd
import numpy as np
# 分组聚合: 先分组,再聚合 --->1、指定如何分组? 2、统计指标?
# 创建df
df = pd.DataFrame(
data={
'cls_id': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B'],
'group_id': [1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2],
'name': ['aa', 'bb', 'cc', 'dd', 'ee', 'ff', 'gg', 'hh', 'ii', 'jj', 'kk', 'oo'],
'hight': [170, 172.5, 168, 192, 181.5, 156, 154, 162, 180, 179.5, 173, 176.5],
'score': [99, 92, 85, 71, 59, 76.5, 89, 95, 93, 79, 70, 84]
},
index=['stu0', 'stu1', 'stu2', 'stu3', 'stu4', 'stu5', 'stu6', 'stu7', 'stu8', 'stu9', 'stu10', 'stu11']
)
print('df:\n', df)
print('df:\n', type(df))
print('*' * 100)
# 指定分组、指定统计指标
# 可以使用groupby 来指定分组 --by参数 --指定分组的列
# 按照单列分组,统计单列的指标
# 按照班级id进行分组,统计各个班级的平均成绩
res = df.groupby(by='cls_id')['score'].mean()
print('res:\n', res)
# 按照多列进行分组,统计单列指标
# 先按照班级id,再按照小组Id, 进行分组, 统计各个组的平均成绩
res = df.groupby(by=['cls_id', 'group_id'])['score'].mean()
print('res:\n',res)
# 按照单列分组,统计多列的指标
# 按照班级Id进行分组,统计成绩、身高的平均值
res = df.groupby(by='cls_id')[['score','hight']].mean()
print('res:\n',res)
# 按照多列进行分组,统计多列的指标
# 先按照班级id,再按照小组id进行分组,统计各个组内的成绩、身高的最大值
res = df.groupby(by=['cls_id', 'group_id'])[['score', 'hight']].max()
print('res:\n',res)
1、可以使用 agg 方法一次求出 DataFrame 多列的多个统计指标
# agg --->不分组 ---对列求取指标
# 注意:agg = aggregate 同样的
# 1、对多列求取多个指标
# 对score 、hight 求取其均值、最大值、最小值
res = df.loc[:, ['score', 'hight']].agg([np.mean, np.max, np.min])
print('res:\n', res)
2、可以使用 agg 方法一次对 DataFrame 的不同的列,求取不同的统计指标
对于某个字段希望只做求均值操作,而对另一个字段则希望只做求和操作,可以使用字典的方式,将两个字段名分别作为 key,然后将 NumPy 库的求和与求均值的函数分别作为 value
# 2、对不同的列 求取不同的指标
# 对score列求最大值,对hight列求均值
res = df.agg({'score': [np.max, ], 'hight': [np.mean, ]})
print('res:\n', res)
3、可以使用 agg 方法一次对 DataFrame 的不同的列,求取不同个数的统计指标。 在某些时候还希望求出某个字段的多个统计量,某些字段则只需要求一个统计量,此时 只需要将字典对应 key 的 value 变为列表,列表元素为多个目标的统计量即可
# 3、对不同的列 求取不同个数的指标
# 对 score列求取最大值、均值 ,对hight列求取最小值
res = df.agg({'score': [np.max, np.mean], 'hight':[np.min,]})
print('res:\n',res)
agg 方法与自定义的函数:
在 agg 方法可传入读者自定义的函数
使用自定义函数需要注意的是NumPy库中的函数np.mean,np.median,np.prod,np.sum, np.std,np.var 能够在 agg 中直接使用,但是在自定义函数中使用 NumPy 库中的这些函数, 如果计算的时候是单个序列则会无法得出想要的结果,如果是多列数据同时计算则不会出现这种问题
使用 agg 方法能够实现对每一个字段每一组使用相同的函数,如果需要对不同的字段应用不同的函数,则可以和 Dataframe 中使用 agg 方法相同
4、使用 transform 方法聚合数据
transform 方法能够对整个 DataFrame 的所有元素进行操作。且 transform 方法只有一个 参数“func”,表示对 DataFrame 操作的函数
同时 transform 方法还能够对 DataFrame 分组后的对象 GroupBy 进行操作,可以实现组 内离差标准化等操作
若在计算离差标准化的时候结果中有 NaN,这是由于根据离差标准化公式,最大值和最 小值相同的情况下分母是 0。而分母为 0 的数在 Python 中表示为 NaN
代码实现:
# 4、agg可以使用自定义函数
res = df.agg({'score': [lambda x: max(x), ]})
print('res:\n',res)
# 如果想使用自定义函数
# apply
# transform
res = df.loc[:, ['score', 'hight']].apply(lambda x: x + 1)
print('res:\n',res)
res = df.loc[:, ['score', 'hight']].transform(lambda x: x + 1)
print('res:\n', res)
# 运算 ---->整列运算
#
res = df.groupby(by='cls_id')['score'].apply(lambda x: max(x))
print('res:\n', res)
print('*' * 100)
res = df.groupby(by='cls_id')['score'].transform(lambda x: max(x))
print('res:\n', res)
二、案例:店铺营业额案例
以订单详情数据为例,统计该商家店铺每日的营业额数据?在 detail 订单详情表中,并 不存在营业额特征字段,但是我们可以通过营业额=菜品单价*菜品数量计算出每一个菜品的 营业额,然后,再进行按照每日进行分组,统计每日的营业额数据
代码实现:
import pandas as pd
# 以detail表为例,--->计算该店铺每日的营业额??
# place_order_time
# 数量 单价
# 分组聚合
# 加载数据
detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n', detail)
print('detail:\n', detail.columns)
# 1、获取日数据
# 先将place_order_time 转化为pandas默认支持的时间序列
detail['place_order_time'] = pd.to_datetime(detail['place_order_time'])
# h获取日期属性
detail['day'] = detail['place_order_time'].dt.date
# 2、构建营业额
# 单价 数量
detail['price'] = detail['amounts'] * detail['counts']
print('detail:\n', detail)
# 3、求取每日营业额
# 按照日 进行分组,统计 price 的sum
res = detail.groupby(by='day')['price'].sum()
print('res:\n', res)
# 11:30 --->11 --->[9,12) --->'9:00 - 12:00' --->分组聚合
三、Pandas 的透视表与交叉表
1、利用 pivot_table 函数可以实现透视表
pivot_table()函数的常用参数及其使用格式如下:
pands.pivot_table(data,values=None,index=None,columns=None,aggfunc='mean',f ill_value=None,margins=False,dropna=True,margins_name='All')
pivot_table 函数参数说明表
| 参数 | 说明 |
|---|---|
| data | 接收 DataFrame,表示创建表的数据,无默认 |
| values | 接收字符串。用于指定想要聚合的数据字段名,默认使用全部数据,默认为 None |
| index | 接收 string 或者 list,表示行分组键,默认为 None |
| columns | 接收 string 或者 list,表示列分组键,默认为 None |
| aggfunc | 接收 functions,表示聚合函数,默认为 mean |
| margins | 接收 boolearn,表示汇总功能的开关,设为 True 后结果集中会出现名为”ALL”的行和列,默认为 True |
| dropna | 接收 boolearn,表示是否删除掉全为 NaN 的列, 默认为 False |
pivot_table 函数主要的参数调节:
在不特殊指定聚合函数 aggfunc 时,会默认使用 numpy.mean 进行聚合运算,numpy.mean 会自动过滤掉非数值类型数据。可以通过指定 aggfunc 参数修改聚合函数
和 groupby 方法分组的时候相同,pivot_table 函数在创建透视表的时候分组键 index 可以有多个
通过设置 columns 参数可以指定列分组
当全部数据列数很多时,若只想要显示某列,可以通过指定 values 参数来实现
当某些数据不存在时,会自动填充 NaN,因此可以指定 fill_value 参数,表示当存在缺失值时,以指定数值进行填充
可以更改 margins 参数,查看汇总数据
创建一个学生信息表,使用该学生信息表进行建立透视表
代码实现:
import pandas as pd
import numpy as np
# 交叉表:比较两列数据的相对频次(主要) ---满足行、列两种情况下的数据个数
# 分组聚合功能(并不常用)
# 创建df
df = pd.DataFrame(
data={
'cls_id': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B'],
'group_id': [1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2],
'name': ['aa', 'bb', 'cc', 'dd', 'ee', 'ff', 'gg', 'hh', 'ii', 'jj', 'kk', 'oo'],
'hight': [170, 172.5, 168, 192, 181.5, 156, 154, 162, 180, 179.5, 173, 176.5],
'score': [99, 92, 85, 71, 59, 76.5, 89, 95, 93, 79, 70, 84],
'weight': np.nan, # (float类型)
},
index=['stu0', 'stu1', 'stu2', 'stu3', 'stu4', 'stu5', 'stu6', 'stu7', 'stu8', 'stu9', 'stu10', 'stu11']
)
print('df:\n', df)
print('df:\n', type(df))
print('*' * 100)
# 透视表:plus版本的分组聚合
# 分组聚合:
# 按照班级id进行分组,统计各个班级的平均成绩
res = df.groupby(by='cls_id')['score'].mean()
print('res:\n', res)
# 透视表----pd.pivot_table实现透视表
res = pd.pivot_table(data=df, # 用来创建透视表的数据
index='cls_id', # 指定行索引
values='score', # 关注的主体
aggfunc=np.mean, # 对于主体的统计指标
)
print('res:\n', res) # dataframe
# 先按照班级id,再按照小组id进行分组,统计各个组内的成绩、身高的最大值
res = df.groupby(by=['cls_id', 'group_id'])[['score', 'hight']].max()
print('res:\n', res)
res = pd.pivot_table(data=df, # 用来创建透视表的数据
index=['cls_id', 'group_id'], # 指定行索引
values=['score', 'hight'], # 关注的主体
aggfunc=np.max, # 对于主体的统计指标
)
print('res:\n', res) # dataframe
# 透视表,index 和 columns单独存在,values 和 aggfunc 单独存在
# 不仅可以指定行索引、也可以指定列索引
res = pd.pivot_table(data=df, # 用来创建透视表的数据
index='cls_id', # 指定行索引
columns='group_id', # 指定列索引
values=['score', 'hight', 'weight'], # 关注的主体
aggfunc=np.max, # 对于主体的统计指标
# margins=True, # 总结开关
# margins_name='All', # 总开关的名称
# dropna=True, # 意味着将整列数据为空的给删除掉
# fill_value=0, # 将控制替换为0
)
print('res:\n', res) # dataframe
2、利用 crosstab 函数可以制作交叉表
交叉表是一种特殊的透视表,主要用于计算分组频率。可以利用 pandas 提供的 crosstab 函数可以制作交叉表,crosstab 函数的常用参数和使用格式如下
由于交叉表是透视表的一种,其参数基本保持一致,不同之处在于 crosstab 函数中的 index,columns,values 填入的都是对应的从 Dataframe 中取出的某一列
pandas.crosstab(index,columns,values=None,rownames=None,colnames=None, aggfunc=None, margins=False, dropna=True, normalize=False)
crosstab 参数说明表
| 参数 | 说明 |
|---|---|
| index | 接收 string 或者 list,表示行索引键,无默认 |
| columns | 接收 string 或者 list,表示行索引键,无默认 |
| values | 接收 array,表示聚合数据,默认为 None |
| aggfunc | 接收 function,表示聚合函数,默认为 None |
| rownames | 表示行分组键名,无默认 |
| colnames | 表示列分组键名,无默认 |
| dropna | 接收 boolearn,表示是否删除掉全部为 NaN 的列,默认为 False |
| margins | 接收 boolearn,表示汇总功能的开关,设为 True 后结果集中会出现名为”ALL”的行和列 |
| normalize | 接收 boolearn,表示是否对值进行标准化,默认为 False |
以学生信息表为例,构建交叉表
代码实现:
import pandas as pd
import numpy as np
# 交叉表:比较两列数据的相对频次(主要) ---满足行、列两种情况下的数据个数
# 分组聚合功能(并不常用)
# 创建df
df = pd.DataFrame(
data={
'cls_id': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B'],
'group_id': [1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2],
'name': ['aa', 'bb', 'cc', 'dd', 'ee', 'ff', 'gg', 'hh', 'ii', 'jj', 'kk', 'oo'],
'hight': [170, 172.5, 168, 192, 181.5, 156, 154, 162, 180, 179.5, 173, 176.5],
'score': [99, 92, 85, 71, 59, 76.5, 89, 95, 93, 79, 70, 84],
'weight': np.nan, # (float类型)
},
index=['stu0', 'stu1', 'stu2', 'stu3', 'stu4', 'stu5', 'stu6', 'stu7', 'stu8', 'stu9', 'stu10', 'stu11']
)
print('df:\n', df)
print('df:\n', type(df))
print('*' * 100)
# 创建一个交叉表 ----pd.crosstab创建交叉表
#
# index : 指定行索引
# columns :指定列索引
res = pd.crosstab(index=df['cls_id'], columns=df['group_id'])
print('res:\n', res)
# 交叉表中必须指定index 和 columns
# 交叉表中的 values 必须和 agggfunc同时出现,或者同时不出现
res = pd.crosstab(index=df['cls_id'],
columns=df['group_id'],
values=df['score'], # 关注的主体
aggfunc=np.max, # 对于主体所进行的统计指标
)
print('res:\n', res)
四、案例:TGI 指数分析案例
什么是 TGI?TGI:即 Target Group Index(目标群体指数),可反映目标群体在特定 研究范围(如地理区域、人口统计领域、媒体受众、产品消费者)内的强势或弱势
TGI 指数= [目标群体中具有某一特征的群体所占比例/总体中具有相同特征的群体所占 比例]*标准数 100
例如,将某地区 15-24 岁的人作为目标群体,将去[电影网站 A]看电影作为相同特征; 若该地区 15-24 岁的人中,有 8.9%的人去过[电影网站 A]看电影,而在该地区总体人群中, 有 6.6%的人去过[电影网站 A]看电影,则[电影网站 A]在 15-24 岁人群中的 TGI 指数是 134.9 (8.9%/6.6%×100),其数额越大,就表明目标群体吻合度就越强势
TGI 指数表征不同特征用户关注问题的差异情况,其中 TGI 指数等于 100 表示平均水平, 高于 100,代表该类用户对某类问题的关注程度高于整体水平
以下为某品牌的交易订单数据,针对该数据进行 TGI 分析
某品牌交易订单数据表(部分)
| 品牌名称 | 买家昵称 | 付款日期 | 订单状态 | 实付金额 | 邮费 | 省份 | 城市 | 购买数量 |
|---|---|---|---|---|---|---|---|---|
| viva la vida | 做快淘饭 | 2019/4/18 0:03 | 交易成功 | 22.32 | 0 | 北京 | 北京市 | 1 |
| viva la vida | 作自有世祟 | 2019/2/17 0:03 | 交易成功 | 87.00 | 0 | 上海 | 上海市 | 1 |
| viva la vida | 作雪白室 | 2019/4/18 0:01 | 交易成功 | 97.66 | 0 | 福建省 | 福州市 | 2 |
| viva la vida | 作美女购物主 | 2019/1/11 23:35 | 交易成功 | 37.23 | 0 | 河南省 | 安阳市 | 3 |
| viva la vida | 作美女购物主 | 2019/2/18 14:16 | 交易成功 | 29.50 | 0 | 河南省 | 安阳市 | 2 |
| viva la vida | 作卢阳口才室 | 2019/6/16 4:15 | 交易成功 | 42.50 | 0 | 浙江省 | 衢州市 | 3 |
| viva la vida | 作饺子室 | 2019/6/1619:35 | 交易成功 | 87.00 | 0 | 广东省 | 广州市 | 1 |
| viva la vida | 作韩式潮流服饰室 | 2019/1/11 16:56 | 交易成功 | 4.50 | 0 | 内蒙古自治区 | 呼伦贝尔市 | 1 |
| viva la vida | 作阿牛室 | 2019/1/11 6:29 | 交易成功 | 11.68 | 0 | 北京省 | 北京市 | 1 |
| viva la vida | 佐 o 佐 | 2019/1/11 11:51 | 交易成功 | 59.50 | 0 | 广东省 | 广州市 | 1 |
| viva la vida | 佐 o 佐 | 2019/6/23 5:47 | 交易成功 | 11.86 | 0 | 广东省 | 广州市 | 1 |
| viva la vida | 左隐右 | 2019/1/11 4:57 | 交易成功 | 84.50 | 0 | 浙江省 | 杭州市 | 1 |
针对该品牌的交易数据,可以提出问题:
(1)单个用户支付金额
(2)基于用户支付金额,判断用户是属于低客单还是高客单
(3)用透视表的方法来统计每个省市低客单、高客单人数
(4)计算总人数,以及每个城市对应的高客单占比
(5)计算全国总体高客单人数占比
(6)计算每个城市高客单 TGI 指数
代码实现:
import pandas as pd
# TGI:可反映目标群体在特定 研究范围(如地理区域、人口统计领域、媒体受众、产品消费者)内的强势或弱势。
# TGI计算:[目标群体中具有某一特征的群体所占比例/总体中具有相同特征的群体所占 比例]*标准数 100
# TGI 指数表征不同特征用户关注问题的差异情况,
# 其中 TGI 指数等于 100 表示平均水平, 高于 100,代表该类用户对某类问题的关注程度高于整体水平。
#
# 加载数据
data = pd.read_excel('./TGI指数案例数据.xlsx', sheet_name=0)
# print('data:\n', data)
# 属于交易数据 --必须为交易成功 才能作为研究数据
# pd.value_counts --->可以查看对应列的值,以及该值出现的次数
res = pd.value_counts(data.loc[:, '订单状态'])
print('res:\n', res)
# res:
# 交易成功 27792
# 付款以后用户退款成功,交易自动关闭 1040
# Name: 订单状态, dtype: int64
# 剔除交易失败的数据
# (1)删除法 ---选择
# a、确定bool数组
mask = data.loc[:, '订单状态'] == '付款以后用户退款成功,交易自动关闭'
# b、确定交易失败的行名称
drop_labels = data.loc[mask, :].index
# c、删除交易失败数据
data.drop(labels=drop_labels, axis=0, inplace=True)
print('剔除交易失败的数据的结果为:\n', data)
print('列索引:\n', data.columns)
# (2)保留法
# 自己实现
# 需求:
# (1)单个用户支付金额
# 按照 买家昵称 进行分组,统计 实付金额 的sum
pay = data.groupby(by='买家昵称')['实付金额'].sum()
print('pay:\n', pay) # series
# 如果需求为:平均每个用户的支付金额? ---- 对 实付金额 求sum / 去重后的买家昵称的数量
# (2)基于用户支付金额,判断用户是属于低客单用户还是高客单用户?
# 设置一个阈值:支付金额如果大于阈值 --->高客单用户;如果小于阈值 ----->低客单用户
# 阈值设置:均值、中位数、众数、自定义
# 自定义阈值为:50
def is_gaokedan(x):
"""
根据用户的支付金额判断该用户是否为高客单用户
:param x: 数据
:return: 高客单 / 低客单
"""
if x >= 50:
return '高客单'
else:
return '低客单'
# 先将pay 转化为df
df = pd.DataFrame(data=pay.values.reshape((-1, 1)),
columns=['支付金额'],
index=pay.index)
print('df:\n', df)
df.loc[:, '客户级别'] = df.loc[:, '支付金额'].transform(is_gaokedan)
# 重设索引 ---将原来的索引作为数据
df = df.reset_index()
print('df:\n', df)
# 客户的城市在 data 中 客户是否是高客单客户在df中
# 拼接合并 ---先借用后面知识
df = pd.merge(left=df, right=data[['买家昵称', '省份']], on='买家昵称', how='inner')
print('df:\n', df)
# (3)用透视表的方法来统计每个省市低客单、高客单人数
res = pd.pivot_table(data=df,
index='省份',
columns='客户级别',
values='买家昵称',
aggfunc='count')
print('res:\n', res)
# (4)计算各个城市的总人数,以及每个城市对应的高客单占比
# 各个城市的总人数 = 高客单客户 + 低客单客户
res['总人数'] = res['高客单'] + res['低客单']
print('res:\n', res)
# 占比
gaokedan_city = res['高客单'] / res['总人数']
print('各个城市的高客单用户占比:\n', gaokedan_city)
# (5)计算全国总体高客单人数占比
# 计算全国总人数
all = df.shape[0]
# 全国高客单人数
all_gaokedan = res['高客单'].sum()
# 占比
gaokedan_China = all_gaokedan / all
print('全国高客单客户占比为:\n', gaokedan_China)
# (6)计算每个城市高客单 TGI 指数
# 高客单的TGI指数 = 高客单在各个城市的占比 / 全国高客单的占比 * 100
TGI = gaokedan_city / gaokedan_China * 100
print('各个城市高客单客户的TGI指数为:\n',TGI )
# 进行排序 --降序排序
# 默认为升序排序,ascending=False 即可变为降序排序
# 排序的时候,按照排序规则,整行数据一块移动
# 如果为df排序,必须用by来指定按照某列排序
# 如果为series,只有一列
print('排序之后的结果为:\n',TGI.sort_values(ascending=False))
# TGI指数越高,在该城市内 高客单用户占比就越高 ,
# 判断该城市的 总的客户数量 > 1000 再去研究,高客单用户的TGI指数
五、案例:连锁超市统计分析案例
近些年来,国内大型连锁超市如雨后春笋般迸发,对于各个超市来说,竞争压力不可谓不大,为了拓展、保留客户,各种促销手段应运而生。以下为国内某连锁超市的成交统计数 据,针对于该数据,挖掘其中价值,为该超市的促销手段提供技术支持
连锁超市数据(部分)
| 商品 ID | 类别 ID | 门店编号 | 单价 | 销量 | 成交时间 | 订单 ID |
|---|---|---|---|---|---|---|
| 30006206 | 915000003 | CDNL | 25.23 | 0.328 | 2017/1/3 9:56 | 20170103CDLG000210052759 |
| 30163281 | 914010000 | CDNL | 2 | 2 | 2017/1/3 9:56 | 20170103CDLG000210052759 |
| 30200518 | 922000000 | CDNL | 19.62 | 0.23 | 2017/1/3 9:56 | 20170103CDLG000210052759 |
| 29989105 | 922000000 | CDNL | 2.8 | 2.044 | 2017/1/3 9:56 | 20170103CDLG000210052759 |
| 30179558 | 915000100 | CDNL | 47.41 | 0.226 | 2017/1/3 9:56 | 20170103CDLG000210052759 |
针对于该超市数据,可以提出问题:
(1)哪些类别的商品比较畅销?
(2)哪些商品比较畅销?
(3)求不同门店的销售额占比
(4)哪段时间段是超市的客流高峰期?
代码实现:
import pandas as pd
# 加载数据
data = pd.read_csv('./order.csv', encoding='ansi')
print('data:\n', data)
print('data:\n', data.columns)
# 查看数据 ---销量数据中存在 <=0的数据 ---干掉
# 删除法 删除<=0销量的数据
# 保留法 保留>0的销量数据
# 确定bool数组
mask = data.loc[:, '销量'] > 0
# 筛选数据
data = data.loc[mask, :]
print('data:\n', data)
print('data:\n', data.columns)
# (1)哪些类别的商品比较畅销?
# 按照 类别id 进行分组 销量 sum 降序排序
# res = data.groupby(by='类别ID')['销量'].sum().sort_values(ascending=False).head(10)
# print('res:\n', res)
res = pd.pivot_table(data=data,
index='类别ID',
values='销量',
aggfunc='sum').sort_values(by='销量', ascending=False).head(10)
print('res:\n', res)
# (2)哪些商品比较畅销?
# 按照 商品id 进行分组 销量 sum 降序排序
# res = data.groupby(by='商品ID')['销量'].sum().sort_values(ascending=False).head(10)
# print('res:\n', res)
res = pd.pivot_table(data=data,
index='商品ID',
values='销量',
aggfunc='sum').sort_values(by='销量', ascending=False).head(10)
print('res:\n', res)
# sort_index() # 按照索引的值的大小排序,此时索引必须可以用来比较大小
# reindex() # 参数为某一列数据,将该列数据作为行索引
# (3)求不同门店的销售额占比
# 没有销售额列 --->存在单价、 存在销量
# 单个商品的销售额 = 该商品单价 * 该商品的销量
# a、确定各个商品的销售额
data.loc[:, '销售额'] = data.loc[:, '单价'] * data.loc[:, '销量']
print('data:\n', data)
print('data:\n', data.columns)
# b、不同门店的销售额 = 根据门店编号 分组,统计 该店的所有商品的销售额之和
se = data.groupby(by='门店编号')['销售额'].sum()
print('se:\n', se)
# c、计算占比
print('各个门店的占比为:\n', (se / se.sum()).apply(lambda x: format(x, '.2%')))
# (4)哪段时间段是超市的客流高峰期?
# 客流高峰 --->哪个时间段超市的人数最多 --->哪个时间段内出现的不同的订单数目最多
# 对data 按照 订单数据 去重
# drop_duplicates
# subset : 指的是df所按照指定列来进行去重
# keep="first":默认保留第一次出现的
# inplace : 如果为True,直接修改原df,如果为False,对原df无影响,会返回一个新的修改的df
data.drop_duplicates(subset='订单ID', inplace=True)
print('去重之后的结果为:\n', data.shape)
# 不同时间段内的
# 按照每小时都算作一个时间段
data.loc[:, '成交时间'] = pd.to_datetime(data.loc[:, '成交时间'])
# 获取其 hour 属性
data.loc[:, 'hour'] = data.loc[:, '成交时间'].dt.hour
print('data:\n', data)
# 按照 hour 进行分组 ---统计 订单ID count
res = data.groupby(by='hour')['订单ID'].count().sort_values(ascending=False)
print('res:\n', res)
# 8 9 10 点是客流高峰