消费金融案例分析

一、单变量分析——用户首逾率增高问题
二、用户群组分析——对相同生命周期阶段的用户进行垂直分析
三、用户行为路径漏斗转化分析

一、单变量分析——用户首逾率增高问题

单变量分析:
单变量分析的目的是,通过对数据的整理、加工、组织和展示,并计算反应数据的集中趋势和离散程度的指标,对变量分布的特征和规律进行刻画和描述。不同类型的变量需要使用不同的方法和指标。
单变量分析又称“单变量统计分析”,就是在一个时间点上对某一变量所进行的描述和统计,因而又可以分为单变量描述统计和单变量推论统计两种方式。

1.案例背景

日常监控发现某款消费贷产品首逾率有逐渐升高的趋势,需要把首逾率降下来以减少产品带来的损失。


2.分析目标及思路

● 分析目标:通过数据探查分析制定出可以有效降低首逾率的策略。
● 分析思路:分析的策略是在客户申请时用来判断客户是否会逾期的条件,所以需要还原这些有首逾表现的客户在申请时的数据,即提取出客户在申请时点各个维度的数据,然后利用这些数据去找出能够区分好坏客户的变量,从而制定策略。

3.数据加工

观察原数据,并对列名进行更改,方便理解。最终所得数据(部分)如下:


用户信息表

3.1 计算首逾率

# 计算总体首逾率 = 首逾客户数/总申请客户数
dt['是否逾期(1是,0否)'].sum()/dt.shape[0]  #因为逾期是1,未逾期是0,所以求出来的和就是逾期的人数。

计算得到首逾率约为0.3076,属于过高的情况。下面将采取单变量分析法,从不同维度去分析,寻找解决方法。

3.2 筛选出有效变量

3.2.1 征信查询次数

# 定义一个信用评级分组的函数
# 定义一个征信分组的函数
def judge_zhengxin(nums):        
    try:                             
        nums=int(nums)
        if 0<= nums <3:
            return '1:[0,3)'
        elif  3<= nums <6:
            return '2:[3,6)'
        elif  6<= nums <12:
            return '3:[6,12)'
        elif  12<= nums <21:
            return '4:[12,21)'
        elif 21<=nums:
            return '5:[21,无穷)'
    except Exception as e:
        return "6:缺失"
# 新建一个字段记录分组信息
dt['征信分组']=dt['近半年征信查询次数'].apply(judge_zhengxin)
# 分组统计各个分组的情况
# 区间用户占比、未逾期客户数要另外计算
dt_info=dt.groupby('征信分组').agg({'用户id':'count',
                                '是否逾期(1是,0否)':'sum'}).reset_index().rename(columns={'用户id':'区间客户数',
                                                                                    '是否逾期(1是,0否)':'区间逾期客户数'})
# 区间用户占比、未逾期客户数要另外计算
dt_info['区间用户占比']=dt_info['区间客户数']/dt_info['区间客户数'].sum()
dt_info['区间没有逾期客户数']=dt_info['区间客户数'] - dt_info['区间逾期客户数']
dt_info['区间首逾率']=dt_info['区间逾期客户数']/dt_info['区间客户数']
dt_info

所得结果如下:



不同征信分组客户数占比

不同征信分组首逾率

由上图可以看出:
● 约80.51%的用户征信查询次数在12次以上;
● 首逾率与征信查询次数呈正相关的趋势:随着征信查询次数的不断增加,首逾率也呈现升高的趋势,且征信查询次数超过21次时,首逾率此时达到最高,约为59.85%。

3.2.2 信用评级分组
将信用评级划分为5个分组:AA、A、BCD、ERC、缺失,并进行聚合统计。

# 定义一个信用评级分组的函数
def judge_pingji(level):
    if level == 'A':
        return 'A'
    elif level == 'AA':
        return 'AA'
    elif level in ('B','C','D'):
        return 'BCD'
    elif level in ('E','HR','NC'):
        return 'ERC'
    else:
        return '缺失'
# 添加一列‘信用评级分组’
dt['信用评级分组']=dt['信用评级'].apply(judge_pingji)
# 分组统计各个分组的情况
dt_info2=dt.groupby('信用评级分组').agg({
    '用户id':'count',
    '是否逾期(1是,0否)':'sum'}).reset_index().rename(columns={'用户id':'区间客户数',
                                                                 '是否逾期(1是,0否)':'区间逾期客户数'})
# 区间用户占比、未逾期客户数要另外计算
dt_info2['区间用户占比']=dt_info2['区间客户数']/dt_info2['区间客户数'].sum()
dt_info2['区间没有逾期客户数']=dt_info2['区间客户数']-dt_info2['区间逾期客户数']
dt_info2['区间首逾率']=dt_info2['区间逾期客户数']/dt_info2['区间客户数']

所得结果如下:



不同信用评级分组客户数占比

不同信用评级分组首逾率

● 除去缺失字段,客户占比最高的是评级为BCD的用户,其次为ERC,整体用户评级偏低;
● 首逾率与评级档次呈正相关,其中ERC评级用户首逾率最高达到52.74%左右,其次为BCD评级用户首逾率在36.27%。

3.2.2 计算提升度
进行变量分析之后,这时我们就要从中筛选中较为有效的变量了,这里涉及到一个衡量变量是否有效的指标,提升度。

提升度:通俗的来说就是衡量拒绝最坏那一部分的客户之后,对整体的风险控制的提升效果。 提升度越高,说明该变量可以更有效的区分好坏客户,能够更少的误拒好客户。

计算公式:提升度=最坏分箱的首逾客户占总首逾客户的比例 /该分箱的区间客户数占比。

例如:上表中征信总查询次数的最坏分箱提升度就是(1923/17365)/(3213/56456)=11%/5.69%=1.93 提升度这个指标一般来说都是用来一批变量分析里做相对比较,很多时候都是在有限的变量里尽可能选提升度更高的变量来做策略。

分别计算征信总查询次数和客户信用评级的提升度:
征信总查询次数提升度

# 计算征信总查询次数最坏分箱的首逾客户占总首逾客户的比例。
bad_rate=dt_info.iloc[dt_info['区间首逾率'].idxmax()]['区间逾期客户数']/dt_info['区间逾期客户数'].sum()
# 计算该分箱的区间客户数占比
num_rate=dt_info.iloc[dt_info['区间首逾率'].idxmax()]['区间客户数']/dt_info['区间客户数'].sum()
# 计算提升度
bad_rate/num_rate

最终结果为1.9458。

信用评级提升度

bad_rate2=dt_info2.iloc[dt_info2['区间首逾率'].idxmax()]['区间逾期客户数']/dt_info2['区间逾期客户数'].sum()
num_rate2=dt_info2.iloc[dt_info2['区间首逾率'].idxmax()]['区间客户数']/dt_info2['区间客户数'].sum()
bad_rate2/num_rate2

最终结果为1.7147。

3.3 解决方案

在上文中, 通过上一步的单变量分析,我们筛出了“征信查询次数”、“信用评级”这两个提升度最高的变量。所以选择将其最坏分箱的人全部拒绝,计算提出后的首逾率降幅为多少。(这个影响就是指假设我们将‘征信总查询次数>=21的3213位客户全部拒绝’之后,剩下的客户逾期率相比拒绝之前的逾期率降幅是多少)

# 对征信分组的数据
# 排除最坏分箱后的逾期客户数
new_yuqi_nums=dt_info['区间逾期客户数'].sum() - dt_info.iloc[dt_info['区间首逾率'].idxmax()]['区间逾期客户数']
# 排除后的新逾期率
new_yuqi_rate=new_yuqi_nums/dt_info['区间客户数'].sum()
new_yuqi_rate
# 原有的逾期率
old_yuqi_rate=dt_info['区间逾期客户数'].sum()/dt_info['区间客户数'].sum()
old_yuqi_rate
# 新的逾期率下降数值
old_yuqi_rate - new_yuqi_rate

# 对评级分组的数据
new_yuqi_nums2=dt_info2['区间逾期客户数'].sum() - dt_info2.iloc[dt_info2['区间首逾率'].idxmax()]['区间逾期客户数']
# 排除后的新逾期率
new_yuqi_rate2=new_yuqi_nums2/dt_info2['区间客户数'].sum()
# 原有的逾期率
old_yuqi_rate2=dt_info2['区间逾期客户数'].sum()/dt_info2['区间客户数'].sum()
# 新的逾期率下降数值
old_yuqi_rate2 - new_yuqi_rate2

最终结果分别为“征信查询次数”:3.41%和“信用评级”:7.53%。
● 通过计算可得,通过拒绝最坏分组的客户,得到的最终首逾率分别下降了3.41%和7.53%,证明该方法确实能起到降低首逾率的效果。

二、用户群组分析——对相同生命周期阶段的用户进行垂直分析

群组分析法就是按某个特征对数据进行分组,通过分组比较,得出结论的方法。
群组分析的作用:
1.对处于相同生命周期阶段的用户进行垂直分析,从而比较得出相似群体随时间的变化。
2.通过比较不同的同期群,可以从总体上看到,应用的表现是否越来越好了。从而验证产品改进是否取得了效果。

1.案例背景

