用户首次付费分析

挖数网精选
挖数网精选
挖数网精选
352
文章
0
评论
2020-04-1809:05:00 评论 110 7612字
摘要

本人从事互联网产品数据分析工作,本项目解决某证券用户交流平台的自激活APP到首次付费的核心路径问题。付费转化是任何一家互联网公司都必须面对的核心问题。

不同的产品,付费模式不同,受到的影响因素不同。证券类收费产品,会受到外界因素如政策、大盘等影响,同时又会受到服务提供者(投顾)水平的影响,其中投顾水平是核心因素,主要表现为对投顾的判断是否精准。

而首次付费由于用户其实未真正体验投顾老师的水平(未买股票),所以该因素由投顾实际指导效果转化为用户对老师的印象,该印象来源于用户在站内(包括APP内)的使用体验,即老师的免费服务,如回答问题,直播互动,分析文章等。免费服务是投顾+站方共同为用户提供的,因此提升首次付费用户使用体验,进而提升付费转化,这是站方可以把控的。至于复购问题,如上述由于产品本身特性,受投顾的判断能力影响很大,而站方又无法做到短时期大幅度提升该能力,同时评判某领域专家对事物的判断是否精准,本身就是一个复杂的题目,基于这些原因,复购问题的复杂程度远高于首次付费,故本次分析不予涉及。笔者认为,复购分析最好是应用于刚性需求,且影响因素可控的付费分析中,比如电商洗发水分析,需求(洗头)为刚需,影响因素(产品调性、价格等)可由电商平台自行选择供应商解决,因此复购问题就能够较好地进行分析。

以上为进行此次分析的缘由。下面逐条介绍本次分析项目

分析逻辑:从APP用户从激活到支付的所有点击行为中,找到结果为"支付"的行为路径,从中筛选出发生数量最大的路径,并优化该路径,进而促进支付。(注:本方法与用户行为漏斗正好是反向的,应该用在漏斗行为之前,请思考为什么)

样本要求: 非羊毛党用户,即APP使用行为出于对APP本身的兴趣。某些渠道或活动引人的用户,比如下APP送话费的引流和ASO带来的用户就应该剔除。否则,后果如下:1.引人误导行为路径;2.无效样本过多,导致计算量过度膨胀,效率下降。

分析环境:R语言

分析代码:代码分为两部分:《关键点击建模分析》和《注册支付时间差分布和消费金额分布》,建议分脚本运行

《关键点击建模分析》

#####################导入模型包#############

library(arules)

library(arulesSequences)

library(dplyr)

library(sqldf)

library(tcltk)

library(DBI)

#####################读入数据(9.1-12.13点击)#############

root<-"E:\R\datamining\appkick"

file<-paste(root,"kick-tab.txt",sep="")

# 文件中有些行的产品名是包括"#"符号的,在R中,"#"是默认注释符号,导致读入时认为"#"后面的

# 信息是注释不认为是数据,所以会出现" line 20412 did not have 17 elements"这种认为某一行缺少

#元素的问题,所以要用"comment.char"这个参数确认没有注释

t.read<-read.table(file,header=TRUE,sep=" ",comment.char="",fileEncoding="UTF-8")

head(t.read)

###########先跟据id再根据event排序

#t.read$event

#class(t.read)

t.read<-t.read[order(t.read$"用户姓名",t.read$event),]

t.read_order<-data.frame(t.read)

#head(t.read_order)

write.csv(t.read_order,file="E:\R\datamining\appkick\appkicksequence_order.csv")

#####################处理item,形成矩阵 #############

#class(t.l)

t.l<-as.list(t.read$items)

# class(t.read$items)

t.df<-as(t.l,"itemMatrix")

#class(t.df)

#str(t.df[1:5])

t.tr <- as(t.df, "transactions")

#inspect(t.tr[1:5])

#class(t.tr)

#str(t.tr)

###########处理id和event 格式必须为数字,且必须以顺序排列的##############

id.new<-c(0)

event.new<-c(0)

length(t.read$"用户姓名")

id.new[1]<-1

event.new[1]<-1

for (i in 2:length(t.read$"用户姓名")){if (t.read$"用户姓名"[i]==t.read$"用户姓名"[i-1]) {id.new[i]<-id.new[i-1]

event.new[i]<-event.new[i-1]+1}

else {id.new[i]<-id.new[i-1]+1

event.new[i]<-1} }

