Python 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,代表该类用户对某类问题的关注程度高于整体水平。
目标:哪些城市的人有高客单偏好
• 特征,高客单,即客户单次购买超过 50 元
• 目标群体,就是各个城市,这里我们可以分别计算出所有城市客户的高客单偏好
• 至于总体,就非常直白了,计算所涉及到的所有客户即为总体
解题的关键在于,计算出不同城市,高客单人数及所占的比例。
加载包并读取数据集
import pandas as pd
df = pd.read_excel(r'F:\Python\案例数据.xlsx')
df.head()
Out[1]:
品牌名称 买家昵称 付款日期 订单状态 实付金额 邮费 省份 城市 购买数量
0 viva la vida 做快淘饭 2019-04-18 00:03:00 交易成功 22.32 0 北京 北京市 1
1 viva la vida 作自有世祟 2019-02-17 00:03:51 交易成功 87.00 0 上海 上海市 1
2 viva la vida 作雪白室 2019-04-18 00:01:43 交易成功 97.66 0 福建省 福州市 2
3 viva la vida 作美女购物主 2019-01-11 23:35:01 交易成功 37.23 0 河南省 安阳市 3
4 viva la vida 作美女购物主 2019-02-18 14:16:03 交易成功 29.50 0 河南省 安阳市 2
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 28832 entries, 0 to 28831
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 品牌名称 28832 non-null object
1 买家昵称 28832 non-null object
2 付款日期 28832 non-null datetime64[ns]
3 订单状态 28832 non-null object
4 实付金额 28832 non-null float64
5 邮费 28832 non-null int64
6 省份 28832 non-null object
7 城市 28832 non-null object
8 购买数量 28832 non-null int64
dtypes: datetime64[ns](1), float64(1), int64(2), object(5)
memory usage: 2.0+ MB
单个用户打标
第一步,我们先判断每个用户是否属于高客单的人群,所以先按用户昵称进行分组,看每位用户的平均支付金额。这里用平均,是因为有的客户多次购买, 而每次下单金额也不一样,故平均之。
gp_user = df.groupby('买家昵称')['实付金额'].mean().reset_index()
gp_user.head()
Out[3]:
买家昵称 实付金额
0 .blue_ram 49.450
1 .christiny 22.000
2 .willn1 34.570
3 .托托m 37.475
4 0000妮 13.500
定义一个判断函数,如果单个用户平均支付金额大于 50,就打上“高客单”的类别,否则为低客单,再用 apply 函数调用:
def if_high(x):
if x > 50:
return '高客单'
else:
return '低客单'
gp_user['客单类别'] = gp_user['实付金额'].apply(if_high)
gp_user.head(10)
Out[4]:
买家昵称 实付金额 客单类别
0 .blue_ram 49.450 低客单
1 .christiny 22.000 低客单
2 .willn1 34.570 低客单
3 .托托m 37.475 低客单
4 0000妮 13.500 低客单
5 0009797王 94.500 高客单
6 000xyx0 99.250 高客单
7 000米粒儿米粒0 24.500 低客单
8 00556旭79618 23.860 低客单
9 00不哭0 53.545 高客单
匹配城市
单个用户的金额和客单标签已经搞定,下一步就是补充每个用户的地域字段,一句pd.merge 函数就能搞定。由于源数据是未去重的,我们得先按昵称去重,不然匹配的结果会有许多重复的数据:
df_dup = df.loc[df.duplicated('买家昵称') == False, :]
df_merge = pd.merge(gp_user, df_dup, left_on='买家昵称', right_on='买家昵称', how='left')
df_merge.head()
Out[6]:
买家昵称 实付金额_x 客单类别 品牌名称 ... 邮费 省份 城市 购买数量
0 .blue_ram 49.450 低客单 viva la vida ... 0 上海 上海市 1
1 .christiny 22.000 低客单 viva la vida ... 0 江苏省 南京市 1
2 .willn1 34.570 低客单 viva la vida ... 0 山东省 烟台市 2
3 .托托m 37.475 低客单 viva la vida ... 0 上海 上海市 3
4 0000妮 13.500 低客单 viva la vida ... 0 广东省 揭阳市 1
[5 rows x 11 columns]
高客单 TGI 指数计算
要计算每个城市高客单 TGI 指数,需要得到每个城市高客单、低客单的人数分别是多少。
df_merge = df_merge[['买家昵称', '客单类别', '省份', '城市']]
result = pd.pivot_table(
df_merge,
index = ['省份', '城市'],
columns = '客单类别',
aggfunc = 'count',
)
result.head()
Out[8]:
买家昵称
客单类别 低客单 高客单
省份 城市
上海 上海市 2818.0 2374.0
云南省 临沧市 3.0 2.0
丽江市 1.0 3.0
保山市 6.0 2.0
大理白族自治州 9.0 8.0
这样得到的结果包含了层次化索引,要得到“高客单”列,需要先索引“买家昵称”,再索引“高客单”:
result['买家昵称']['高客单'].reset_index().head()
Out[9]:
省份 城市 高客单
0 上海 上海市 2374.0
1 云南省 临沧市 2.0
2 云南省 丽江市 3.0
3 云南省 保山市 2.0
4 云南省 大理白族自治州 8.0
拿到了每个省市的高客单人数,然后再拿到低客单的人数,进行横向合并:
tgi = pd.merge(
result['买家昵称']['高客单'].reset_index(),
result['买家昵称']['低客单'].reset_index(),
left_on = ['省份', '城市'],
right_on = ['省份', '城市'],
how = 'inner'
)
tgi.head()
Out[10]:
省份 城市 高客单 低客单
0 上海 上海市 2374.0 2818.0
1 云南省 临沧市 2.0 3.0
2 云南省 丽江市 3.0 1.0
3 云南省 保山市 2.0 6.0
4 云南省 大理白族自治州 8.0 9.0
目标群体中具有某一特征的群体所占比例:
我们再看看每个城市总人数以及高客单人数占比,来完成“目标群体中具有某一特征的群体所占比例”这个分子的计算:
tgi['总人数'] = tgi['高客单'] + tgi['低客单']
tgi['高客单占比'] = tgi['高客单'] / tgi['总人数']
tgi.head()
有些非常小众的城市,高客单或者低客单人数等于 1 甚至没有,而这些值尤其是空值会影响结果的计算,我们要提前检核数据:
tgi.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 346 entries, 0 to 345
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 省份 346 non-null object
1 城市 346 non-null object
2 高客单 332 non-null float64
3 低客单 329 non-null float64
4 总人数 315 non-null float64
5 高客单占比 315 non-null float64
dtypes: float64(4), object(2)
memory usage: 18.9+ KB
果然,高客单和低客单都有空值(可以理解为 0),从而导致总人数也存在空值,而 TGI 指数对于空值来说意义不大,所以我们剔除掉存在空值的行:
tgi = tgi.dropna()
总体中具有相同特征的群体所占比例:
接着统计总人数中,高客单人群的比例,来对标公式中的分母“总体中具有相同特征的群体所占比例”:
total_percentage = tgi['高客单'].sum() / tgi['总人数'].sum()
total_percentage
Out[14]: 0.41528303343887557
TGI 指数:
最后一步,就是 TGI 指数的计算,并排序:
tgi['高客单TGI指数'] = tgi['高客单占比'] / total_percentage * 100
tgi = tgi.sort_values('高客单TGI指数', ascending=False)
tgi.head()
发现了一个严重的问题: 高客单 TGI 指数排名靠前的城市,总客户数几乎不超过 10 人,这样的高客单人口占比,完全没有说服力。TGI 指数能够显示偏好的强弱,但很容易让人忽略具体的样本量大小, 这个是需要格外注意的。
对总人数进行筛选,用总人数的平均值作为阈值,只保留总人数大于平均值的城市:
tgi.loc[tgi['总人数'] > tgi['总人数'].mean(), :].head()