当我们在做用户分析时,会遇到这样的一个问题,一个用户使用APP时,会留下一连串的使用数据,可能是一月份的购买了1次,二月份购买了2次,三四月没有购买,五月份又购买了。也就是对于用户来说他的数据是一个时间面数据,而且不同用户的时间面是不相同的,开始时间经历时间都不一样,而如果我们分析的时候不考虑到这个因素而直接进行分析,显然是不够合理的,因为新用户和老用户经历的产品运营情况是不一样的。

那我们应该如何处理呢,这个时候就有一种分析方法,可以帮助我们在时间轴上对齐用户,这就是群组分析。

2. 分析目标及思路

通过用户的订单消费情况,对比同一月份的新用户留存率的变化趋势,以及不同时间期的新用户在同周期时的留存率情况

3. 数据加工

对数据进行观察,发现分析留存率只需要用到四个字段:
OrederId:订单编号
OrderDate:订单日期
UserId:用户编号
TotalCharges:消费金额

3.1 分组处理

# 生成一个新字段,用户订单月份(只要月份)
df['orderperiod']=df.orderdate.apply(lambda x:x.strftime('%Y-%m')) 
# 设置userid为索引
df.set_index('userid',inplace=True)
# 这里的level=0表示第一层索引即userid(因为此时userid已经被设置为索引),并且每次分组之后都会形成很多个dataframe
# 按照每个用户的订单的最早时期,生成用户群组
df["cohortgroup"]=df.groupby(level=0)['orderdate'].min().apply(lambda x:x.strftime('%Y-%m'))
# 重设索引
df.reset_index(inplace=True)
# 根据用户群组和月份字段进行分组
grouped=df.groupby(['cohortgroup','orderperiod'])  #1月份首次消费用户,在后续的1、2、3、4、5月的相关消费情况,所以对两列数据都要进行分组。

# 求每个用户群下每一个月份的用户数量、订单数量、购买金额
cohorts=grouped.agg({'userid':pd.Series.nunique,'orderid':pd.Series.nunique,'totalcharges':np.sum})  #因为有多个dataframe,所以用nunique和np.sum

#列重命名
cohorts.rename(columns={'userid':'totalusers','orderid':'totalorders'},inplace=True)  #totalcharges无需重新命名
cohorts

orderperiod:用户消费月份
chortgroup:用户最早消费时间(出现的时间点)

3.2 计算留存率

# 把每个群组继续购买的日期字段进行改变。因为不同月份的客户,首次消费的月份也有不同(如2月份的客户,首次消费就是2月,而不是1月),所以重新编号。
def cohort_period(df):
    # 给首次购买日期进行编号,第二次购买为2,第三次购买为3
    df["cohortperiod"] = np.arange(len(df)) + 1    #len(df):有多少行。得到的结果命名为‘cohortperiod’。np.arange():列表函数,得到一个列表。
    return df

# 注意的是apply后面传入的是一个个dataframe
cohorts = cohorts.groupby(level=0).apply(cohort_period)   # level=0表示第一层索引,此时即'cohortgroup'。对该分组下的每个月进行编号。
# 得到每个群组的用户量,重新设置索引
cohorts.reset_index(inplace=True)
# 将"cohortgroup","cohortperiod"两列设为索引(level=0和level=1)。
cohorts.set_index(["cohortgroup","cohortperiod"],inplace=True)
# 得到每个群组的用户量,就是第一天的用户数据量,用作留存率的分母
cohort_group_size = cohorts["totalusers"].groupby(level=0).first() 
# 计算每个群组的留存
cohorts["totalusers"].unstack(0).head()   # unstack(0):对level=0的索引进行unstack操作。
# 计算留存
user_retention = cohorts["totalusers"].unstack(0).divide(cohort_group_size,axis=1).fillna(0)


3.3 总结和解决方案

● 整体用户留存率偏低,在5月就已经没有用户;
● 3、4月份的用户生命周期较短,而1、2月用户生命周期相对较长;
● 猜测可能1月、2月有活动,特别是1月份的,能够让用户的留存较高,对于此类情况的产生需要想办法增加用户留存,比如持续推出新品、进行短信营销、推出活动等。

三、用户行为路径漏斗转化分析

用户行为路径分析:用户行为路径分析是一种监测用户流向,从而统计产品使用深度的分析方法。它主要根据每位用户在App或网站中的点击行为日志,分析用户在App或网站中各个模块的流转规律与特点,挖掘用户的访问或点击模式,进而实现一些特定的业务用途,如App核心模块的到达率提升、特定用户群体的主流路径提取与浏览特征刻画,App产品设计的优化与改版等。

行为路径分析有如下作用:
1.可视化用户流向,对海量用户的行为习惯形成宏观了解。
2.定位影响转化的主次因素,使产品的优化与改进有的放矢。

1. 案例背景及目的

