用户首次付费分析

本人从事互联网产品数据分析工作,本项目解决某证券用户交流平台的自激活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="\t",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="\t",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")

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

推荐阅读更多精彩内容