第12章 用户偏好分析

12.1 用户偏好分析和 TGI

用户偏好分析与 TGI 的关系

我们常说的用户偏好分析是一种分析方向,它基于用户属性和行为等数据,可以分析用户对于某些产品、服务或特征的喜好程度。通过用户偏好分析,品牌可以更好地了解目标客户群体的需求和行为特点,从而制定契合度更高的产品策略、宣传策略等。

TGI 是一种衡量和对比不同用户群体偏好程度的方法,具有逻辑清晰、计算便捷的特点,被广泛应用于各类用户偏好分析之中,而且效果都还不错。

那么,到底什么是 TGI 呢?

TGI 的定义

对于 TGI,百度百科上是这样解释的:TGI(Target Group Index,目标群体指数)用于反映目标群体在特定研究范围内强势或者弱势的程度。

这个解释有些晦涩难懂,简单翻译一下是:TGI 是一种反映偏好的指标。这样还是不够具体,我们可以结合 TGI 公式来理解。

TGI=目标群体中具有某一特征的群体所占比例 × 标准数 100 总体中具有相同特征的群体所占比例

接下来,我们结合上面的公式详细解读 TGI 的计算逻辑。

通过拆解指标来理解 TGI

TGI 公式中有 3 个核心要素需要进一步拆解:某一特征、总体和目标群体。

举个例子,假设我们要研究 A 公司的脱发 TGI,则对应的 3 个核心要素如下。

口某一特征:我们想要分析的某种行为或者状态,这里是脱发或者受脱发困扰的状态。

口总体:我们研究的所有对象,即 A 公司所有人。

口目标群体:总体中我们感兴趣的一个分组。假设我们关注的分组是数据部,那么目标群体就是数据部。

于是,TGI 公式中的分子“目标群体中具有某一特征的群体所占比例"可以理解为"数据部脱发人数占数据部的比例”。假设数据部有 15 人,其中有 9 人受脱发困扰,那么数据部脱发人数占比就是 9/15,即 60% 0

而分母"总体中具有相同特征的群体所占比例”,等同于“全公司受脱发困扰人数占公司总人数的比例”。假设公司共 500 人,有 120 人受脱发困扰,那么这个比例就是 24% 0

于是,数据部脱发 TGI 为 60%/24%×100=250 。其他部门脱发 TGI 的计算逻辑是一样的,为本部门脱发人数占比/公司脱发人数占比 ×100

TGI 具体数值是围绕 100 这个值来解读的:

TGI= 100,表示目标群体和总体在某特征或行为上的表现相同。

TGI > 100,表示目标群体在某特征或行为上的表现高于总体,具有较高的偏好程度,数值越大偏好越强。

TGI< 100,表示目标群体在某特征或行为上的表现低于总体,具有较低的偏好程度,数值越小偏好越弱。

刚才的例子中,数据部脱发 TGI 是 250,远远高于 100,看来在该公司做数据工作的人脱发风险较高。

下面,我们通过一个 TGI 的 Pandas 实例来巩固对概念的理解。

12.2 用 Pandas 实现 TGI 分析

项目背景

许久未见的老板又抛来一份订单明细,说道:“我们最近要推出一款价格比较高的产品,打算在一些城市先试销,你分析一下这份数据,看看哪些城市的用户有高客单价偏好,帮我筛选 5 个吧。”

你赶紧打开表格,看看数据的基本情况:

df = pd. read_excel ('用户偏好分析案例数据. xlsx')

print (df.head ()) print (df.info ())

数据预览如下:

品牌名称用户 ID 付款日期订单状态实付金额郭费省份城市购买数量 0 阿粥(小 z)uid 00524123 2023- 04- 18 交易成功 22.32 0 北京北京市 11 阿粥(小 z)uid 00524124 2023- 02- 17 交易成功 87.00 0 上海上海市 12 阿粥(小 z)uid 00524125 2023- 04- 18 交易成功 97.66 0 福建省福州市 23 阿粥(小 z)uid 00524126 2023- 01- 11 交易成功 37.23 0 河南省安阳市 34 阿粥(小 z)uid 00524126 2023- 02- 18 交易成功 29.50 0 河南省安阳市 2<class 'pandas.core.frame.DataFrame'>RangeIndex: 28832 entries, 0 to 28831 Data columns (total * columns): # Column Non- Null Count Dtype - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 品牌名称 28832 non- null object 1 用户 ID 28832 non- null object 2 付款日期 28832 non- null datetime 64[ns]3 订单状态 28832 non- null object 4 实付金额 28832 non- null float 645 邮费 28832 non- null int 646 省份 28832 non- null object 7 城市 28832 non- null object 8 购买数量 28832 non- null int 64 dtypes: datetime 64 ns, float 64 (1), int 64 (2), object (5) memory usage: 2.0+ MB

这份被告知不用再清洗的订单数据包括品牌名称、用户 ID、付款日期、订单状态和地域等字段,一共 28832 条数据,没有空值。

粗略看了几眼源数据,你想趁热打铁明确数据需求:“老板,高客单价的标准是什么?”

“就我们产品线和历史数据来看,单次购买超过 50 元的就算高客单价的用户了。”

确认了高客单价的标准之后,我们的目标非常明确:按照高客单价偏好对城市排序。这里的偏好可以用 TGI 来衡量,对应 TGI 计算的 3 个核心要素分别如下。

口特征:高客单价,即用户单次购买超过 50 元。口目标群体:各个城市,这里我们可以分别计算出所有城市用户的高客单价偏好。口总体:计算涉及的所有用户。解题的关键在于,计算出不同城市高客单价人数及其所占的比例。

用户打标

第一步,判断每个用户是否属于高客单价的人群。先按用户 ID 进行分组,看每个用户的平均每次支付金额。这里用平均,是因为有的用户会多次购买,而每次下单的金额可能不一样。

gp_user df. groupby('用户 ID')['实付金额']. mean(). reset_index()gp_user. columns [用户 ID','平均每次支付金额']gp_user.head ()

得到每个用户的平均每次支付金额如下:

用户 ID 平均每次支付金额 0 uid 00324123 22.3201 uid 00324124 87.0002 uid 00324125 97.6603 uid 00324126 33.3654 uid 00324127 42.500

第二步,定义一个判断函数,如果单个用户平均每次支付金额大于 50 元,就打上“高客单价"的类别,否则为低客单价,再用 apply()方法调用:

def if_high (x): if x>50 . return'高客单价 else: return‘低客单价’ gp_user['客单价类别 ΦΦ=Φ gp_user[平均每次支付金额']- apply (if_high) gp_user.head ()

到这里,基于高低客单价的用户初步打标已经完成,结果如下:

用户 ID平均每次支付金额客单价类别
0uid 0032412322.320低客单价
1uid 0032412487.000高客单价
2uid 0032412597.660高客单价
3uid 0032412633.365低客单价
4uid 0032412742.500低客单价

匹配城市

我们已经得到单个用户的平均每次支付金额和客单价标签,下一步就是补充每个用户的地域字段。用 pd. merge()方法只需一行代码就能轻松完成。由于源数据是未去重的,我们必须先按用户 ID 去重,否则匹配的结果会有许多重复的数据。

#先去重df_dup =df. loc[df. duplicated('用户 ID') == False,:]再合并 df_merge = pd. merge(gp_user, df_dup, left_on = 用户 ID', right_on = 用户 ID', how = 'left') #返回关键字段df_merge = df_merge[('用户 ID','平均每次支付金额',客单价类别','省份','城市']df_merge.head () 匹配之后的结果如下:

匹配之后的结果如下:

用户 ID平均每次支付金额客单价类别省份城市
0uid 0032412322.320低客单价北京北京市
1uid 0032412487.000高客单价上海上海市
2uid 0032412597.660高客单价福建省福州市
3uid 0032412633.365低客单价河南省安阳市
4uid 0032412742.500低客单价浙江省衢州市

高客单价 TGI 计算

要计算每个城市的高客单价 TGI,需要得到每个城市高客单价、低客单价的人数分别是多少。用 Excel 的数据透视表处理起来很简单,直接把省份和城市拖曳到行的位置,把客单价类别拖曳到列的位置,值随便选一个字段,只要是统计就行。

这一套操作用 Python 实现起来也非常容易,用 pivot_table()透视表方法只需一行代码就能实现:

#先筛选出需要的列df_merge = df_merge[!用户 ID',客单价类别',省份',城市']

再用透视表

result = pd. pivot_table ( df_merge, index = [省份',城市'], columns = 客单价类别',aggfunc = 'count') result.head ()

运行代码,得到如图 12- 1 所示的聚合结果。


图 12-1 省市聚合的结果

这样得到的结果包含层次化索引,我们只要知道,要索引得到“高客单价”列,需要先索引“用户 ID”,再索引“高客单价”:

result['用户 ID']['高客单价']. reset_index (). head ()

运行结果如下:

省份城市高客单价 0 上海上海市 2374.01 云南省临沧市 2.02 云南省丽江市 3.03 云南省保山市 2.04 云南省大理白族自治州 8.0

这样就获取了每个省市的高客单价人数。然后再获取低客单价的人数,进行横向合并:

tgi = pd.merge (result['用户 ID']['高客单价']). reset_index (), result['用户 ID']['低客单价']. reset_index (), left_on = ['省份','城市'], right_on = ['省份','城市'], how = 'inner') tgi.head ()

得到我们熟悉的数据结构:

省份城市高客单价低客单价
0上海上海市2374.02818.0
1云南省临沧市2.03.0
2云南省丽江市3.01.0
3云南省保山市2.06.0
4云南省大理白族自治州8.09.0

我们再看看每个城市总人数以及高客单价人数占比,来完成 TGI 公式中“目标群体中具有某一特征的群体所占比例”这个分子的计算:

tgi['总人数'] = tgi['高客单价'] + tgi['低客单价']tgi['高客单价占比'] = tgi['高客单价'] / tgi['总人数']

tgi.head ()

运行结果如下:

省份城市高客单价低客单价总人数高客单价占比
0上海上海市2374.02818.05192.00.457242
1云南省临沧市2.03.05.00.400000
2云南省丽江市3.01.04.00.750000
3云南省保山市2.06.08.00.250000
4云南省大理白族自治州8.09.017.00.470588

有些非常小众的城市,其高客单价或者低客单价人数为 1 甚至为 0,而这些值尤其是空值会影响计算结果,因此我们要提前检核数据:

tgi.info ()

<class 'pandas.core.frame.DataFrame'> Int 64 Index: 346 entries, 0 to 345 Data columns (total 6 columns):

省份 346 non- null object 城市 346 non- null object 高客单价 332 non- null float 64 低客单价 329 non- null float 64 总人数 315 non- null float 64 高客单价占比 315 non- null float 64 dtypes: float 64 (4), object (2) memory usage: 18.9+ KB

果然,高客单价和低客单价都有空值(可以理解为 0),导致总人数也存在空值,而 TGI 对于空值来说意义不大,所以我们剔除掉存在空值的行:

tgi = tgi.dropna ()

接着统计总人数中高客单价人群的比例,来对标 TGI 公式中的分母“总体中具有相同特征的群体所占比例”:

total_percentage = tgi['高客单价']. sum () / tgi['总人数']. sum () print (total_percentage)

结果是 0.4153。

最后一步就是计算 TGI,顺便排个序:

tgi['高客单价 TGI'] = tgi['高客单价占比'] / total_percentage * 100 tgi = tgi. sort_values ('高客单价 TGI', ascending = False)

运行结果如图 12- 2 所示

省份城市高客单价低客单价总人数高客单价占比高客单价 TGI
新疆维吾尔自治区哈密市4.01.03.00.800000192.639702
新疆维吾尔自治区巴音郭楞蒙古自治州10.03.013.00.769231185.230483
云南省丽江市3.01.04.00.750000180.599721
甘肃省白银市3.01.04.00.750000180.599721
吉林省辽源市2.01.03.00.666667160.533085
四川省广安市6.03.09.00.666667160.533085
广西壮族自治区河池市4.02.06.00.666667160.533085
内蒙古自治区锡林郭勒盟2.01.03.00.666667160.533085
黑龙江省鹤岗市2.01.03.00.666667160.533085
山西省临汾市9.05.014.00.642857154.799761

TGI 计算中隐藏的问题

得到了最终结果,你正打算第一时间报告老板。说时迟,那时快,在按下回车键(发送)之前你又扫了一眼数据,发现了一个严重的问题:高客单价 TGI 排名靠前的城市,总用户数几乎不超过 10 人,这样的高客单价占比完全没有说服力。

TGI 能够显示偏好的强弱,但很容易让人忽略具体的样本量大小,而小样本量往往意味着波动极大,TGI 很容易飙升到前面,这是需要格外注意的。

怎么办?为了加强数据整体的信度,可以先对总人数进行筛选,用总人数的平均值作为阀值,只保留总人数大于平均值的城市:

tgi. loc[tgi['总人数'] > tgi['总人数']. mean (),:]. head (10)

处理之后,这份数据结果看起来合理不少:

省份城市高客单价低客单价总人数高客单价占比高客单价 TGI
287福建省福州市145.0135.0280.00.517857124.699807
124广东省珠海市49.052.0101.00.485149116.823582
27北京北京市1203.01298.02501.00.481008115.826450
283福建省厦门市105.0118.0223.00.470852113.380991
111广东省佛山市118.0135.0253.00.466403112.309708
173江西省南昌市63.073.0136.00.463235111.546887
46四川省成都市287.0334.0621.00.462158111.287429
0上海上海市2374.02818.05192.00.457242110.103682
164江苏省无锡市135.0162.0297.00.454545109.454376
120广东省深圳市438.0528.0966.00.453416109.182440

“基于各城市高客单价 TGI,福州、珠海、北京、厦门和佛山是高客单价偏好排名前五的城市。咱们要试销的高客单价新产品,仅从客单价偏好角度,可以优先考虑这些城市。”你自信地发出了这段文字。

12.3 本章小结

TGI 是衡量和对比不同用户群体偏好程度的工具,我们理解了某一特征、目标群体和总体这 3 个概念,就掌握了它的本质逻辑。使用 Pandas 计算 TGI 的过程,就是探究“目标群体中具备某一特征的人”和“总体中具备某一特征的人”二者关系的过程。只有当 TGI>100 时,才能说明目标群体对该特征的偏好高于总体,且 TGI 越高,偏好越强。同时,在计算和应用 TGI 时,我们必须考虑样本量的影响,以避免在极小值样本量的情况下出现异常(极高或极低)的 TGI。