#t.read$"用户姓名"[1:20]

#data.frame(t.read$"用户姓名",id.new,event.new)[1:20,]

#导入sequenceID和eventID

transactionInfo(t.tr)$sequenceID <-id.new

transactionInfo(t.tr)$eventID <- event.new

transactionInfo(t.tr)

#inspect(t.tr[1:130,])

#class(t.tr)

#str(t.tr)

data.t.tr<-as(t.tr,"data.frame")

#输出表格

write.csv(data.t.tr,file="E:\R\datamining\appkick\appkicksequence_transaction.csv")

################################

#有条件限制

t.cs <- cspade(t.tr, parameter = list(support = 0,

maxsize = 5,maxlen=2),

control=list(verbose = TRUE))

t.out<-as(t.cs,"data.frame")

write.csv(t.out, file="E:\R\datamining\appkick\appkicksequence_output.csv")

##############################正则表达式找到以"支付"为目标的序列###################################

t.cs<-sort(t.cs,by="support")

#t.cs<-as(t.cs,"data.frame")

#head(t.cs)

#t.cs$sequence

kick.pay<-".*未签约用户点击原价支付[^\}]*\}>"

t.cs.pay<-t.cs[grep(kick.pay,as(t.cs,"data.frame")$sequence)]

inspect(t.cs.pay)

t.cs.pay<-t.cs.pay[-1]

t.cs.pay.dataframe<-as(t.cs.pay,"data.frame")

################筛选重点页面######################

persent<-t.cs.pay.dataframe$support/sum(t.cs.pay.dataframe$support)

#累计计算支持占比,发现对占比影响最大的一部分点击按钮

sum.persent<-cumsum(persent)

t.cs.pay.dataframe<-cbind(t.cs.pay.dataframe,persent,sum.persent)

max.persent = 0.7#考核影响达到70%的

#整理表格,加入相关数据项

t.cs.pay.dataframe<-subset(t.cs.pay.dataframe,sum.persent<=max.persent)

#length(t.cs.pay.dataframe$sequence)

########找到引导到支付的重要前点击############

kick.ant<-0;#前项(Antecedent)

i<-1

for(i.seq in t.cs.pay.dataframe$sequence) {

head.seq1<-regexpr("<\{",i.seq)+2

trail.seq1<-regexpr("\}",i.seq)[1]-1

kick.ant[i]<-substr(i.seq,head.seq1,trail.seq1)

i<-i+1

}

#kick.ant

###############计算前项点击的人数################

i<-1;

#点击前项(Antecedent)的人,这里后项(Consequent)指"支付",注意,这里是点击某个按钮的人数不是次数

kick.antpeople<-0

for(i.kick in kick.ant) {

kick.antpeople[i]<-length(unique(t.read$id[which(t.read$items==i.kick)]))

i<-i+1 }

################计算置信度############

#产生序列<点击i,未签约用户点击原价支付>的实例有多少,及该序列支尺度* 序列人数

kick.peoplenum<-t.cs.pay.dataframe$support*t.cs@info$nsequences

#计算影响到支付点击的置信度(confidence)

con.kick.affectingpay<-kick.peoplenum/kick.antpeople

#最终结果:将cspade出来并且优化后的序列结果,前项点击名称和置信度,放在一起。

result.final<-cbind(t.cs.pay.dataframe,antecedent.kick=kick.ant,confidence=con.kick.affectingpay)

head(result.final)

#class(result.final)

#输出

write.csv(result.final,file="E:\R\datamining\appkick\appkicksequence_resultfinal.csv")

#绘图

#支持度

barplot(as.matrix(result.final$support,nrow=1),ylim=c(1,0),beside=TRUE,xlab = "点击名称", main = "引导用户点击支付重要点击分析")

#画线

lines(0.5+c(1:nrow(result.final)),result.final$confidence,type="b",col = "red" )

text(0.5+c(1:nrow(result.final)),result.final$confidence,labels = paste(round(result.final$confidence*100,2),"%",sep = ""))

#坐标轴标签

axis(1,at=0.5+c(1:nrow(result.final)),labels = result.final$antecedent.kick,tick=FALSE)

《注册支付时间差分布和消费金额分布》

#####《注册支付时间差分布和消费金额分布》#####

#####################导入模型包#############

library(arules)