案例基于网络消费贷款形式,对消费贷借款进行复盘分析,增加用户借贷率。

2. 数据加工

2.1 数据观察及重命名

获取原始表格每日信息表dt_flow、用户信息表dt_check,如下所示


每日信息表dt_flow

用户信息表dt_check
# 对列名进行重命名
dt_flow.rename(columns={'date':'日期',
                        'regist_cnt':'注册数',
                        'regist_rate':'访客注册率',
                        'active_cnt':'激活数'
                        },inplace=True)

dt_check.rename(columns={'ID':'用户ID',
                        'date':'申请日期',
                        'new_cus':'是否新用户(1为是,0为否)',
                        'lending':'是否放贷(1为是,0为否)'
                        },inplace=True)

2.2 计算每日申请贷款人数、审批贷款人数、放贷率

# 选取子集,将新用户和老用户分开统计每天申请贷款人数和审批放贷人数,然后计算新用户放贷率。
# 将新老用户分组
dt_check_1=dt_check[dt_check['是否新用户(1为是,0为否)']==1]  # 新用户
dt_check_0=dt_check[dt_check['是否新用户(1为是,0为否)']==0]  # 老用户
#对新用户数据透视计算放贷率。
pt_1=pd.pivot_table(data=dt_check_1,index='申请日期',values='是否放贷(1为是,0为否)',aggfunc=[np.sum,'count'])
# 删除指定的索引/列级别的数据。
pt_1.columns=pt_1.columns.droplevel(0)  
# 改列名。由于此时列名一样,所以直接采用pt_1.columns来赋值,不用rename。
pt_1.columns=['新用户放贷数','新用户申请数']
# 增加一列为:新用户放贷率
pt_1['新用户放贷率']=pt_1['新用户放贷数']/pt_1['新用户申请数']
#重置索引
pt_1 = pt_1.reset_index()
pt_1 

得到新用户表:


新用户

同理,也可以得到老用户表:


老用户

总体上来看,老用户的平均放贷率比新用户要高出许多。

2.3 计算老用户复借率

老用户定义:前一天的放款的新用户第二天继续借款就是老用户。

对存量老用户我们暂时不考虑,就看前一天贷款的人第二天是否还继续贷款,贷款的就认为是老用户复借

取新用户放贷透视表的5月前29天数据+4月30日的人(分析5月1日-5月30日的复借率)构成老客户数量,我们看这些客户是否还继续贷款。

# 先得到当月前29天的数据,得到的数据为list
old = list(pt_1["新用户放贷数"])[0:29]
# 4月30日有24个人,添加到old数据中
old.insert(0,24)
# 设置数据 data(字典)
data={'申请日期':list(pt_1["申请日期"]),'老客户数':old} 
# 将data数据转换为DataFrame表格
dt_old=pd.DataFrame(data)
# 将老用户数据pt_0和dt_old拼接为一张表
pt_0_m=pd.merge(pt_0,dt_old,on=['申请日期'],how='left')
pt_0_m['老客户复借率']=pt_0_m['老用户申请数']/pt_0_m['老客户数']
pt_0_m.head()

得到复借率表和走势图:


复借率

走势图

● 从整体上看,5月复借率走势起伏不定,推测在复借率较高的时间段可能是因为营销活动的影响;
● 平均复借率约为34.72%。

2.4 计算各节点路径转化率并绘图

# pd.merge连接平台流量表,组成一张用户路径总表,计算各节点转化率。(所有表格连接起来
dt=pd.merge(dt_flow,pt_1,how='left',left_on='日期',right_on='申请日期')
dt_1=pd.merge(dt,pt_0_m,how='left',left_on='日期',right_on='申请日期')
dt_1=dt_1.drop(['申请日期_x','申请日期_y'],axis=1)

#计算转化漏斗。 计算汇总数据
# 删除‘日期’列。(因为属于日期型数据,计算时会报错)
dt_2=dt_1.drop('日期',axis=1)
#汇总求和  
dt_2.loc['Row_sum'] = dt_2.apply(lambda x: x.sum()) 
# 将 "Row_sum"这一行这些列的数据取出来
dt_3 = dt_2[dt_2.index == "Row_sum"][["PV","UV","注册数","激活数","新用户申请数","新用户放贷数"]]  
dt_3_s = pd.DataFrame(dt_3.stack()).reset_index().iloc[:,[1,2]]
dt_3_s.columns = ["指标","汇总"]

得到指标汇总图和漏斗图:


指标汇总图

漏斗图

由上图可知:
● 转换率在 UV → 注册的转换率很低,需要采取针对性营销措施解决;
● 最终放贷成功率很低,可以优化继续模型,在保证资金安全的情况下提高放贷率。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343