library(arulesSequences)

library(dplyr)

library(sqldf)

library(tcltk)

library(DBI)

library(ggplot2)

#####################读入数据(9.1-12.13点击)#############

root<-"E:\R\datamining\appkick"

file<-paste(root,"kick-tab.txt",sep="")

# 文件中有些行的产品名是包括"#"符号的,在R中,"#"是默认注释符号,导致读入时认为"#"后面的

# 信息是注释不认为是数据,所以会出现" line 20412 did not have 17 elements"这种认为某一行缺少

#元素的问题,所以要用"comment.char"这个参数确认没有注释

t.read<-read.table(file,header=TRUE,sep=" ",comment.char="",fileEncoding="UTF-8")

kNames<-names(t.read)#保留字段名

p<-t.read #下面用sqldf必须转为data.frame格式 但是t.read已经是data.frame格式了

####################计算筛选出有点击用户###########

kKickpay<-sqldf("select 用户姓名 from p where items like "未签约用户点击原价支付"")

kKickpay<-unique(kKickpay)

kKickall<-sqldf("select * from p where 用户姓名 IN kKickpay")

names(kKickall)<-kNames

t.read<-kKickall

length(t.read$用户姓名)

####################支付与注册时间差分布##############

lct <- Sys.getlocale("LC_TIME"); Sys.setlocale("LC_TIME", "C")

###############################################################

# 一定要注意时间表示格式,比如"Y"是"Year with century"如1919,#

# "y"是"Year without century (00–99),如19" #

###############################################################

kPaytime2<-strptime(t.read$支付时间,"%Y/%m/%d %H:%M:%S",tz = "GMT")

#kStartime<-strptime(t.read$激活时间,"%Y/%m/%d %H:%M:%S",tz = "GMT") #激活时间,有空值,弃用

kRegistractiontime2<-strptime(t.read$注册时间,"%Y/%m/%d %H:%M:%S",tz = "GMT")#注册时间

kdifftime1<-difftime(kPaytime2,kRegistractiontime2,units="days") #注册时间差

kdifftime2<-data.frame(as.integer(kdifftime1))

colnames(kdifftime2)<-"difftime"

t.read2<-cbind(t.read,kdifftime2)#合并原表和时间差列

#去掉一列中重复的行,"duplicated"返回一个逻辑值,判断一个数是不是会与它前面的数重复,

#这里用index建立一个索引

index<-duplicated(t.read2$用户姓名)

#注意利用索引去掉重复值

t.read3<-t.read2[!index,]

t.read3[1:5,]#有点击用户去重数据

#接下来画图

summary(t.read3$difftime)

#length(unique(which(t.read3$difftime<90)))

diff.mean<-mean(t.read3$difftime)#平均值

diff.sd<-sd(t.read3$difftime)#标准差

diff.var<-var(t.read3$difftime)#方差

#异常值范围

difftime.description1<- diff.mean+3*diff.sd

difftime.description2<- diff.mean-3*diff.sd

c(difftime.description1,difftime.description2)

i<-1

difftime.description<-c(0)

for (i in 1:length(t.read3$difftime)) {

if (t.read3$difftime[i]>=difftime.description1)

{difftime.description[i]=difftime.description1}

else

{difftime.description[i]=t.read3$difftime[i]}

}

summary(difftime.description)

t.description<-data.frame(difftime.description)

#导出时间差分布到表格

write.csv(t.description,file="E:\R\datamining\appkick\difftimedescription.csv")

#par(mfrow=c(1,1))

hist (difftime.description, breaks = seq(0,364,7),freq =TRUE, include.lowest = TRUE, main = "注册支付时间差分布",

xlab = "注册支付时间间隔天数" ,ylab ="频数",

xlim =c(0,400),ylim=c(0,60))

####################付费产品价格分布###########

head(t.read3)#有点击用户去重数据

#接下来画图

summary(t.read3$支付金额)

pay.description<-t.read3$支付金额;

hist (pay.description, breaks = c(1,3000,100),freq =TRUE, include.lowest = TRUE, main = "注册支付时间差分布",

xlab = "支付金额" ,ylab ="频数",

xlim =c(0,3000),ylim=c(0,60))

write.csv(pay.description,file = "E:\R\datamining\appkick\PAYdescription.csv")

End.

作者:爱米不渝

来源:简书

  • 我的微信公众号
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: