diff --git a/src/views/portraitModel/components/AARRRAnalysis.vue b/src/views/portraitModel/components/AARRRAnalysis.vue index 5f4e45d..c907cdb 100644 --- a/src/views/portraitModel/components/AARRRAnalysis.vue +++ b/src/views/portraitModel/components/AARRRAnalysis.vue @@ -84,10 +84,10 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html" -
- X - √ -
+
+ X + √ +

2、哪种渠道的获客质量最高:

@@ -97,10 +97,10 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html" -
- X - √ -
+
+ X + √ +
提交
@@ -134,9 +134,9 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html"
- X - √ -
+ X + √ +

2、需要重点分析哪个月的激活率:

@@ -144,9 +144,9 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html"
- X - √ -
+ X + √ +
提交
@@ -189,9 +189,9 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html"
- X - √ -
+ X + √ +

2、从哪个时间段开始留存下来的客户,是公司的忠诚用户:

@@ -201,9 +201,9 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html"
- X - √ -
+ X + √ +
提交
@@ -240,27 +240,30 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html"

低活跃用户:

- % + + %
- X - √ -
+ X + √ +

普通用户:

- % + + %
- X - √ -
+ X + √ +

会员用户:

- % + + %
- X - √ -
+ X + √ +
@@ -272,9 +275,9 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html"
- X - √ -
+ X + √ +
提交
@@ -319,9 +322,9 @@ https://help.fanruan.com/finebi5.1/doc-view-1256.html"

该买菜app的K值为:(计算结果保留两位小数)

- X - √ -
+ X + √ + @@ -349,7 +352,7 @@ const { proxy } = getCurrentInstance(); const baseUrl = import.meta.env.VITE_APP_BASE_API; const showAnswerFlag = ref(false); const knowledgeHtml = ref( - `

从事市场营销工作必须建立相关的理论知识体系,才能从更好地理解市场环境、消费者行为和竞争态势等复杂因素,制定出更有效的营销策略和指导具体的营销实践工作,取得更佳的营销效果。

营销理论模型使用特定的框架或图形来表述相关理论,其结构化、可视化的特点让我们能够更好地理解和掌握理论,为理解和分析复杂的问题、预测市场趋势或消费者行为等提供了高效的思考路径,更让同业交流时能够基于模型使用相同的语言和框架来描述和解决问题,从而提高协作效率和减少理解偏差,这些都有助于提升营销工作的效率和帮助营销人实现持续成长。

因此,掌握营销理论模型对于营销专业人员来说非常重要。

市场营销学科发展至今已经总结提炼了大量的理论模型,并在营销实践中被持续精进完善,像是STP、波特五力、SWOT、4Ps、AIDA、AISAS、AARRR等理论模型早已为资深从业者耳熟能详和熟练运用

但这些模型的具体含义是什么和如何使用,对于从业新人或过往忽视理论的朋友来说还是相当陌生,在公众号后台和知乎上有不少朋友在向我提问。

此前,我已经在文章中介绍过STP、品牌生命周期、商业模式画布、消费者旅程等相关理论模型,打算在之后的文章中继续分享一些其他常用的营销理论模型,也期待与感兴趣的朋友交流。

今天就让我们一起来了解下AARRR模型(海盗模型和增长黑客模型)。

一、什么是AARRR模型?

AARRR模型的实质是一个用户生命周期模型,关注用户获取(Acquisition)、激活(Activation)、留存(Retention)、收入(Revenue)和自传播(Refer)这五个方面,描述了用户从了解一个产品或服务到成为忠实用户的整个过程,对应于产品生命周期中的五个阶段。

AARRR模型是由硅谷著名风险投资人戴夫·麦克卢尔Dave McClure于2007年提出的,也被称为海盗模型或增长黑客理论模型。

AARRR模型之所以被称为海盗模型,是因为将其对用户生命周期的开发过程形象地比喻为像海盗一样的掠夺式增长;

而AARRR模型被冠以增长黑客模型的名称,则是因为其是增长黑客(互联网时代对以数据驱动营销,以市场指导产品,通过技术化手段贯彻增长目标的人)在进行用户增长时的重要工具。

具体来看,AARRR模型包含五个关键阶段:

1、获取用户(Acquisition):通过大数据分析将消费者更感兴趣的产品推荐给用户,从而获得用户。

2、提高活跃度(Activation):通过优化传播内容、拓宽传播渠道以及提供更有情绪价值的服务等方式,提升用户的活跃度。

3、提高留存(Retention):在兴趣电商时代,留存率指的是消费者在购买某个品牌或产品后,能够继续使用并保持忠诚度的比例。对于电商平台而言,如何帮助品牌提高留存率是衡量平台吸引力和用户满意度的重要指标之一。

4、获取收入(Revenue):通过使用户有兴趣并持续保持兴趣,最终实现销售转化。

5、自传播(Refer):在新营销时代,自传播更多地代表着用户多元化的分享行为,也是品牌的营销和产品被用户认可所产生的UGC内容、朋友圈推荐以及愿意将品牌行为/产品变成自己“炫耀”的资本。

AARRR模型可以抽象为一个五级漏斗模型,如下图:

image.png

要注意的是,市场营销还经常会使用AIDA模型(Attention、Interest、Desire、Action)和AISAS模型(Attention、Interest、Search、、Action、Share),我认为它们与AARRR模型都是描述用户行为和购买过程的模型,只不过是从不同视角出发的一体两面:AIDA模型和AISAS模型是站在用户视角强调其行动,而AARRR模型是站在产品视角强调通过合适手段去调动用户的行动。

营销理论没有绝对的对与错,在实践工作中我们可以根据实际情况切换不同视角,把这些模型结合起来灵活应用。

二、为何AARRR模型对于营销工作非常重要?

互联网时代,大量软件产品服务涌现,AARRR模型被增长黑客和运营人员应用来推动产品用户的增长和商业变现,取得了相当不错的成绩,也因此被市场营销人员所注意,应用到更广泛领域的市场营销实践工作中。

随着工业生产能力不断提升、信息技术快速发展,品牌竞争日益激烈、媒介传播环境充斥着无限的诱惑和巨大噪声,为了保持领先地位并实现持续增长,品牌需要紧紧地抓住客户、强化其忠诚度,并追求商业变现、用户忠诚、品牌建设之间的均衡发展,AARRR模型则为我们提供了有益的思考框架和清晰的可执行路径。

AARRR模型提供了一个全面的视角,描述了用户生命周期对应品牌增长的每一个关键环节,通过深入了解用户需求和市场趋势,品牌可以制定不同阶段的有针对性的策略,提高用户留存率、转化率和满意度,以实现在保持快速用户增长的同时,做好商业变现和品牌建设的平衡,更充分地挖掘用户全生命周期的价值。

数据分析在现代营销中扮演着至关重要的角色,AARRR模型强调了数据在每个增长阶段中的核心地位,通过数据分析企业可以深入了解用户行为、制定营销策略、跟踪和评估营销工作效果并进行持续的调整优化,这有利于并提高营销资源的使用效率和增加营销活动的成功率。

此外,AARRR模型站在产品发展的视角去调动用户,强调了用户体验在品牌增长中的关键作用。从初次接触用户开始,企业就需要提供令人印象深刻的第一印象并持续提供良好的用户体验,再以此去驱动研发设计、生产制造等部门不断升级产品服务和提升品质,驱动客服、营销、销售等人员强化互动沟通和优化服务体验,以确保用户满意从而挖掘其潜藏的商业价值。

三、如何运用AARRR模型来优化营销工作?

AARRR模型为我们提供了一个思考的框架和执行路径,但具体如何运用这一模型还需要根据实际情况而定,但一般来说在不同阶段可以从以下方面着手:

1、获取Acquisition——吸引尽可能多的潜在用户接触品牌。

但这并不是意味着盲目地在各种传播渠道进行大声量的传播和高频大体量的活动,而是要基于调研精准定位潜在用户后再集中资源渗透。

首先,品牌要进行市场研究和用户调查,了解目标用户是谁、他们在哪里以及他们的需求和兴趣是什么;

然后,品牌要根据收集到的信息制定用户获取策略,包括选择合适的渠道和推广方式,例如社交媒体广告、搜索引擎优化(SEO)、内容营销、合作伙伴关系等,来吸引潜在用户进入品牌或产品;

此外,品牌要定期评估获取策略的效果,并根据数据进行调整和优化,而效果评估可以关注获取成本(CAC)、用户来源和转化率等指标。

2、激活Activation——引导用户使用产品和全面了解品牌。

为此,品牌需要降低使用门槛、消除接触障碍,并提供优秀的用户体验。

品牌要以合适的方式唤醒客户的注意,并提供易于使用的界面、优秀的客户服务、明确的使用引导、有趣的互动体验等来满足用户需求和期望。

例如,品牌可以在营销活动中手机用户的手机号或引导其注册APP、关注公众号、添加工作人员微信,这可以通过提供免费的试用样品或者优惠券来实现,后续通过短信、公众号消息、微信沟通或电话来引导其深入使用,为了调动其积极性可以提供优惠券、运用会员机会体系或策划互动活动等。

3、保留Retention——使用户继续使用你的产品或服务,保持和提升其忠诚度,

为此,品牌需要定期更新产品、提供新的功能和服务,以满足用户的需求和期望,保持用户的兴趣和参与度,并建立优质的客户服务体系,及时回应用户的问题和反馈,通过专业、友好的客户服务提高用户忠诚度;

其次,品牌可以通过邮件、通知、活动等方式与用户保持长期的联系和互动,也可以尝试建立有效的激励制度,例如积分、等级、优惠券等,以激励用户更频繁地使用产品或服务;

此外,品牌也可以建立用户社区,鼓励用户之间的互动和交流和更好地了解用户需求和反馈,强化他们对品牌文化的认可、参与产品服务的开发和升级、增加用户粘性和品牌忠诚度。

4、收益Revenue——制定和优化定价策略,并提供便捷的支付方式,让用户愿意也容易为产品服务付费。

在定价策略上,企业需要综合考虑多种因素,以确保价格与产品或服务的价值相匹配并具有竞争力,例如价值定价(确保价格与用户感知的价值相匹配)、市场比较定价(根据竞争对手的价格来设定自己的价格,以提供更具竞争力的定价)、差别定价(根据不同的用户群体、产品版本或服务等级来设定不同的价格),从而满足不同用户的需求和期望,提升其付费的意愿。

同时,企业需要选择合适的付费模式以吸引和满足用户的需求。例如,存储和在线办公套件等频繁使用的服务可以采取按月或按年付费的订阅模式,既为用户提供灵活的付费选择的同时又保证企业的持续收益;软件和游戏等用户一次性付费即可永久使用的产品可以采用买断模式,通过提供永久的使用权增加付费购买意愿;也可以采用“免费+增值”模式是基础功能免费,高级功能收费的模式,例如如广告免费、无广告收费的区别服务模式,在保证大用户基础的同时促使用户付费享受更高品质的服务。

此外,品牌还要支持信用卡、支付宝、微信支付等多种支付方式,并提供清晰的支付流程说明,确保用户能够轻松完成支付;还可以开展如折扣、买一送一、捆绑销售等促销活动,吸引用户付费,充分挖掘留存用户所潜藏的商业价值。

5、推荐Refer——调懂用户推荐品牌或产品给其他人。

为了激励用户推荐品牌或产品给其他人,企业需要提供优秀的用户体验,让用户对产品或服务满意并产生推荐意愿,并建立有效的推荐机制,如推荐奖励、分享功能等来激励用户积极参与推荐活动。

此外,品牌要善于利用社交媒体和口碑营销来进一步扩大品牌的知名度和影响力,例如策划有奖互动活动来引导老用户参与和吸引更多潜在用户的关注和信任,并通过建立话题标签或品牌专区来沉淀相关的传播内容,以实现更长期的可持续的品牌影响。

四、结语

AARRR模型提供了一个全面且系统的营销框架,旨在指导品牌更好地理解用户并与其建立长期关系。本模型关注用户全生命周期,通过获取、激活、留存、收入和推荐来吸引更多潜在用户、提高用户满意度和忠诚度、增加收益、扩大影响力,并最终实现持续的增长和成功。

因此,理解和运用AARRR模型对于品牌的长期发展至关重要。这需要品牌不仅关注单一的销售或推广活动,而要从更高层次的视角和更长时间周期来看待与用户的关系,并以数据分析驱动精准高效的营销活动,在用户生命周期的不同阶段实现相应的营销目标。

` + `

从事市场营销工作必须建立相关的理论知识体系,才能从更好地理解市场环境、消费者行为和竞争态势等复杂因素,制定出更有效的营销策略和指导具体的营销实践工作,取得更佳的营销效果。

营销理论模型使用特定的框架或图形来表述相关理论,其结构化、可视化的特点让我们能够更好地理解和掌握理论,为理解和分析复杂的问题、预测市场趋势或消费者行为等提供了高效的思考路径,更让同业交流时能够基于模型使用相同的语言和框架来描述和解决问题,从而提高协作效率和减少理解偏差,这些都有助于提升营销工作的效率和帮助营销人实现持续成长。

因此,掌握营销理论模型对于营销专业人员来说非常重要。

市场营销学科发展至今已经总结提炼了大量的理论模型,并在营销实践中被持续精进完善,像是STP、波特五力、SWOT、4Ps、AIDA、AISAS、AARRR等理论模型早已为资深从业者耳熟能详和熟练运用

但这些模型的具体含义是什么和如何使用,对于从业新人或过往忽视理论的朋友来说还是相当陌生,在公众号后台和知乎上有不少朋友在向我提问。

此前,我已经在文章中介绍过STP、品牌生命周期、商业模式画布、消费者旅程等相关理论模型,打算在之后的文章中继续分享一些其他常用的营销理论模型,也期待与感兴趣的朋友交流。

今天就让我们一起来了解下AARRR模型(海盗模型和增长黑客模型)。

一、什么是AARRR模型?

AARRR模型的实质是一个用户生命周期模型,关注用户获取(Acquisition)、激活(Activation)、留存(Retention)、收入(Revenue)和自传播(Refer)这五个方面,描述了用户从了解一个产品或服务到成为忠实用户的整个过程,对应于产品生命周期中的五个阶段。

AARRR模型是由硅谷著名风险投资人戴夫·麦克卢尔Dave McClure于2007年提出的,也被称为海盗模型或增长黑客理论模型。

AARRR模型之所以被称为海盗模型,是因为将其对用户生命周期的开发过程形象地比喻为像海盗一样的掠夺式增长;

而AARRR模型被冠以增长黑客模型的名称,则是因为其是增长黑客(互联网时代对以数据驱动营销,以市场指导产品,通过技术化手段贯彻增长目标的人)在进行用户增长时的重要工具。

具体来看,AARRR模型包含五个关键阶段:

1、获取用户(Acquisition):通过大数据分析将消费者更感兴趣的产品推荐给用户,从而获得用户。

2、提高活跃度(Activation):通过优化传播内容、拓宽传播渠道以及提供更有情绪价值的服务等方式,提升用户的活跃度。

3、提高留存(Retention):在兴趣电商时代,留存率指的是消费者在购买某个品牌或产品后,能够继续使用并保持忠诚度的比例。对于电商平台而言,如何帮助品牌提高留存率是衡量平台吸引力和用户满意度的重要指标之一。

4、获取收入(Revenue):通过使用户有兴趣并持续保持兴趣,最终实现销售转化。

5、自传播(Refer):在新营销时代,自传播更多地代表着用户多元化的分享行为,也是品牌的营销和产品被用户认可所产生的UGC内容、朋友圈推荐以及愿意将品牌行为/产品变成自己“炫耀”的资本。

AARRR模型可以抽象为一个五级漏斗模型,如下图:

image.png

要注意的是,市场营销还经常会使用AIDA模型(Attention、Interest、Desire、Action)和AISAS模型(Attention、Interest、Search、、Action、Share),我认为它们与AARRR模型都是描述用户行为和购买过程的模型,只不过是从不同视角出发的一体两面:AIDA模型和AISAS模型是站在用户视角强调其行动,而AARRR模型是站在产品视角强调通过合适手段去调动用户的行动。

营销理论没有绝对的对与错,在实践工作中我们可以根据实际情况切换不同视角,把这些模型结合起来灵活应用。

二、为何AARRR模型对于营销工作非常重要?

互联网时代,大量软件产品服务涌现,AARRR模型被增长黑客和运营人员应用来推动产品用户的增长和商业变现,取得了相当不错的成绩,也因此被市场营销人员所注意,应用到更广泛领域的市场营销实践工作中。

随着工业生产能力不断提升、信息技术快速发展,品牌竞争日益激烈、媒介传播环境充斥着无限的诱惑和巨大噪声,为了保持领先地位并实现持续增长,品牌需要紧紧地抓住客户、强化其忠诚度,并追求商业变现、用户忠诚、品牌建设之间的均衡发展,AARRR模型则为我们提供了有益的思考框架和清晰的可执行路径。

AARRR模型提供了一个全面的视角,描述了用户生命周期对应品牌增长的每一个关键环节,通过深入了解用户需求和市场趋势,品牌可以制定不同阶段的有针对性的策略,提高用户留存率、转化率和满意度,以实现在保持快速用户增长的同时,做好商业变现和品牌建设的平衡,更充分地挖掘用户全生命周期的价值。

数据分析在现代营销中扮演着至关重要的角色,AARRR模型强调了数据在每个增长阶段中的核心地位,通过数据分析企业可以深入了解用户行为、制定营销策略、跟踪和评估营销工作效果并进行持续的调整优化,这有利于并提高营销资源的使用效率和增加营销活动的成功率。

此外,AARRR模型站在产品发展的视角去调动用户,强调了用户体验在品牌增长中的关键作用。从初次接触用户开始,企业就需要提供令人印象深刻的第一印象并持续提供良好的用户体验,再以此去驱动研发设计、生产制造等部门不断升级产品服务和提升品质,驱动客服、营销、销售等人员强化互动沟通和优化服务体验,以确保用户满意从而挖掘其潜藏的商业价值。

三、如何运用AARRR模型来优化营销工作?

AARRR模型为我们提供了一个思考的框架和执行路径,但具体如何运用这一模型还需要根据实际情况而定,但一般来说在不同阶段可以从以下方面着手:

1、获取Acquisition——吸引尽可能多的潜在用户接触品牌。

但这并不是意味着盲目地在各种传播渠道进行大声量的传播和高频大体量的活动,而是要基于调研精准定位潜在用户后再集中资源渗透。

首先,品牌要进行市场研究和用户调查,了解目标用户是谁、他们在哪里以及他们的需求和兴趣是什么;

然后,品牌要根据收集到的信息制定用户获取策略,包括选择合适的渠道和推广方式,例如社交媒体广告、搜索引擎优化(SEO)、内容营销、合作伙伴关系等,来吸引潜在用户进入品牌或产品;

此外,品牌要定期评估获取策略的效果,并根据数据进行调整和优化,而效果评估可以关注获取成本(CAC)、用户来源和转化率等指标。

2、激活Activation——引导用户使用产品和全面了解品牌。

为此,品牌需要降低使用门槛、消除接触障碍,并提供优秀的用户体验。

品牌要以合适的方式唤醒客户的注意,并提供易于使用的界面、优秀的客户服务、明确的使用引导、有趣的互动体验等来满足用户需求和期望。

例如,品牌可以在营销活动中手机用户的手机号或引导其注册APP、关注公众号、添加工作人员微信,这可以通过提供免费的试用样品或者优惠券来实现,后续通过短信、公众号消息、微信沟通或电话来引导其深入使用,为了调动其积极性可以提供优惠券、运用会员机会体系或策划互动活动等。

3、保留Retention——使用户继续使用你的产品或服务,保持和提升其忠诚度,

为此,品牌需要定期更新产品、提供新的功能和服务,以满足用户的需求和期望,保持用户的兴趣和参与度,并建立优质的客户服务体系,及时回应用户的问题和反馈,通过专业、友好的客户服务提高用户忠诚度;

其次,品牌可以通过邮件、通知、活动等方式与用户保持长期的联系和互动,也可以尝试建立有效的激励制度,例如积分、等级、优惠券等,以激励用户更频繁地使用产品或服务;

此外,品牌也可以建立用户社区,鼓励用户之间的互动和交流和更好地了解用户需求和反馈,强化他们对品牌文化的认可、参与产品服务的开发和升级、增加用户粘性和品牌忠诚度。

4、收益Revenue——制定和优化定价策略,并提供便捷的支付方式,让用户愿意也容易为产品服务付费。

在定价策略上,企业需要综合考虑多种因素,以确保价格与产品或服务的价值相匹配并具有竞争力,例如价值定价(确保价格与用户感知的价值相匹配)、市场比较定价(根据竞争对手的价格来设定自己的价格,以提供更具竞争力的定价)、差别定价(根据不同的用户群体、产品版本或服务等级来设定不同的价格),从而满足不同用户的需求和期望,提升其付费的意愿。

同时,企业需要选择合适的付费模式以吸引和满足用户的需求。例如,存储和在线办公套件等频繁使用的服务可以采取按月或按年付费的订阅模式,既为用户提供灵活的付费选择的同时又保证企业的持续收益;软件和游戏等用户一次性付费即可永久使用的产品可以采用买断模式,通过提供永久的使用权增加付费购买意愿;也可以采用“免费+增值”模式是基础功能免费,高级功能收费的模式,例如如广告免费、无广告收费的区别服务模式,在保证大用户基础的同时促使用户付费享受更高品质的服务。

此外,品牌还要支持信用卡、支付宝、微信支付等多种支付方式,并提供清晰的支付流程说明,确保用户能够轻松完成支付;还可以开展如折扣、买一送一、捆绑销售等促销活动,吸引用户付费,充分挖掘留存用户所潜藏的商业价值。

5、推荐Refer——调懂用户推荐品牌或产品给其他人。

为了激励用户推荐品牌或产品给其他人,企业需要提供优秀的用户体验,让用户对产品或服务满意并产生推荐意愿,并建立有效的推荐机制,如推荐奖励、分享功能等来激励用户积极参与推荐活动。

此外,品牌要善于利用社交媒体和口碑营销来进一步扩大品牌的知名度和影响力,例如策划有奖互动活动来引导老用户参与和吸引更多潜在用户的关注和信任,并通过建立话题标签或品牌专区来沉淀相关的传播内容,以实现更长期的可持续的品牌影响。

四、结语

AARRR模型提供了一个全面且系统的营销框架,旨在指导品牌更好地理解用户并与其建立长期关系。本模型关注用户全生命周期,通过获取、激活、留存、收入和推荐来吸引更多潜在用户、提高用户满意度和忠诚度、增加收益、扩大影响力,并最终实现持续的增长和成功。

因此,理解和运用AARRR模型对于品牌的长期发展至关重要。这需要品牌不仅关注单一的销售或推广活动,而要从更高层次的视角和更长时间周期来看待与用户的关系,并以数据分析驱动精准高效的营销活动,在用户生命周期的不同阶段实现相应的营销目标。

` ); const dialogVisible = ref(false); const list = ref(["-"]); @@ -1090,6 +1093,65 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveSubmitData = () => { + const params = { + stepFiveA: step1Params1.value, + stepFiveB: step1Params2.value, + stepFiveC: step2Params1.value, + stepFiveD: step2Params2.value, + stepSixA: step3Params1.value, + stepSixB: step3Params2.value, + stepSixC: step4Params1.value, + stepSixD: step4Params2.value, + stepSevenA: step4Params3.value, + stepSevenB: step4Params4.value, + stepSevenC: step5Params.value, + // stepSevenD: step4Data.value[1].params1, + // stepEightA: step4Data.value[2].params1, + // stepEightB: step4Data.value[3].params1, + // stepEightC: step4Data.value[4].params1, + // stepEightD: step4Data.value[5].params1, + // stepNineA: step4Data.value[6].params1, + // stepNineB: step4Data.value[7].params1, + // stepNineC: step4Data.value[8].params1, + // stepNineD: step4Data.value[9].params1, + // stepTenA: step4DataTable2.value[0].params1, + // stepTenB: step4DataTable2.value[1].params1, + // stepTenC: analysisConclusionStep1.value, + // stepTenD: analysisConclusionStep2.value, + // stepElevenA: analysisConclusionStep3.value, + // stepOneA: analysisConclusionStep4.value, + // stepOneB: step1Data.value[8].params3, + // stepOneC: step1Data.value[9].params1, + // stepOneD: step1Data.value[9].params2, + // stepTwoA: step1Data.value[9].params3, + // stepTwoB: step4Data.value[6].params2, + // stepTwoC: step5DataTable2.value[0].params, + // stepThreeA: step5DataTable2.value[1].params, + // stepThreeB: step5DataTable2.value[2].params, + // stepThreeC: step5DataTable2.value[3].params, + // stepThreeD: step5DataTable2.value[4].params, + // stepFourA: step5DataTable3.value[0].params1, + // stepFourB: step5DataTable3.value[0].params2, + // stepFourC: step5DataTable3.value[1].params1, + // stepFourD: step5DataTable3.value[1].params2, + // stepNewOne: step5DataTable3.value[2].params1, + // stepNewTwo: step5DataTable3.value[2].params2, + // stepNewThree: step5DataTable3.value[3].params1, + // stepNewFive: step5DataTable3.value[3].params2, + // stepNewSix: step5DataTable3.value[4].params1, + // stepNewEight: step5DataTable3.value[4].params2, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "AARRR用户运营分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const getData = () => { portraitModel .getData({ @@ -1143,6 +1205,11 @@ const getData = () => { // step5DataTable3.value[3].params2 = res.data.stepNewFive; // step5DataTable3.value[4].params1 = res.data.stepNewSix; // step5DataTable3.value[4].params2 = res.data.stepNewEight; + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + + } }) .catch((error) => {}); }; @@ -1163,6 +1230,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveSubmitData(); }) .catch((error) => {}); }; diff --git a/src/views/portraitModel/components/abcAnalysis.vue b/src/views/portraitModel/components/abcAnalysis.vue index 95092e0..7165e7a 100644 --- a/src/views/portraitModel/components/abcAnalysis.vue +++ b/src/views/portraitModel/components/abcAnalysis.vue @@ -896,10 +896,10 @@ const saveData = () => { stepEightB: step4Data.value[1].params, stepEightC: step4Data.value[2].params, stepEightD: step4Data.value[3].params, - // stepNineA: step4Data.value[0].params1, - // stepNineB: step4Data.value[0].params2, - // stepNineC: step4Data.value[0].params3, - // stepNineD: step4Data.value[1].params1, + stepNineA: chartParam1.value, + stepNineB: chartParam2.value, + stepNineC: chartParam3.value, + stepNineD: chartParam4.value, // stepTenA: step4Data.value[1].params2, // stepTenB: step4Data.value[1].params3, // stepTenC: step4Data.value[2].params1, @@ -930,6 +930,59 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveDataSubmit = () => { + const params = { + stepFiveA: step1Data.value[0].params, + stepFiveB: step1Data.value[1].params, + stepFiveC: step1Data.value[2].params, + stepFiveD: step1Data.value[3].params, + stepSixA: step3Data.value[0].params1, + stepSixB: step3Data.value[0].params2, + stepSixC: step3Data.value[1].params1, + stepSixD: step3Data.value[1].params2, + stepSevenA: step3Data.value[2].params1, + stepSevenB: step3Data.value[2].params2, + stepSevenC: step3Data.value[3].params1, + stepSevenD: step3Data.value[3].params2, + stepEightA: step4Data.value[0].params, + stepEightB: step4Data.value[1].params, + stepEightC: step4Data.value[2].params, + stepEightD: step4Data.value[3].params, + stepNineA: chartParam1.value, + stepNineB: chartParam2.value, + stepNineC: chartParam3.value, + stepNineD: chartParam4.value, + // stepTenA: step4Data.value[1].params2, + // stepTenB: step4Data.value[1].params3, + // stepTenC: step4Data.value[2].params1, + // stepTenD: step4Data.value[2].params2, + // stepElevenA: step4Data.value[2].params3, + // stepOneA: step5Data.value[0].params1, + // stepOneB: step5Data.value[0].params2, + // stepOneC: step5Data.value[0].params3, + // stepOneD: step6Data.value[0].params1, + // stepTwoA: step6Data.value[0].params2, + // stepTwoB: step6Data.value[0].params3, + // stepTwoC: step6Data.value[1].params1, + // stepThreeA: step6Data.value[1].params2, + // stepThreeB: step6Data.value[1].params3, + // stepThreeC: step6Data.value[2].params1, + // stepThreeD: step6Data.value[2].params2, + // stepFourA: step6Data.value[2].params3, + // stepFourB: step7Data.value[0].params, + // stepFourC: step7Data.value[1].params, + // stepFourD: step7Data.value[2].params, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "帕累托/ABC分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const getData = () => { portraitModel .getData({ @@ -953,10 +1006,28 @@ const getData = () => { step4Data.value[1].params = res.data.stepEightB; step4Data.value[2].params = res.data.stepEightC; step4Data.value[3].params = res.data.stepEightD; - // step4Data.value[0].params1 = res.data.stepNineA; - // step4Data.value[0].params2 = res.data.stepNineB; - // step4Data.value[0].params3 = res.data.stepNineC; - // step4Data.value[1].params1 = res.data.stepNineD; + chartParam1.value = res.data.stepNineA; + chartParam2.value = res.data.stepNineB; + chartParam3.value = res.data.stepNineC; + chartParam4.value = res.data.stepNineD; + //将所填值同步到第二步表格 + step2Data.value[0].amount = step1Data.value[0].params; + step2Data.value[1].amount = step1Data.value[1].params; + step2Data.value[2].amount = step1Data.value[2].params; + step2Data.value[3].amount = step1Data.value[3].params; + if (res.data.subState & (res.data.subState == 1)) { + step4Data.value[0].data2 = step3Data.value[0].data2; + step4Data.value[1].data2 = step3Data.value[1].data2; + step4Data.value[2].data2 = step3Data.value[2].data2; + step4Data.value[3].data2 = step3Data.value[3].data2; + step4Data.value[0].data3 = step3Data.value[0].params2; + step4Data.value[1].data3 = step3Data.value[1].params2; + step4Data.value[2].data3 = step3Data.value[2].params2; + step4Data.value[3].data3 = step3Data.value[3].params2; + step2AnswerFlag.value =true + step3AnswerFlag.value = true; + initCharts(); + } // step4Data.value[1].params2 = res.data.stepTenA; // step4Data.value[1].params3 = res.data.stepTenB; // step4Data.value[2].params1 = res.data.stepTenC; @@ -977,6 +1048,92 @@ const getData = () => { // step7Data.value[0].params = res.data.stepFourB; // step7Data.value[1].params = res.data.stepFourC; // step7Data.value[2].params = res.data.stepFourD; + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + if (step1Data.value[0].params && step1Data.value[0].params !== "5900") { + step1Data.value[0].isError = true; + } else { + step1Data.value[0].isError = false; + } + if (step1Data.value[1].params && step1Data.value[1].params !== "600") { + step1Data.value[1].isError = true; + } else { + step1Data.value[1].isError = false; + } + if (step1Data.value[2].params && step1Data.value[2].params !== "1100") { + step1Data.value[2].isError = true; + } else { + step1Data.value[2].isError = false; + } + if (step1Data.value[3].params && step1Data.value[3].params !== "2400") { + step1Data.value[3].isError = true; + } else { + step1Data.value[3].isError = false; + } + + if (step3Data.value[0].params1 && step3Data.value[0].params1 !== "59") { + step3Data.value[0].isError1 = true; + } else { + step3Data.value[0].isError1 = false; + } + if (step3Data.value[0].params2 && step3Data.value[0].params2 !== "59") { + step3Data.value[0].isError2 = true; + } else { + step3Data.value[0].isError2 = false; + } + if (step3Data.value[1].params1 && step3Data.value[1].params1 !== "24") { + step3Data.value[1].isError1 = true; + } else { + step3Data.value[1].isError1 = false; + } + if (step3Data.value[1].params2 && step3Data.value[1].params2 !== "83") { + step3Data.value[1].isError2 = true; + } else { + step3Data.value[1].isError2 = false; + } + if (step3Data.value[2].params1 && step3Data.value[2].params1 !== "11") { + step3Data.value[2].isError1 = true; + } else { + step3Data.value[2].isError1 = false; + } + if (step3Data.value[2].params2 && step3Data.value[2].params2 !== "94") { + step3Data.value[2].isError2 = true; + } else { + step3Data.value[2].isError2 = false; + } + if (step3Data.value[3].params1 && step3Data.value[3].params1 !== "6") { + step3Data.value[3].isError1 = true; + } else { + step3Data.value[3].isError1 = false; + } + if (step3Data.value[3].params2 && step3Data.value[3].params2 !== "100") { + step3Data.value[3].isError2 = true; + } else { + step3Data.value[3].isError2 = false; + } + + if (step4Data.value[0].params && step4Data.value[0].params !== "A类商品") { + step4Data.value[0].isError = true; + } else { + step4Data.value[0].isError = false; + } + if (step4Data.value[1].params && step4Data.value[1].params !== "B类商品") { + step4Data.value[1].isError = true; + } else { + step4Data.value[1].isError = false; + } + if (step4Data.value[2].params && step4Data.value[2].params !== "C类商品") { + step4Data.value[2].isError = true; + } else { + step4Data.value[2].isError = false; + } + if (step4Data.value[3].params && step4Data.value[3].params !== "C类商品") { + step4Data.value[3].isError = true; + } else { + step4Data.value[3].isError = false; + } + } }) .catch((error) => {}); }; @@ -1002,6 +1159,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveDataSubmit(); }) .catch((error) => {}); }; diff --git a/src/views/portraitModel/components/basketAnalysis.vue b/src/views/portraitModel/components/basketAnalysis.vue index db1956c..5c540cd 100644 --- a/src/views/portraitModel/components/basketAnalysis.vue +++ b/src/views/portraitModel/components/basketAnalysis.vue @@ -749,7 +749,7 @@ const step4Submit = () => { if (step4Data.value[5].params && step4Data.value[5].params !== "负向效果") { step4Error.value++; step4Data.value[5].isError = true; - }else{ + } else { step4Data.value[5].isError = false; } // console.log(666); @@ -809,6 +809,59 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveDataSubmit = () => { + const params = { + stepFiveA: list.value.join(","), + stepFiveB: step2Data.value[0].params, + stepFiveC: step2Data.value[1].params, + stepFiveD: step2Data.value[2].params, + stepSixA: step2Data.value[3].params, + stepSixB: step3Data.value[0].params1, + stepSixC: step3Data.value[0].params2, + stepSixD: step3Data.value[0].params3, + stepSevenA: step3Data.value[1].params1, + stepSevenB: step3Data.value[1].params2, + stepSevenC: step3Data.value[1].params3, + stepSevenD: step3Data.value[2].params1, + stepEightA: step3Data.value[2].params2, + stepEightB: step3Data.value[2].params3, + stepEightC: step3Data.value[3].params1, + stepEightD: step3Data.value[3].params2, + stepNineA: step3Data.value[3].params3, + stepNineB: step3Data.value[4].params1, + stepNineC: step3Data.value[4].params2, + stepNineD: step3Data.value[4].params3, + stepTenA: step3Data.value[5].params1, + stepTenB: step3Data.value[5].params2, + stepTenC: step3Data.value[5].params3, + stepTenD: step4Data.value[0].params, + stepElevenA: step4Data.value[1].params, + stepOneA: step4Data.value[2].params, + stepOneB: step4Data.value[3].params, + stepOneC: step4Data.value[4].params, + stepOneD: step4Data.value[5].params, + // stepTwoA: step6Data.value[0].params2, + // stepTwoB: step6Data.value[0].params3, + // stepTwoC: step6Data.value[1].params1, + // stepThreeA: step6Data.value[1].params2, + // stepThreeB: step6Data.value[1].params3, + // stepThreeC: step6Data.value[2].params1, + // stepThreeD: step6Data.value[2].params2, + // stepFourA: step6Data.value[2].params3, + // stepFourB: step7Data.value[0].params, + // stepFourC: step7Data.value[1].params, + // stepFourD: step7Data.value[2].params, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "购物篮分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const getData = () => { portraitModel .getData({ @@ -860,6 +913,158 @@ const getData = () => { // step7Data.value[1].params = res.data.stepFourC; // step7Data.value[2].params = res.data.stepFourD; // console.log(list.value); + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + if (!isArrayContained(list.value, combinationRight.value)) { + comListError.value = true; + } else { + comListError.value = false; + } + + if (step2Data.value[0].params && step2Data.value[0].params !== "60") { + step2Data.value[0].isError = true; + } else { + step2Data.value[0].isError = false; + } + if (step2Data.value[1].params && step2Data.value[1].params !== "60") { + step2Data.value[1].isError = true; + } else { + step2Data.value[1].isError = false; + } + if (step2Data.value[2].params && step2Data.value[2].params !== "80") { + step2Data.value[2].isError = true; + } else { + step2Data.value[2].isError = false; + } + if (step2Data.value[3].params && step2Data.value[3].params !== "60") { + step2Data.value[3].isError = true; + } else { + step2Data.value[3].isError = false; + } + + if (step3Data.value[0].params1 && step3Data.value[0].params1 !== "40" && step3Data.value[0].params1 !== "40.00") { + step3Data.value[0].isError1 = true; + } else { + step3Data.value[0].isError1 = false; + } + if (step3Data.value[0].params2 && step3Data.value[0].params2 !== "66.67") { + step3Data.value[0].isError2 = true; + } else { + step3Data.value[0].isError2 = false; + } + if (step3Data.value[0].params3 && step3Data.value[0].params3 !== "111.11") { + step3Data.value[0].isError3 = true; + } else { + step3Data.value[0].isError3 = false; + } + if (step3Data.value[1].params1 && step3Data.value[1].params1 !== "20" && step3Data.value[1].params1 !== "20.00") { + step3Data.value[1].isError1 = true; + } else { + step3Data.value[1].isError1 = false; + } + if (step3Data.value[1].params2 && step3Data.value[1].params2 !== "33.33") { + step3Data.value[1].isError2 = true; + } else { + step3Data.value[1].isError2 = false; + } + if (step3Data.value[1].params3 && step3Data.value[1].params3 !== "41.67") { + step3Data.value[1].isError3 = true; + } else { + step3Data.value[1].isError3 = false; + } + if (step3Data.value[2].params1 && step3Data.value[2].params1 !== "40" && step3Data.value[2].params1 !== "40.00") { + step3Data.value[2].isError1 = true; + } else { + step3Data.value[2].isError1 = false; + } + if (step3Data.value[2].params2 && step3Data.value[2].params2 !== "60.00" && step3Data.value[2].params2 !== "60") { + step3Data.value[2].isError2 = true; + } else { + step3Data.value[2].isError2 = false; + } + if (step3Data.value[2].params3 && step3Data.value[2].params3 !== "100.00" && step3Data.value[2].params3 !== "100") { + step3Data.value[2].isError3 = true; + } else { + step3Data.value[2].isError3 = false; + } + if (step3Data.value[3].params1 && step3Data.value[3].params1 !== "20" && step3Data.value[3].params1 !== "20.00") { + step3Data.value[3].isError1 = true; + } else { + step3Data.value[3].isError1 = false; + } + if (step3Data.value[3].params2 && step3Data.value[3].params2 !== "33.33") { + step3Data.value[3].isError2 = true; + } else { + step3Data.value[3].isError2 = false; + } + if (step3Data.value[3].params3 && step3Data.value[3].params3 !== "41.67") { + step3Data.value[3].isError3 = true; + } else { + step3Data.value[3].isError3 = false; + } + if (step3Data.value[4].params1 && step3Data.value[4].params1 !== "20" && step3Data.value[4].params1 !== "20.00") { + step3Data.value[4].isError1 = true; + } else { + step3Data.value[4].isError1 = false; + } + if (step3Data.value[4].params2 && step3Data.value[4].params2 !== "33.33") { + step3Data.value[4].isError2 = true; + } else { + step3Data.value[4].isError2 = false; + } + if (step3Data.value[4].params3 && step3Data.value[4].params3 !== "55.56") { + step3Data.value[4].isError3 = true; + } else { + step3Data.value[4].isError3 = false; + } + if (step3Data.value[5].params1 && step3Data.value[5].params1 !== "40" && step3Data.value[5].params1 !== "40.00") { + step3Data.value[5].isError1 = true; + } else { + step3Data.value[5].isError1 = false; + } + if (step3Data.value[5].params2 && step3Data.value[5].params2 !== "50.00" && step3Data.value[5].params2 !== "50") { + step3Data.value[5].isError2 = true; + } else { + step3Data.value[5].isError2 = false; + } + if (step3Data.value[5].params3 && step3Data.value[5].params3 !== "83.33") { + step3Data.value[5].isError3 = true; + } else { + step3Data.value[5].isError3 = false; + } + + if (step4Data.value[0].params && step4Data.value[0].params !== "提升效果") { + step4Data.value[0].isError = true; + } else { + step4Data.value[0].isError = false; + } + if (step4Data.value[1].params && step4Data.value[1].params !== "负向效果") { + step4Data.value[1].isError = true; + } else { + step4Data.value[1].isError = false; + } + if (step4Data.value[2].params && step4Data.value[2].params !== "没有影响") { + step4Data.value[2].isError = true; + } else { + step4Data.value[2].isError = false; + } + if (step4Data.value[3].params && step4Data.value[3].params !== "负向效果") { + step4Data.value[3].isError = true; + } else { + step4Data.value[3].isError = false; + } + if (step4Data.value[4].params && step4Data.value[4].params !== "负向效果") { + step4Data.value[4].isError = true; + } else { + step4Data.value[4].isError = false; + } + if (step4Data.value[5].params && step4Data.value[5].params !== "负向效果") { + step4Data.value[5].isError = true; + } else { + step4Data.value[5].isError = false; + } + } }) .catch((error) => {}); }; @@ -892,6 +1097,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveDataSubmit(); }) .catch((error) => {}); }; diff --git a/src/views/portraitModel/components/bostonAnalysis.vue b/src/views/portraitModel/components/bostonAnalysis.vue index 6b7b80d..5a28a2e 100644 --- a/src/views/portraitModel/components/bostonAnalysis.vue +++ b/src/views/portraitModel/components/bostonAnalysis.vue @@ -242,21 +242,21 @@
现金流客户
-
+
X √
问题客户
-
+
X √
瘦狗客户
-
+
X √
@@ -1487,6 +1487,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveDataSubmit(); }) .catch((error) => {}); }; @@ -1548,6 +1549,65 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveDataSubmit = () => { + const params = { + stepFiveA: step1Data.value[0].params1, + stepFiveB: step1Data.value[0].params2, + stepFiveC: step1Data.value[1].params1, + stepFiveD: step1Data.value[1].params2, + stepSixA: step1Data.value[2].params1, + stepSixB: step1Data.value[2].params2, + stepSixC: step1Data.value[3].params1, + stepSixD: step1Data.value[3].params2, + stepSevenA: step2Data.value[4].params1, + stepSevenB: step2Data.value[4].params2, + stepSevenC: JSON.stringify(chart1OptionData.value), + stepSevenD: JSON.stringify(chart2OptionData.value), + stepEightA: optionStep5.value.series[0].markArea.data[0][0].name, + stepEightB: optionStep5.value.series[0].markArea.data[1][0].name, + stepEightC: optionStep5.value.series[0].markArea.data[2][0].name, + stepEightD: optionStep5.value.series[0].markArea.data[3][0].name, + stepNineA: optionStep5.value.series[0].markArea.data[0][0].itemStyle.color, + stepNineB: optionStep5.value.series[0].markArea.data[1][0].itemStyle.color, + stepNineC: optionStep5.value.series[0].markArea.data[2][0].itemStyle.color, + stepNineD: optionStep5.value.series[0].markArea.data[3][0].itemStyle.color, + // stepTenA: step2Data.value[14].params, + // stepTenB: step2Data.value[15].params, + // stepTenC: step2Data.value[16].params, + // stepTenD: step2Data.value[17].params, + // stepElevenA: step2Data.value[18].params, + // stepOneA: step2Data.value[19].params, + // stepOneB: step2Data.value[20].params, + // stepOneC: step2Data.value[21].params, + // stepOneD: step2Data.value[22].params, + // stepTwoA: step2Data.value[23].params, + // stepTwoB: step2Data.value[24].params, + // stepTwoC: step2Data.value[25].params, + // stepThreeA: step2Data.value[26].params, + // stepThreeB: step2Data.value[27].params, + // stepThreeC: step2Data.value[28].params, + // stepThreeD: step2Data.value[29].params, + // stepFourA: step2Data.value[30].params, + // stepFourB: step2Data.value[31].params, + // stepFourC: step2Data.value[32].params, + // stepFourD: step2Data.value[33].params, + // stepNewOne: step2Data.value[34].params, + // stepNewTwo: step2Data.value[35].params, + // stepNewThree: step2Data.value[36].params, + // stepNewFive: step5DataTable3.value[3].params2, + // stepNewSix: step5DataTable3.value[4].params1, + // stepNewEight: step5DataTable3.value[4].params2, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "波士顿矩阵分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const getData = () => { portraitModel .getData({ @@ -1606,6 +1666,125 @@ const getData = () => { // step5DataTable3.value[4].params1 = res.data.stepNewSix; // step5DataTable3.value[4].params2 = res.data.stepNewEight; // console.log(JSON.parse(res.data.stepSevenC)); + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + if (step1Data.value[0].params1 && step1Data.value[0].params1 !== "3") { + step1Data.value[0].isError1 = true; + } else { + step1Data.value[0].isError1 = false; + } + if (step1Data.value[0].params2 && step1Data.value[0].params2 !== "800") { + step1Data.value[0].isError2 = true; + } else { + step1Data.value[0].isError2 = false; + } + if (step1Data.value[1].params1 && step1Data.value[1].params1 !== "4") { + step1Data.value[1].isError1 = true; + } else { + step1Data.value[1].isError1 = false; + } + if (step1Data.value[1].params2 && step1Data.value[1].params2 !== "1200") { + step1Data.value[1].isError2 = true; + } else { + step1Data.value[1].isError2 = false; + } + if (step1Data.value[2].params1 && step1Data.value[2].params1 !== "6") { + step1Data.value[2].isError1 = true; + } else { + step1Data.value[2].isError1 = false; + } + if (step1Data.value[2].params2 && step1Data.value[2].params2 !== "600") { + step1Data.value[2].isError2 = true; + } else { + step1Data.value[2].isError2 = false; + } + if (step1Data.value[3].params1 && step1Data.value[3].params1 !== "7") { + step1Data.value[3].isError1 = true; + } else { + step1Data.value[3].isError1 = false; + } + if (step1Data.value[3].params2 && step1Data.value[3].params2 !== "1400") { + step1Data.value[3].isError2 = true; + } else { + step1Data.value[3].isError2 = false; + } + + if (step2Data.value[4].params1 && step2Data.value[4].params1 !== "5") { + step2Data.value[4].isError1 = true; + } else { + step2Data.value[4].isError1 = false; + } + if (step2Data.value[4].params2 && step2Data.value[4].params2 !== "1000") { + step2Data.value[4].isError2 = true; + } else { + step2Data.value[4].isError2 = false; + } + + for (let i in chart1OptionData.value) { + // console.log(option.value.series[0].data[i]); + if (chart1OptionData.value[i].name == "A") { + if (Math.round(chart1OptionData.value[i].value[0]) != 3 || Math.round(chart1OptionData.value[i].value[1]) != 800) { + pointAError.value = true; + } else { + pointAError.value = false; + } + } + if (chart1OptionData.value[i].name == "B") { + if (Math.round(chart1OptionData.value[i].value[0]) != 4 || Math.round(chart1OptionData.value[i].value[1]) != 1200) { + pointBError.value = true; + } else { + pointBError.value = false; + } + } + if (chart1OptionData.value[i].name == "C") { + if (Math.round(chart1OptionData.value[i].value[0]) != 6 || Math.round(chart1OptionData.value[i].value[1]) != 600) { + pointCError.value = true; + } else { + pointCError.value = false; + } + } + if (chart1OptionData.value[i].name == "D") { + if (Math.round(chart1OptionData.value[i].value[0]) != 7 || Math.round(chart1OptionData.value[i].value[1]) != 1400) { + pointDError.value = true; + } else { + pointDError.value = false; + } + } + } + + for (let i in chart2OptionData.value) { + // console.log(option2.value.series[0].data[i]); + + if (chart2OptionData.value[i].name == "E") { + if (Math.round(chart2OptionData.value[i].value[0]) != 5 || Math.round(chart2OptionData.value[i].value[1]) != 1000) { + centerPointError.value = true; + } else { + centerPointError.value = false; + } + } + } + + if (quadrant1.value != "明星客户") { + markArea1Error.value = true; + } else { + markArea1Error.value = false; + } + if (quadrant2.value != "问题客户") { + markArea2Error.value = true; + } else { + markArea2Error.value = false; + } + if (quadrant3.value != "瘦狗客户") { + markArea3Error.value = true; + } else { + markArea3Error.value = false; + } + if (quadrant4.value != "现金流客户") { + markArea4Error.value = true; + } else { + markArea4Error.value = false; + } + } step3EchartInit(); step4EchartInit(); step5EchartInit(); diff --git a/src/views/portraitModel/components/funnelAnalysis.vue b/src/views/portraitModel/components/funnelAnalysis.vue index 97667c3..0750417 100644 --- a/src/views/portraitModel/components/funnelAnalysis.vue +++ b/src/views/portraitModel/components/funnelAnalysis.vue @@ -230,10 +230,10 @@ -
- X - √ -
+
+ X + √ +

@@ -244,10 +244,10 @@ -
- X - √ -
+
+ X + √ +

注:实际运用时 ,转化率需要与不同时期比较、与竞争对手、行业平均水平比较,分析的结果相对准确些。

@@ -977,6 +977,87 @@ const getData = () => { // step7Data.value[0].params = res.data.stepFourB; // step7Data.value[1].params = res.data.stepFourC; // step7Data.value[2].params = res.data.stepFourD; + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + const step1DataSlice = step1Data.value.slice(1); + //判断对错 + if (step1DataSlice[0].params1 && step1DataSlice[0].params1 !== "100") { + step1DataSlice[0].isError1 = true; + } else { + step1DataSlice[0].isError1 = false; + } + if (step1DataSlice[0].params2 && step1DataSlice[0].params2 !== "30.0" && step1DataSlice[0].params2 !== "30") { + step1DataSlice[0].isError2 = true; + } else { + step1DataSlice[0].isError2 = false; + } + if (step1DataSlice[0].params3 && step1DataSlice[0].params3 !== "45.7") { + step1DataSlice[0].isError3 = true; + } else { + step1DataSlice[0].isError3 = false; + } + if (step1DataSlice[0].params4 && step1DataSlice[0].params4 !== "82.7") { + step1DataSlice[0].isError4 = true; + } else { + step1DataSlice[0].isError4 = false; + } + if (step1DataSlice[0].params5 && step1DataSlice[0].params5 !== "94.9") { + step1DataSlice[0].isError5 = true; + } else { + step1DataSlice[0].isError5 = false; + } + if (step1DataSlice[1].params1 && step1DataSlice[1].params1 !== "0") { + step1DataSlice[1].isError1 = true; + } else { + step1DataSlice[1].isError1 = false; + } + if (step1DataSlice[1].params2 && step1DataSlice[1].params2 !== "70") { + step1DataSlice[1].isError2 = true; + } else { + step1DataSlice[1].isError2 = false; + } + if (step1DataSlice[1].params3 && step1DataSlice[1].params3 !== "54.3") { + step1DataSlice[1].isError3 = true; + } else { + step1DataSlice[1].isError3 = false; + } + if (step1DataSlice[1].params4 && step1DataSlice[1].params4 !== "17.3") { + step1DataSlice[1].isError4 = true; + } else { + step1DataSlice[1].isError4 = false; + } + if (step1DataSlice[1].params5 && step1DataSlice[1].params5 !== "5.1") { + step1DataSlice[1].isError5 = true; + } else { + step1DataSlice[1].isError5 = false; + } + if (step1DataSlice[2].params1 && step1DataSlice[2].params1 !== "100") { + step1DataSlice[2].isError1 = true; + } else { + step1DataSlice[2].isError1 = false; + } + if (step1DataSlice[2].params2 && step1DataSlice[2].params2 !== "30.0" && step1DataSlice[2].params2 !== "30") { + step1DataSlice[2].isError2 = true; + } else { + step1DataSlice[2].isError2 = false; + } + if (step1DataSlice[2].params3 && step1DataSlice[2].params3 !== "13.7") { + step1DataSlice[2].isError3 = true; + } else { + step1DataSlice[2].isError3 = false; + } + if (step1DataSlice[2].params4 && step1DataSlice[2].params4 !== "11.3") { + step1DataSlice[2].isError4 = true; + } else { + step1DataSlice[2].isError4 = false; + } + if (step1DataSlice[2].params5 && step1DataSlice[2].params5 !== "10.8") { + step1DataSlice[2].isError5 = true; + } else { + step1DataSlice[2].isError5 = false; + } + } }) .catch((error) => {}); }; @@ -997,10 +1078,64 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveDataSubmit(); }) .catch((error) => {}); // console.log(666); }; +const saveDataSubmit = () => { + const params = { + stepFiveA: step1Data.value[1].params1, + stepFiveB: step1Data.value[1].params2, + stepFiveC: step1Data.value[1].params3, + stepFiveD: step1Data.value[1].params4, + stepSixA: step1Data.value[1].params5, + stepSixB: step1Data.value[2].params1, + stepSixC: step1Data.value[2].params2, + stepSixD: step1Data.value[2].params3, + stepSevenA: step1Data.value[2].params4, + stepSevenB: step1Data.value[2].params5, + stepSevenC: step1Data.value[3].params1, + stepSevenD: step1Data.value[3].params2, + stepEightA: step1Data.value[3].params3, + stepEightB: step1Data.value[3].params4, + stepEightC: step1Data.value[3].params5, + stepEightD: step2Select1.value, + stepNineA: step2Select2.value, + stepNineB: step2Select3.value, + stepNineC: step2Select4.value, + stepNineD: step2Select5.value, + stepTenA: step3Select1.value, + stepTenB: step3Select2.value, + // stepTenC: step4Data.value[2].params1, + // stepTenD: step4Data.value[2].params2, + // stepElevenA: step4Data.value[2].params3, + // stepOneA: step5Data.value[0].params1, + // stepOneB: step5Data.value[0].params2, + // stepOneC: step5Data.value[0].params3, + // stepOneD: step6Data.value[0].params1, + // stepTwoA: step6Data.value[0].params2, + // stepTwoB: step6Data.value[0].params3, + // stepTwoC: step6Data.value[1].params1, + // stepThreeA: step6Data.value[1].params2, + // stepThreeB: step6Data.value[1].params3, + // stepThreeC: step6Data.value[2].params1, + // stepThreeD: step6Data.value[2].params2, + // stepFourA: step6Data.value[2].params3, + // stepFourB: step7Data.value[0].params, + // stepFourC: step7Data.value[1].params, + // stepFourD: step7Data.value[2].params, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "转化分析-漏斗模型", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const retrain = () => { portraitModel .retrain({ diff --git a/src/views/portraitModel/components/portraitAnalysis.vue b/src/views/portraitModel/components/portraitAnalysis.vue index ab5ec27..6428930 100644 --- a/src/views/portraitModel/components/portraitAnalysis.vue +++ b/src/views/portraitModel/components/portraitAnalysis.vue @@ -260,7 +260,7 @@ import * as echarts from "echarts"; const { proxy } = getCurrentInstance(); const baseUrl = import.meta.env.VITE_APP_BASE_API; const knowledgeHtml = ref( - `

一、 什么是用户画像

用户画像是指根据用户的属性、用户偏好、生活习惯、用户行为等信息而抽象出来的标签化用户模型。通俗说就是给用户打标签,而标签是通过对用户信息分析而来的高度精炼的特征标识。通过打标签可以利用一些高度概括、容易理解的特征来描述用户,可以让人更容易理解用户,并且可以方便计算机处理。

用户画像是对现实世界中用户的建模,用户画像应该包含目标,方式,组织,标准,验证这5个方面。

目标:指的是描述人,认识人,了解人,理解人。

方式:又分为非形式化手段,如使用文字、语言、图像、视频等方式描述人;形式化手段,即使用数据的方式来刻画人物的画像。

组织:指的是结构化、非结构化的组织形式。

标准:指的是使用常识、共识、知识体系的渐进过程来刻画人物,认识了解用户。

验证:依据侧重说明了用户画像应该来源事实、经得起推理和检验。

二、 用户画像的作用和应用场景

在互联网、电商领域用户画像常用来作为精准营销、推荐系统的基础性工作,其作用总体包括:

(1) 精准营销:根据历史用户特征,分析产品的潜在用户和用户的潜在需求,针对特定群体,利用短信、邮件等方式进行营销。

(2) 用户统计:根据用户的属性、行为特征对用户进行分类后,统计不同特征下的用户数量、分布;分析不同用 户画像群体的分布特征。

(3) 数据挖掘:以用户画像为基础构建推荐系统、搜索引擎、广告投放系统,提升服务精准度。

(4) 服务产品:对产品进行用户画像,对产品进行受众分析,更透彻地理解用户使用产品的心理动机和行为习惯,完善产品运营,提升服务质量。

(5) 行业报告&用户研究:通过用户画像分析可以了解行业动态,比如人群消费习惯、消费偏好分析、不同地域品类消费差异分析

根据用户画像的作用可以看出,用户画像的使用场景较多,用户画像可以用来挖掘用户兴趣、偏好、人口统计学特征,主要目的是提升营销精准度、推荐匹配度,终极目的是提升产品服务,起到提升企业利润。用户画像适合于各个产品周期:从新用户的引流到潜在用户的挖掘、从老用户的培养到流失用户的回流等。

三、 用户画像的分类

用户画像体系和标签分类从两个不同角度来梳理标签,用户画像体系偏战略和应用,标签分类偏管理和技术实现侧。

把标签分成不同的层级和类别:

  • 一是方便管理数千个标签,让散乱的标签体系化;
  • 二是维度并不孤立,标签之间互有关联;
  • 三可以为标签建模提供标签子集,例如计算美妆总体偏好度,主要使用美妆分类的标签集合。

用户画像是对现实用户做的一个数学模型,在整个数学模型中,核心是怎么描述业务知识体系,而这个业务知识体系就是本体论,本体论很复杂,我们找到一个特别朴素的实现,就是标签。

一般来说,将能关联到具体用户数据的标签,称为叶子标签。对叶子标签进行分类汇总的标签,称为父标签。父标签和叶子标签共同构成标签体系,但两者是相对概念。例如:下表中,地市、型号在标签体系中相对于省份、品牌,是叶子标签。

四、 用户画像需要用到哪些数据

一般来说,根据具体的业务内容,会有不同的数据,不同的业务目标,也会使用不同的数据。在互联网领域,用户画像数据可以包括以下内容:

(1)人口属性:包括性别、年龄等人的基本信息

(2)兴趣特征:浏览内容、收藏内容、阅读咨询、购买物品偏好等

(3)消费特征:与消费相关的特征

(4)位置特征:用户所处城市、所处居住区域、用户移动轨迹等

(5)设备属性:使用的终端特征等

(6)行为数据:访问时间、浏览路径等用户在网站的行为日志数据

(7)社交数据:用户社交相关数据

五、 用户画像平台&架构

1、用户画像平台需要实现的功能。

2、用户画像系统技术架构


(1) 数据处理

a、数据指标的梳理来源于各个系统日常积累的日志记录系统,通过sqoop导入hdfs,也可以用代码来实现,比如spark的jdbc连接传统数据库进行数据的cache。还有一种方式,可以通过将数据写入本地文件,然后通过sparksql的load或者hive的export等方式导入HDFS。

b、通过hive编写UDF 或者hiveql根据业务逻辑拼接ETL,使用户对应上不同的用户标签数据(这里的指标可以理解为每个用户打上了相应的标签),生成相应的源表数据,以便于后续用户画像系统,通过不同的规则进行标签宽表的生成。

(2) 数据平台

a、数据平台应用的分布式文件系统为Hadoop的HDFS,因为Hadoop2.0以后,任何的大数据应用都可以通过ResoureManager申请资源,注册服务。比如(sparksubmit、hive)等等。而基于内存的计算框架的出现,就并不选用Hadoop的MapReduce了。当然很多离线处理的业务,很多人还是倾向于使用Hadoop,但是Hadoop封装的函数只有map和Reduce太过单一,而不像spark一类的计算框架有更多封装的函数(可参考博客spark专栏)。可以大大提升开发效率。

b、计算的框架选用Spark以及RHadoop,这里Spark的主要用途有两种,一种是对于数据处理与上层应用所指定的规则的数据筛选过滤,(通过Scala编写spark代码提交至sparksubmit)。一种是服务于上层应用的SparkSQL(通过启动spark thriftserver与前台应用进行连接)。RHadoop的应用主要在于对于标签数据的打分,比如利用协同过滤算法等各种推荐算法对数据进行各方面评分。

c、MongoDB内存数据的应用主要在于对于单个用户的实时的查询,也是通过对spark数据梳理后的标签宽表进行数据格式转换(json格式)导入mongodb,前台应用可通过连接mongodb进行数据转换,从而进行单个标签的展现。(当然也可将数据转换为Redis中的key value形式,导入Redis集群)

d、mysql的作用在于针对上层应用标签规则的存储,以及页面信息的展现。后台的数据宽表是与spark相关联,通过连接mysql随后cache元数据进行filter、select、map、reduce等对元数据信息的整理,再与真实存在于Hdfs的数据进行处理。

(3) 算法部分

用户画像的核心是标签的建立,用户画像标签建立的各个阶段使用的模型和算法如下图所示。

原始数据层。 对原始数据,我们主要使用文本挖掘的算法进行分析如常见的TF-IDF、LDA 等算法,主要是对原始数据的预处理和清洗,对用户数据的匹配和标识。

事实标签层。 通过文本挖掘的方法,我们从数据中尽可能多的提取事实数据信息,如人口属性信息,用户行为信息,消费信息等。其主要使用的算法是分类和聚类。分类主要用于预测新用户,信息不全的用户的信息,对用户进行预测分类。聚类主要用于分析挖掘出具有相同特征的群体信息,进行受众细分,市场细分。对于文本的特征数据,其主要使用相似度计算,如余弦夹角,欧式距离等。

模型标签层。 使用机器学习的方法,结合推荐算法。模型标签层完成对用户的标签建模与用户标识。其主要可以采用的算法有回归,决策树,支持向量机等。通过建模分析,我们可以进一步挖掘出用户的群体特征和个性权重特征,从而完善用户的价值衡量,服务满意度衡量等。

预测层。 也是标签体系中的营销模型预测层。这一层级利用预测算法,如机器学习中的监督学习,计量经济学中的回归预测,数学中的线性规划等方法。实习对用户的流失预测,忠实度预测,兴趣程度预测等等,从而实现精准营销,个性化和定制化服务。

六、 用户画像标签体系的建立

1.标签的级别和属性

(1)标签级别(标签的体系结构)

分级有两个层面的含义,其一是:指标到最低层级的涵盖的层级;其二是指:指标的运算层级。其一非常好理解,这里重点说运算层级。

标签从运算层级角度可以分为三层:事实标签、模型标签、预测标签。

事实标签: 是通过对于原始数据库的数据进行统计分析而来的,比如用户投诉次数,是基于用户一段时间内实际投诉的行为做的统计。

预测标签: 则是在模型的基础上做预测,比如针对投诉倾向类型结构的变化,预测平台舆情风险指数。

(2)标签属性

标签属性可以理解为针对标签进行的再标注,这一环节的工作主要目的是帮助内部理解标签赋值的来源,进而理解指标的含义。如图所示,可以总结为5种来源:

1、固有属性:是指这些指标的赋值体现的是用户生而有之或者事实存在的,不以外界条件或者自身认知的改变而改变的属性。比如:性别、年龄、是否生育等。

2、推导属性:由其他属性推导而来的属性,比如星座,我们可以通过用户的生日推导,比如用户的品类偏好,则可以通过日常购买来推导。

3、行为属性:产品内外实际发生的行为被记录后形成的赋值,比如用户的登陆时间,页面停留时长等。

4、态度属性:用户自我表达的态度和意愿。比如说我们通过一份问卷向用户询问一些问题,并形成标签,如询问用户:是否愿意结婚,是否喜欢某个品牌等。当然在大数据的需求背景下,利用问卷收集用户标签的方法效率显得过低,更多的是利用产品中相关的模块做了用户态度信息收集。

5、测试属性:测试属性是指来自用户的态度表达,但并不是用户直接表达的内容,而是通过分析用户的表达,结构化处理后,得出的测试结论。比如,用户填答了一系列的态度问卷,推导出用户的价值观类型等。

2. 兴趣标签衰减

我们根据用户点击,计算用户对分类、主题、关键词的兴趣,得到用户兴趣标签的权重。最简单的计数方法是用户点击一个主题,就把用户对该主题的所有标签在用户兴趣上加一。

用户对每个标签词的兴趣计算就使用如下的公式:

其中:词在这次浏览的主题中出现C=1,否则C=0,weight表示词在这个主题中的权重。按道理,我们的主题不能仅仅使用一个主题所属分类来判断,主题应该有N个有序词语来概括。

这样做有两个问题:一个是用户的兴趣累加是线性的,数值会非常大,老的兴趣权重会特别高;另一个是用户的兴趣有很强的时效性,昨天的点击要比一个月之前的点击重要的多,线性叠加无法突出近期兴趣。

为了解决这个问题,需要要对用户兴趣得分进行衰减,我们使用如下的方法对兴趣得分进行次数衰减和时间衰减。

次数衰减的公式如下:

其中,α是衰减因子,每次都对上一次的分数做衰减,最终得分会收敛到一个稳定值 ,α取0.9时,得分会无限接近10。

时间衰减的公式如下:

它表示根据时间对兴趣进行衰减,这样做可以保证时间较早的兴趣会在一段时间以后变的非常弱,同时近期的兴趣会有更大的权重。根据用户兴趣变化的速度、用户活跃度等因素,也可以对兴趣进行周级别、月级别或小时级别的衰减。

七、 用户画像验证

方法:

对于兴趣画像的常用评估方法是设计小流量的A/B-test进行验证。我们可以筛选一部分标签用户,给这部分用户进行和标签相关的推送,看标签用户对相关内容是否有更好的反馈。

例如,在新闻推荐中,我们给用户构建了兴趣画像,我们从体育类兴趣用户中选取一小批用户,给他们推送体育类新闻,如果这批用户的点击率和阅读时长明显高于平均水平,就说明标签是有效的。

1. 效果评估

用户画像效果最直接的评估方法就是看其对实际业务的提升,如互联网广告投放中画像效果主要看使用画像以后点击率和收入的提升,精准营销过程中主要看使用画像后销量的提升等。

用户画像的评估指标主要是指准确率、覆盖率、时效性等指标。

1.1 准确率

标签的准确率指的是被打上正确标签的用户比例,准确率是用户画像最核心的指标,一个准确率非常低的标签是没有应用价值的。准确率的计算公式如下:

其中| Utag |表示被打上标签的用户数,| Utag=true |表示有标签用户中被打对标签的用户数。准确率的评估一般有两种方法:一种是在标注数据集里留一部分测试数据用于计算模型的准确率;另一种是在全量用户中抽一批用户,进行人工标注,评估准确率。

由于初始的标注数据集的分布和全量用户分布相比可能有一定偏差,故后一种方法的数据更可信。准确率一般是对每个标签分别评估,多个标签放在一起评估准确率是没有意义的。

1.2 覆盖率

标签的覆盖率指的是被打上标签的用户占全量用户的比例,我们希望标签的覆盖率尽可能的高。但覆盖率和准确率是一对矛盾的指标,需要对二者进行权衡,一般的做法是在准确率符合一定标准的情况下,尽可能的提升覆盖率。

我们希望覆盖尽可能多的用户,同时给每个用户打上尽可能多的标签,因此标签整体的覆盖率一般拆解为两个指标来评估。一个是标签覆盖的用户比例,另一个是覆盖用户的人均标签数,前一个指标是覆盖的广度,后一个指标表示覆盖的密度。

用户覆盖比例的计算方法是:

其中| U |表示用户的总数,| Utag |表示被打上标签的用户数。

人均标签数的计算方法是:

其中| tagi |表示每个用户的标签数,| Utag |表示被打上标签的用户数。覆盖率既可以对单一标签计算,也可以对某一类标签计算,还可以对全量标签计算,这些都是有统计意义的。

1.3 时效性

有些标签的时效性很强,如兴趣标签、出现轨迹标签等,一周之前的就没有意义了;有些标签基本没有时效性,如性别、年龄等,可以有一年到几年的有效期。对于不同的标签,需要建立合理的更新机制,以保证标签时间上的有效性。

1.4 其他指标

标签还需要有一定的可解释性,便于理解;同时需要便于维护且有一定的可扩展性,方便后续标签的添加。这些指标难以给出量化的标准,但在构架用户画像时也需要注意。

八、用户画像困难点、用户画像瓶颈

1.用户多渠道信息打通

同一个用户的数据面临多渠道的数据来源、格式、定义方式等,在数据清洗层面上就已经产生了很多的困难。

2.多渠道产品信息打通

同一个用户不同产品,比如微信数据、支付宝、百度检索数据。每个产品都能在不同层面对用户进行精准的定位,但结合起来又不太现实。

3.实时采集用户数据

目前收集用户的行为数据都属于敏感信息,过多或者过详细的数据都没有办法上报,更存在上报缺失等情况。假如再上升到实时收集用户信息,不止对产品来说,用户层面也会有诸多困难。

4.用户数据挖掘建模

挖掘用户有效信息,一直是很多企业想要做的事情,但数据量大,有效数据难以利用,并存在和现有的技术难以结合等。

5.亿级画像系统实践和应用

当用户数据上升到一定程度,就要面临技术上的问题,有些技术并不是不能实现或者准确产出结果,而是需要一定时间去计算,而某些场景迭代快,用户兴趣转移快又需要实时进行画像数据或者结果数据的变动,那对技术也是一方面的难点。

` + `

一、 什么是用户画像

用户画像是指根据用户的属性、用户偏好、生活习惯、用户行为等信息而抽象出来的标签化用户模型。通俗说就是给用户打标签,而标签是通过对用户信息分析而来的高度精炼的特征标识。通过打标签可以利用一些高度概括、容易理解的特征来描述用户,可以让人更容易理解用户,并且可以方便计算机处理。

用户画像是对现实世界中用户的建模,用户画像应该包含目标,方式,组织,标准,验证这5个方面。

目标:指的是描述人,认识人,了解人,理解人。

方式:又分为非形式化手段,如使用文字、语言、图像、视频等方式描述人;形式化手段,即使用数据的方式来刻画人物的画像。

组织:指的是结构化、非结构化的组织形式。

标准:指的是使用常识、共识、知识体系的渐进过程来刻画人物,认识了解用户。

验证:依据侧重说明了用户画像应该来源事实、经得起推理和检验。

二、 用户画像的作用和应用场景

在互联网、电商领域用户画像常用来作为精准营销、推荐系统的基础性工作,其作用总体包括:

(1) 精准营销:根据历史用户特征,分析产品的潜在用户和用户的潜在需求,针对特定群体,利用短信、邮件等方式进行营销。

(2) 用户统计:根据用户的属性、行为特征对用户进行分类后,统计不同特征下的用户数量、分布;分析不同用 户画像群体的分布特征。

(3) 数据挖掘:以用户画像为基础构建推荐系统、搜索引擎、广告投放系统,提升服务精准度。

(4) 服务产品:对产品进行用户画像,对产品进行受众分析,更透彻地理解用户使用产品的心理动机和行为习惯,完善产品运营,提升服务质量。

(5) 行业报告&用户研究:通过用户画像分析可以了解行业动态,比如人群消费习惯、消费偏好分析、不同地域品类消费差异分析

根据用户画像的作用可以看出,用户画像的使用场景较多,用户画像可以用来挖掘用户兴趣、偏好、人口统计学特征,主要目的是提升营销精准度、推荐匹配度,终极目的是提升产品服务,起到提升企业利润。用户画像适合于各个产品周期:从新用户的引流到潜在用户的挖掘、从老用户的培养到流失用户的回流等。

三、 用户画像的分类

用户画像体系和标签分类从两个不同角度来梳理标签,用户画像体系偏战略和应用,标签分类偏管理和技术实现侧。

把标签分成不同的层级和类别:

  • 一是方便管理数千个标签,让散乱的标签体系化;
  • 二是维度并不孤立,标签之间互有关联;
  • 三可以为标签建模提供标签子集,例如计算美妆总体偏好度,主要使用美妆分类的标签集合。

用户画像是对现实用户做的一个数学模型,在整个数学模型中,核心是怎么描述业务知识体系,而这个业务知识体系就是本体论,本体论很复杂,我们找到一个特别朴素的实现,就是标签。

一般来说,将能关联到具体用户数据的标签,称为叶子标签。对叶子标签进行分类汇总的标签,称为父标签。父标签和叶子标签共同构成标签体系,但两者是相对概念。例如:下表中,地市、型号在标签体系中相对于省份、品牌,是叶子标签。

四、 用户画像需要用到哪些数据

一般来说,根据具体的业务内容,会有不同的数据,不同的业务目标,也会使用不同的数据。在互联网领域,用户画像数据可以包括以下内容:

(1)人口属性:包括性别、年龄等人的基本信息

(2)兴趣特征:浏览内容、收藏内容、阅读咨询、购买物品偏好等

(3)消费特征:与消费相关的特征

(4)位置特征:用户所处城市、所处居住区域、用户移动轨迹等

(5)设备属性:使用的终端特征等

(6)行为数据:访问时间、浏览路径等用户在网站的行为日志数据

(7)社交数据:用户社交相关数据

五、 用户画像平台&架构

1、用户画像平台需要实现的功能。

2、用户画像系统技术架构


(1) 数据处理

a、数据指标的梳理来源于各个系统日常积累的日志记录系统,通过sqoop导入hdfs,也可以用代码来实现,比如spark的jdbc连接传统数据库进行数据的cache。还有一种方式,可以通过将数据写入本地文件,然后通过sparksql的load或者hive的export等方式导入HDFS。

b、通过hive编写UDF 或者hiveql根据业务逻辑拼接ETL,使用户对应上不同的用户标签数据(这里的指标可以理解为每个用户打上了相应的标签),生成相应的源表数据,以便于后续用户画像系统,通过不同的规则进行标签宽表的生成。

(2) 数据平台

a、数据平台应用的分布式文件系统为Hadoop的HDFS,因为Hadoop2.0以后,任何的大数据应用都可以通过ResoureManager申请资源,注册服务。比如(sparksubmit、hive)等等。而基于内存的计算框架的出现,就并不选用Hadoop的MapReduce了。当然很多离线处理的业务,很多人还是倾向于使用Hadoop,但是Hadoop封装的函数只有map和Reduce太过单一,而不像spark一类的计算框架有更多封装的函数(可参考博客spark专栏)。可以大大提升开发效率。

b、计算的框架选用Spark以及RHadoop,这里Spark的主要用途有两种,一种是对于数据处理与上层应用所指定的规则的数据筛选过滤,(通过Scala编写spark代码提交至sparksubmit)。一种是服务于上层应用的SparkSQL(通过启动spark thriftserver与前台应用进行连接)。RHadoop的应用主要在于对于标签数据的打分,比如利用协同过滤算法等各种推荐算法对数据进行各方面评分。

c、MongoDB内存数据的应用主要在于对于单个用户的实时的查询,也是通过对spark数据梳理后的标签宽表进行数据格式转换(json格式)导入mongodb,前台应用可通过连接mongodb进行数据转换,从而进行单个标签的展现。(当然也可将数据转换为Redis中的key value形式,导入Redis集群)

d、mysql的作用在于针对上层应用标签规则的存储,以及页面信息的展现。后台的数据宽表是与spark相关联,通过连接mysql随后cache元数据进行filter、select、map、reduce等对元数据信息的整理,再与真实存在于Hdfs的数据进行处理。

(3) 算法部分

用户画像的核心是标签的建立,用户画像标签建立的各个阶段使用的模型和算法如下图所示。

原始数据层。 对原始数据,我们主要使用文本挖掘的算法进行分析如常见的TF-IDF、LDA 等算法,主要是对原始数据的预处理和清洗,对用户数据的匹配和标识。

事实标签层。 通过文本挖掘的方法,我们从数据中尽可能多的提取事实数据信息,如人口属性信息,用户行为信息,消费信息等。其主要使用的算法是分类和聚类。分类主要用于预测新用户,信息不全的用户的信息,对用户进行预测分类。聚类主要用于分析挖掘出具有相同特征的群体信息,进行受众细分,市场细分。对于文本的特征数据,其主要使用相似度计算,如余弦夹角,欧式距离等。

模型标签层。 使用机器学习的方法,结合推荐算法。模型标签层完成对用户的标签建模与用户标识。其主要可以采用的算法有回归,决策树,支持向量机等。通过建模分析,我们可以进一步挖掘出用户的群体特征和个性权重特征,从而完善用户的价值衡量,服务满意度衡量等。

预测层。 也是标签体系中的营销模型预测层。这一层级利用预测算法,如机器学习中的监督学习,计量经济学中的回归预测,数学中的线性规划等方法。实习对用户的流失预测,忠实度预测,兴趣程度预测等等,从而实现精准营销,个性化和定制化服务。

六、 用户画像标签体系的建立

1.标签的级别和属性

(1)标签级别(标签的体系结构)

分级有两个层面的含义,其一是:指标到最低层级的涵盖的层级;其二是指:指标的运算层级。其一非常好理解,这里重点说运算层级。

标签从运算层级角度可以分为三层:事实标签、模型标签、预测标签。

事实标签: 是通过对于原始数据库的数据进行统计分析而来的,比如用户投诉次数,是基于用户一段时间内实际投诉的行为做的统计。

预测标签: 则是在模型的基础上做预测,比如针对投诉倾向类型结构的变化,预测平台舆情风险指数。

(2)标签属性

标签属性可以理解为针对标签进行的再标注,这一环节的工作主要目的是帮助内部理解标签赋值的来源,进而理解指标的含义。如图所示,可以总结为5种来源:

1、固有属性:是指这些指标的赋值体现的是用户生而有之或者事实存在的,不以外界条件或者自身认知的改变而改变的属性。比如:性别、年龄、是否生育等。

2、推导属性:由其他属性推导而来的属性,比如星座,我们可以通过用户的生日推导,比如用户的品类偏好,则可以通过日常购买来推导。

3、行为属性:产品内外实际发生的行为被记录后形成的赋值,比如用户的登陆时间,页面停留时长等。

4、态度属性:用户自我表达的态度和意愿。比如说我们通过一份问卷向用户询问一些问题,并形成标签,如询问用户:是否愿意结婚,是否喜欢某个品牌等。当然在大数据的需求背景下,利用问卷收集用户标签的方法效率显得过低,更多的是利用产品中相关的模块做了用户态度信息收集。

5、测试属性:测试属性是指来自用户的态度表达,但并不是用户直接表达的内容,而是通过分析用户的表达,结构化处理后,得出的测试结论。比如,用户填答了一系列的态度问卷,推导出用户的价值观类型等。

2. 兴趣标签衰减

我们根据用户点击,计算用户对分类、主题、关键词的兴趣,得到用户兴趣标签的权重。最简单的计数方法是用户点击一个主题,就把用户对该主题的所有标签在用户兴趣上加一。

用户对每个标签词的兴趣计算就使用如下的公式:

其中:词在这次浏览的主题中出现C=1,否则C=0,weight表示词在这个主题中的权重。按道理,我们的主题不能仅仅使用一个主题所属分类来判断,主题应该有N个有序词语来概括。

这样做有两个问题:一个是用户的兴趣累加是线性的,数值会非常大,老的兴趣权重会特别高;另一个是用户的兴趣有很强的时效性,昨天的点击要比一个月之前的点击重要的多,线性叠加无法突出近期兴趣。

为了解决这个问题,需要要对用户兴趣得分进行衰减,我们使用如下的方法对兴趣得分进行次数衰减和时间衰减。

次数衰减的公式如下:

其中,α是衰减因子,每次都对上一次的分数做衰减,最终得分会收敛到一个稳定值 ,α取0.9时,得分会无限接近10。

时间衰减的公式如下:

它表示根据时间对兴趣进行衰减,这样做可以保证时间较早的兴趣会在一段时间以后变的非常弱,同时近期的兴趣会有更大的权重。根据用户兴趣变化的速度、用户活跃度等因素,也可以对兴趣进行周级别、月级别或小时级别的衰减。

七、 用户画像验证

方法:

对于兴趣画像的常用评估方法是设计小流量的A/B-test进行验证。我们可以筛选一部分标签用户,给这部分用户进行和标签相关的推送,看标签用户对相关内容是否有更好的反馈。

例如,在新闻推荐中,我们给用户构建了兴趣画像,我们从体育类兴趣用户中选取一小批用户,给他们推送体育类新闻,如果这批用户的点击率和阅读时长明显高于平均水平,就说明标签是有效的。

1. 效果评估

用户画像效果最直接的评估方法就是看其对实际业务的提升,如互联网广告投放中画像效果主要看使用画像以后点击率和收入的提升,精准营销过程中主要看使用画像后销量的提升等。

用户画像的评估指标主要是指准确率、覆盖率、时效性等指标。

1.1 准确率

标签的准确率指的是被打上正确标签的用户比例,准确率是用户画像最核心的指标,一个准确率非常低的标签是没有应用价值的。准确率的计算公式如下:

其中| Utag |表示被打上标签的用户数,| Utag=true |表示有标签用户中被打对标签的用户数。准确率的评估一般有两种方法:一种是在标注数据集里留一部分测试数据用于计算模型的准确率;另一种是在全量用户中抽一批用户,进行人工标注,评估准确率。

由于初始的标注数据集的分布和全量用户分布相比可能有一定偏差,故后一种方法的数据更可信。准确率一般是对每个标签分别评估,多个标签放在一起评估准确率是没有意义的。

1.2 覆盖率

标签的覆盖率指的是被打上标签的用户占全量用户的比例,我们希望标签的覆盖率尽可能的高。但覆盖率和准确率是一对矛盾的指标,需要对二者进行权衡,一般的做法是在准确率符合一定标准的情况下,尽可能的提升覆盖率。

我们希望覆盖尽可能多的用户,同时给每个用户打上尽可能多的标签,因此标签整体的覆盖率一般拆解为两个指标来评估。一个是标签覆盖的用户比例,另一个是覆盖用户的人均标签数,前一个指标是覆盖的广度,后一个指标表示覆盖的密度。

用户覆盖比例的计算方法是:

其中| U |表示用户的总数,| Utag |表示被打上标签的用户数。

人均标签数的计算方法是:

其中| tagi |表示每个用户的标签数,| Utag |表示被打上标签的用户数。覆盖率既可以对单一标签计算,也可以对某一类标签计算,还可以对全量标签计算,这些都是有统计意义的。

1.3 时效性

有些标签的时效性很强,如兴趣标签、出现轨迹标签等,一周之前的就没有意义了;有些标签基本没有时效性,如性别、年龄等,可以有一年到几年的有效期。对于不同的标签,需要建立合理的更新机制,以保证标签时间上的有效性。

1.4 其他指标

标签还需要有一定的可解释性,便于理解;同时需要便于维护且有一定的可扩展性,方便后续标签的添加。这些指标难以给出量化的标准,但在构架用户画像时也需要注意。

八、用户画像困难点、用户画像瓶颈

1.用户多渠道信息打通

同一个用户的数据面临多渠道的数据来源、格式、定义方式等,在数据清洗层面上就已经产生了很多的困难。

2.多渠道产品信息打通

同一个用户不同产品,比如微信数据、支付宝、百度检索数据。每个产品都能在不同层面对用户进行精准的定位,但结合起来又不太现实。

3.实时采集用户数据

目前收集用户的行为数据都属于敏感信息,过多或者过详细的数据都没有办法上报,更存在上报缺失等情况。假如再上升到实时收集用户信息,不止对产品来说,用户层面也会有诸多困难。

4.用户数据挖掘建模

挖掘用户有效信息,一直是很多企业想要做的事情,但数据量大,有效数据难以利用,并存在和现有的技术难以结合等。

5.亿级画像系统实践和应用

当用户数据上升到一定程度,就要面临技术上的问题,有些技术并不是不能实现或者准确产出结果,而是需要一定时间去计算,而某些场景迭代快,用户兴趣转移快又需要实时进行画像数据或者结果数据的变动,那对技术也是一方面的难点。

` ); const dialogVisible = ref(false); const list = ref(["-"]); @@ -958,7 +958,7 @@ const step2Submit = () => { if (sliceList2[i].params != "规则标签") { step2Error.value++; sliceList2[i].isError = true; - }else{ + } else { sliceList2[i].isError = false; } } @@ -967,7 +967,7 @@ const step2Submit = () => { if (sliceList3[i].params != "预测标签") { step2Error.value++; sliceList3[i].isError = true; - }else{ + } else { sliceList3[i].isError = false; } } @@ -1033,6 +1033,65 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveSubmitData = () => { + const params = { + stepFiveA: step1SelectParams1.value, + stepFiveB: step1SelectParams2.value, + stepFiveC: step1SelectParams3.value, + stepFiveD: step1SelectParams4.value, + stepSixA: step1SelectParams5.value, + stepSixB: step1SelectParams6.value, + stepSixC: step2Data.value[0].params, + stepSixD: step2Data.value[1].params, + stepSevenA: step2Data.value[2].params, + stepSevenB: step2Data.value[3].params, + stepSevenC: step2Data.value[4].params, + stepSevenD: step2Data.value[5].params, + stepEightA: step2Data.value[6].params, + stepEightB: step2Data.value[7].params, + stepEightC: step2Data.value[8].params, + stepEightD: step2Data.value[9].params, + stepNineA: step2Data.value[10].params, + stepNineB: step2Data.value[11].params, + stepNineC: step2Data.value[12].params, + stepNineD: step2Data.value[13].params, + stepTenA: step2Data.value[14].params, + stepTenB: step2Data.value[15].params, + stepTenC: step2Data.value[16].params, + stepTenD: step2Data.value[17].params, + stepElevenA: step2Data.value[18].params, + stepOneA: step2Data.value[19].params, + stepOneB: step2Data.value[20].params, + stepOneC: step2Data.value[21].params, + stepOneD: step2Data.value[22].params, + stepTwoA: step2Data.value[23].params, + stepTwoB: step2Data.value[24].params, + stepTwoC: step2Data.value[25].params, + stepThreeA: step2Data.value[26].params, + stepThreeB: step2Data.value[27].params, + stepThreeC: step2Data.value[28].params, + stepThreeD: step2Data.value[29].params, + stepFourA: step2Data.value[30].params, + stepFourB: step2Data.value[31].params, + stepFourC: step2Data.value[32].params, + stepFourD: step2Data.value[33].params, + stepNewOne: step2Data.value[34].params, + stepNewTwo: step2Data.value[35].params, + stepNewThree: step2Data.value[36].params, + // stepNewFive: step5DataTable3.value[3].params2, + // stepNewSix: step5DataTable3.value[4].params1, + // stepNewEight: step5DataTable3.value[4].params2, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "用户画像分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const getData = () => { portraitModel .getData({ @@ -1086,6 +1145,35 @@ const getData = () => { // step5DataTable3.value[3].params2 = res.data.stepNewFive; // step5DataTable3.value[4].params1 = res.data.stepNewSix; // step5DataTable3.value[4].params2 = res.data.stepNewEight; + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + + let sliceList1 = step2Data.value.slice(0, step2Data.value.length - 8); + for (let i in sliceList1) { + if (sliceList1[i].params != "事实标签") { + sliceList1[i].isError = true; + } else { + sliceList1[i].isError = false; + } + } + let sliceList2 = step2Data.value.slice(29, 34); + for (let i in sliceList2) { + if (sliceList2[i].params != "规则标签") { + sliceList2[i].isError = true; + } else { + sliceList2[i].isError = false; + } + } + let sliceList3 = step2Data.value.slice(34); + for (let i in sliceList3) { + if (sliceList3[i].params != "预测标签") { + sliceList3[i].isError = true; + } else { + sliceList3[i].isError = false; + } + } + } }) .catch((error) => {}); }; @@ -1107,6 +1195,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveSubmitData(); }) .catch((error) => {}); }; diff --git a/src/views/portraitModel/components/repurchaseAnalysis.vue b/src/views/portraitModel/components/repurchaseAnalysis.vue index 077c518..62bf0ed 100644 --- a/src/views/portraitModel/components/repurchaseAnalysis.vue +++ b/src/views/portraitModel/components/repurchaseAnalysis.vue @@ -1817,6 +1817,65 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveSubmitData = () => { + const params = { + stepFiveA: step1Data.value[0].params, + stepFiveB: step1Data.value[1].params, + stepFiveC: step1Data.value[2].params, + stepFiveD: step1Data.value[3].params, + stepSixA: step1Data.value[4].params, + stepSixB: step1Data.value[5].params, + stepSixC: step1Data.value[6].params, + stepSixD: step1Data.value[7].params, + stepSevenA: step1Data.value[8].params, + stepSevenB: step1Data.value[9].params, + stepSevenC: step1Data.value[10].params, + stepSevenD: step1Data.value[11].params, + stepEightA: step1Data.value[12].params, + stepEightB: step1Data.value[13].params, + stepEightC: step1Data.value[14].params, + stepEightD: step1Data.value[15].params, + stepNineA: step1Data.value[16].params, + stepNineB: step1Data.value[17].params, + stepNineC: step1Data.value[18].params, + stepNineD: step1Data.value[19].params, + stepTenA: step2Data.value[0].params, + stepTenB: step2Data.value[1].params, + stepTenC: step2Data.value[2].params, + stepTenD: step4Data.value[0].params1, + stepElevenA: step4Data.value[0].params2, + stepOneA: step4Data.value[2].params1, + stepOneB: step4Data.value[2].params2, + stepOneC: step4Data.value[4].params1, + stepOneD: step4Data.value[4].params2, + stepTwoA: step4Data.value[6].params1, + stepTwoB: step4Data.value[6].params2, + stepTwoC: step5DataTable2.value[0].params, + stepThreeA: step5DataTable2.value[1].params, + stepThreeB: step5DataTable2.value[2].params, + stepThreeC: step5DataTable2.value[3].params, + stepThreeD: step5DataTable2.value[4].params, + stepFourA: step5DataTable3.value[0].params1, + stepFourB: step5DataTable3.value[0].params2, + stepFourC: step5DataTable3.value[1].params1, + stepFourD: step5DataTable3.value[1].params2, + stepNewOne: step5DataTable3.value[2].params1, + stepNewTwo: step5DataTable3.value[2].params2, + stepNewThree: step5DataTable3.value[3].params1, + stepNewFive: step5DataTable3.value[3].params2, + stepNewSix: step5DataTable3.value[4].params1, + stepNewEight: step5DataTable3.value[4].params2, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "复购分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const getData = () => { portraitModel .getData({ @@ -1870,6 +1929,241 @@ const getData = () => { step5DataTable3.value[3].params2 = res.data.stepNewFive; step5DataTable3.value[4].params1 = res.data.stepNewSix; step5DataTable3.value[4].params2 = res.data.stepNewEight; + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + if (step1Data.value[0].params && step1Data.value[0].params != "1") { + step1Data.value[0].isError = true; + } else { + step1Data.value[0].isError = false; + } + if (step1Data.value[1].params && step1Data.value[1].params != "2") { + step1Data.value[1].isError = true; + } else { + step1Data.value[1].isError = false; + } + if (step1Data.value[2].params && step1Data.value[2].params != "1") { + step1Data.value[2].isError = true; + } else { + step1Data.value[2].isError = false; + } + if (step1Data.value[3].params && step1Data.value[3].params != "2") { + step1Data.value[3].isError = true; + } else { + step1Data.value[3].isError = false; + } + if (step1Data.value[4].params && step1Data.value[4].params != "3") { + step1Data.value[4].isError = true; + } else { + step1Data.value[4].isError = false; + } + if (step1Data.value[5].params && step1Data.value[5].params != "1") { + step1Data.value[5].isError = true; + } else { + step1Data.value[5].isError = false; + } + if (step1Data.value[6].params && step1Data.value[6].params != "2") { + step1Data.value[6].isError = true; + } else { + step1Data.value[6].isError = false; + } + if (step1Data.value[7].params && step1Data.value[7].params != "3") { + step1Data.value[7].isError = true; + } else { + step1Data.value[7].isError = false; + } + if (step1Data.value[8].params && step1Data.value[8].params != "4") { + step1Data.value[8].isError = true; + } else { + step1Data.value[8].isError = false; + } + if (step1Data.value[9].params && step1Data.value[9].params != "1") { + step1Data.value[9].isError = true; + } else { + step1Data.value[9].isError = false; + } + if (step1Data.value[10].params && step1Data.value[10].params != "2") { + step1Data.value[10].isError = true; + } else { + step1Data.value[10].isError = false; + } + if (step1Data.value[11].params && step1Data.value[11].params != "2") { + step1Data.value[11].isError = true; + } else { + step1Data.value[11].isError = false; + } + if (step1Data.value[12].params && step1Data.value[12].params != "3") { + step1Data.value[12].isError = true; + } else { + step1Data.value[12].isError = false; + } + if (step1Data.value[13].params && step1Data.value[13].params != "4") { + step1Data.value[13].isError = true; + } else { + step1Data.value[13].isError = false; + } + if (step1Data.value[14].params && step1Data.value[14].params != "1") { + step1Data.value[14].isError = true; + } else { + step1Data.value[14].isError = false; + } + if (step1Data.value[15].params && step1Data.value[15].params != "1") { + step1Data.value[15].isError = true; + } else { + step1Data.value[15].isError = false; + } + if (step1Data.value[16].params && step1Data.value[16].params != "1") { + step1Data.value[16].isError = true; + } else { + step1Data.value[16].isError = false; + } + if (step1Data.value[17].params && step1Data.value[17].params != "1") { + step1Data.value[17].isError = true; + } else { + step1Data.value[17].isError = false; + } + if (step1Data.value[18].params && step1Data.value[18].params != "1") { + step1Data.value[18].isError = true; + } else { + step1Data.value[18].isError = false; + } + if (step1Data.value[19].params && step1Data.value[19].params != "1") { + step1Data.value[19].isError = true; + } else { + step1Data.value[19].isError = false; + } + + if (step2Data.value[0].params && step2Data.value[0].params != "10") { + step2Data.value[0].isError = true; + } else { + step2Data.value[0].isError = false; + } + if (step2Data.value[1].params && step2Data.value[1].params != "4") { + step2Data.value[1].isError = true; + } else { + step2Data.value[1].isError = false; + } + if (step2Data.value[2].params && step2Data.value[2].params != "40") { + step2Data.value[2].isError = true; + } else { + step2Data.value[2].isError = false; + } + + if (step4Data.value[0].params1 && step4Data.value[0].params1 != "22") { + step4Data.value[0].isError1 = true; + } else { + step4Data.value[0].isError1 = false; + } + if (step4Data.value[0].params2 && step4Data.value[0].params2 != "30天内") { + step4Data.value[0].isError2 = true; + } else { + step4Data.value[0].isError2 = false; + } + if (step4Data.value[2].params1 && step4Data.value[2].params1 != "10") { + step4Data.value[2].isError1 = true; + } else { + step4Data.value[2].isError1 = false; + } + if (step4Data.value[2].params2 && step4Data.value[2].params2 != "15天内") { + step4Data.value[2].isError2 = true; + } else { + step4Data.value[2].isError2 = false; + } + if (step4Data.value[4].params1 && step4Data.value[4].params1 != "4") { + step4Data.value[4].isError1 = true; + } else { + step4Data.value[4].isError1 = false; + } + if (step4Data.value[4].params2 && step4Data.value[4].params2 != "7天内") { + step4Data.value[4].isError2 = true; + } else { + step4Data.value[4].isError2 = false; + } + + if (step4Data.value[6].params1 && step4Data.value[6].params1 != "2") { + step4Data.value[6].isError1 = true; + } else { + step4Data.value[6].isError1 = false; + } + if (step4Data.value[6].params2 && step4Data.value[6].params2 != "7天内") { + step4Data.value[6].isError2 = true; + } else { + step4Data.value[6].isError2 = false; + } + + if (step5DataTable2.value[0].params && step5DataTable2.value[0].params != "裤子" && step5DataTable2.value[0].params != "外套") { + + step5DataTable2.value[0].isError = true; + } else { + step5DataTable2.value[0].isError = false; + } + if (step5DataTable2.value[1].params && step5DataTable2.value[1].params != "衬衫" && step5DataTable2.value[1].params != "短袖" && step5DataTable2.value[1].params != "睡衣") { + + step5DataTable2.value[1].isError = true; + } else { + step5DataTable2.value[1].isError = false; + } + if (step5DataTable3.value[0].params1 && step5DataTable3.value[0].params1 != "无") { + + step5DataTable3.value[0].isError1 = true; + } else { + step5DataTable3.value[0].isError1 = false; + } + if (step5DataTable3.value[0].params2 && step5DataTable3.value[0].params2 != "0") { + + step5DataTable3.value[0].isError2 = true; + } else { + step5DataTable3.value[0].isError2 = false; + } + if (step5DataTable3.value[1].params1 && step5DataTable3.value[1].params1 != "无") { + + step5DataTable3.value[1].isError1 = true; + } else { + step5DataTable3.value[1].isError1 = false; + } + if (step5DataTable3.value[1].params2 && step5DataTable3.value[1].params2 != "0") { + + step5DataTable3.value[1].isError2 = true; + } else { + step5DataTable3.value[1].isError2 = false; + } + if (step5DataTable3.value[2].params1 && step5DataTable3.value[2].params1 != "无") { + + step5DataTable3.value[2].isError1 = true; + } else { + step5DataTable3.value[2].isError1 = false; + } + if (step5DataTable3.value[2].params2 && step5DataTable3.value[2].params2 != "0") { + + step5DataTable3.value[2].isError2 = true; + } else { + step5DataTable3.value[2].isError2 = false; + } + if (step5DataTable3.value[3].params1 && step5DataTable3.value[3].params1 != "C") { + + step5DataTable3.value[3].isError1 = true; + } else { + step5DataTable3.value[3].isError1 = false; + } + if (step5DataTable3.value[3].params2 && step5DataTable3.value[3].params2 != "1") { + + step5DataTable3.value[3].isError2 = true; + } else { + step5DataTable3.value[3].isError2 = false; + } + if (step5DataTable3.value[4].params1 && step5DataTable3.value[4].params1 != "D") { + + step5DataTable3.value[4].isError1 = true; + } else { + step5DataTable3.value[4].isError1 = false; + } + if (step5DataTable3.value[4].params2 && step5DataTable3.value[4].params2 != "2") { + ; + step5DataTable3.value[4].isError2 = true; + } else { + step5DataTable3.value[4].isError2 = false; + } + } }) .catch((error) => {}); }; @@ -1901,6 +2195,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveSubmitData(); }) .catch((error) => {}); // console.log(666); diff --git a/src/views/portraitModel/components/retainAnalysis.vue b/src/views/portraitModel/components/retainAnalysis.vue index bb3a669..06de356 100644 --- a/src/views/portraitModel/components/retainAnalysis.vue +++ b/src/views/portraitModel/components/retainAnalysis.vue @@ -410,7 +410,7 @@ const { proxy } = getCurrentInstance(); const baseUrl = import.meta.env.VITE_APP_BASE_API; const showAnswerFlag = ref(false); const knowledgeHtml = ref( - `

前言

Tableau的本质还是辅助分析、辅助工作,分析思维不可少。所以在分享Tableau技能之外,也会分享些有关分析技能的内容,统一更新入“Janie碎谈”模块中。

这篇文章是我花了好几个晚上认认真真写的,自己编造的数据,自己画的图,自己写的代码。一方面写的过程中会促使我进入更深层次的思考,边改进边学习边进步;另一方面独乐乐不如众乐乐,分享出来大家一起进步,顺便也帮我指出不足。

网上有关留存的文章很多,这篇不敢说是最全的,但最起码是较全的。由于上班加带娃,又要坚持原创,又要精细的准备每一篇干货,所以大概一周一更。如有做的不足的地方,请于后台留言,督促我改进,以分享更有价值的干货。

在这个用户为大的互联网世界中,我们一起发现更多有意思的点......

目录

  1. 互联网企业面临的留存问题
  2. 什么是留存分析
  3. 留存分析常用口径
  4. 留存分析的适用场景
  5. 留存分析参考代码
  6. 留存下降的可能性原因
  7. 留存分析方法
  8. 案例实攻

1 互联网企业面临的留存问题

  • 流量红利见顶、拉新成本高

在极易同质化的今天,流量竞争着实激烈,提高用户留存的重要性不言而喻,且存量用户的获客成本远远低于拉新成本。

  • 新用户较易流失

企业大规模投广告、做活动,高额支出锁不住新用户,长期可持续发展有些无力,只顾眼前利益治标不治本。

2 什么是留存分析

留存率:某日用户数在第N日仍启动该App的用户比例,留存分析即分析用户随时间变化的活跃情况。获取用户只是第一步,留住用户才是所有产品最终目标。

可以理解为:由初期的摇摆用户转化为忠诚&稳定用户的过程。留存率越高,说明用户对产品越有强烈的依赖感。

可分为三个阶段:

  • 初期:新用户刚注册,用户留存下降较快,需快速让用户感受到产品核心价值。
  • 中期:新用户沉淀下来,形成活跃用户。此时需分析活跃留存,加强核心功能,培养用户对产品的使用习惯。
  • 后期:思考产品核心价值,做好产品迭代与优化。

从时间维度划分:

常见的的有:次日留存、3日留存、7日留存、30日留存、周留存、月留存

从用户维度划分:

常见的的有:新用户留存、活跃留存

图解如下:

​3 留存分析常用口径

以新用户留存为例

次日留存率=(某日新增的用户中,在注册的第2天还进行登录的用户数)/ 该日新增用户数

3日留存率=(某日新增用户中,在注册的第3天还进行登录的用户数)/ 该日新增用户数

7日留存率=(某日新增的用户中,在注册的第7天还进行登录的用户数)/ 该日新增用户数

30日留存率=(某日新增的用户中,在注册的第30天还进行登录的用户数)/ 该日新增用户数

1周后留存率=(某周新增的用户中,在注册的第2周还进行登录的用户数)/ 该周新增用户数

2周后留存率=(某周新增的用户中,在注册的第3周还进行登录的用户数)/ 该周新增用户数

1月后留存率=(某月新增的用户中,在注册的第2月还进行登录的用户数)/ 该月新增用户数

2月后留存率=(某月新增的用户中,在注册的第3月还进行登录的用户数)/ 该月新增用户数

以活跃留存为例

次日留存率=(某日登录的用户中,在第2天还进行登录的用户数)/ 该日登录用户数

3日留存率=(某日登录用户中,在第3天还进行登录的用户数)/ 该日登录用户数

7日留存率=(某日登录的用户中,在第7天还进行登录的用户数)/ 该日登录用户数

30日留存率=(某日登录的用户中,在第30天还进行登录的用户数)/ 该日登录用户数

1周后留存率=(某周登录的用户中,在第2周还进行登录的用户数)/ 该周登录用户数

2周后留存率=(某周登录的用户中,在第3周还进行登录的用户数)/ 该周登录用户数

1月后留存率=(某月登录的用户中,在第2月还进行登录的用户数)/ 该月登录用户数

2月后留存率=(某月登录的用户中,在第3月还进行登录的用户数)/ 该月登录用户数

4 留存分析的适用场景

日留存率

  • 快速判断产品是否迎合市场需求,比如新手对产品的UI设计、功能设置、新手引导等的体验是否满意,是否需做调整。
  • 快速判断用户粘性,比如用户是否更易受促销活动的影响等等。

周留存率

  • 判断用户忠诚度,用户此时对产品基本已有完整的体验。一整套流程体验下来,继续访问的用户可判断为潜在忠诚用户。
  • 分析用户再次访问的原因,找出产品最能巩固用户的点,且参考此点以一贯之,拓展应用到更多的用户身上,促使更多的用户留下来。
    月留存率
  • 评估迭代与优化的效果。砍掉留存率低的产品功能,进行迭代优化。
    5 留存分析参考代码

有些互联网公司面试会出留存SQL题型,该代码仅供参考。

​代码运行结果

语法环境:SparkSql或Impala,其他环境也可以,只是个别函数会略有差别,替换个别函数即可,无需更改代码结构。

参考代码如下:

 --以a表日期作为主体
+  `

前言

Tableau的本质还是辅助分析、辅助工作,分析思维不可少。所以在分享Tableau技能之外,也会分享些有关分析技能的内容,统一更新入“Janie碎谈”模块中。

这篇文章是我花了好几个晚上认认真真写的,自己编造的数据,自己画的图,自己写的代码。一方面写的过程中会促使我进入更深层次的思考,边改进边学习边进步;另一方面独乐乐不如众乐乐,分享出来大家一起进步,顺便也帮我指出不足。

网上有关留存的文章很多,这篇不敢说是最全的,但最起码是较全的。由于上班加带娃,又要坚持原创,又要精细的准备每一篇干货,所以大概一周一更。如有做的不足的地方,请于后台留言,督促我改进,以分享更有价值的干货。

在这个用户为大的互联网世界中,我们一起发现更多有意思的点......

目录

  1. 互联网企业面临的留存问题
  2. 什么是留存分析
  3. 留存分析常用口径
  4. 留存分析的适用场景
  5. 留存分析参考代码
  6. 留存下降的可能性原因
  7. 留存分析方法
  8. 案例实攻

1 互联网企业面临的留存问题

  • 流量红利见顶、拉新成本高

在极易同质化的今天,流量竞争着实激烈,提高用户留存的重要性不言而喻,且存量用户的获客成本远远低于拉新成本。

  • 新用户较易流失

企业大规模投广告、做活动,高额支出锁不住新用户,长期可持续发展有些无力,只顾眼前利益治标不治本。

2 什么是留存分析

留存率:某日用户数在第N日仍启动该App的用户比例,留存分析即分析用户随时间变化的活跃情况。获取用户只是第一步,留住用户才是所有产品最终目标。

可以理解为:由初期的摇摆用户转化为忠诚&稳定用户的过程。留存率越高,说明用户对产品越有强烈的依赖感。

可分为三个阶段:

  • 初期:新用户刚注册,用户留存下降较快,需快速让用户感受到产品核心价值。
  • 中期:新用户沉淀下来,形成活跃用户。此时需分析活跃留存,加强核心功能,培养用户对产品的使用习惯。
  • 后期:思考产品核心价值,做好产品迭代与优化。

从时间维度划分:

常见的的有:次日留存、3日留存、7日留存、30日留存、周留存、月留存

从用户维度划分:

常见的的有:新用户留存、活跃留存

图解如下:

​3 留存分析常用口径

以新用户留存为例

次日留存率=(某日新增的用户中,在注册的第2天还进行登录的用户数)/ 该日新增用户数

3日留存率=(某日新增用户中,在注册的第3天还进行登录的用户数)/ 该日新增用户数

7日留存率=(某日新增的用户中,在注册的第7天还进行登录的用户数)/ 该日新增用户数

30日留存率=(某日新增的用户中,在注册的第30天还进行登录的用户数)/ 该日新增用户数

1周后留存率=(某周新增的用户中,在注册的第2周还进行登录的用户数)/ 该周新增用户数

2周后留存率=(某周新增的用户中,在注册的第3周还进行登录的用户数)/ 该周新增用户数

1月后留存率=(某月新增的用户中,在注册的第2月还进行登录的用户数)/ 该月新增用户数

2月后留存率=(某月新增的用户中,在注册的第3月还进行登录的用户数)/ 该月新增用户数

以活跃留存为例

次日留存率=(某日登录的用户中,在第2天还进行登录的用户数)/ 该日登录用户数

3日留存率=(某日登录用户中,在第3天还进行登录的用户数)/ 该日登录用户数

7日留存率=(某日登录的用户中,在第7天还进行登录的用户数)/ 该日登录用户数

30日留存率=(某日登录的用户中,在第30天还进行登录的用户数)/ 该日登录用户数

1周后留存率=(某周登录的用户中,在第2周还进行登录的用户数)/ 该周登录用户数

2周后留存率=(某周登录的用户中,在第3周还进行登录的用户数)/ 该周登录用户数

1月后留存率=(某月登录的用户中,在第2月还进行登录的用户数)/ 该月登录用户数

2月后留存率=(某月登录的用户中,在第3月还进行登录的用户数)/ 该月登录用户数

4 留存分析的适用场景

日留存率

  • 快速判断产品是否迎合市场需求,比如新手对产品的UI设计、功能设置、新手引导等的体验是否满意,是否需做调整。
  • 快速判断用户粘性,比如用户是否更易受促销活动的影响等等。

周留存率

  • 判断用户忠诚度,用户此时对产品基本已有完整的体验。一整套流程体验下来,继续访问的用户可判断为潜在忠诚用户。
  • 分析用户再次访问的原因,找出产品最能巩固用户的点,且参考此点以一贯之,拓展应用到更多的用户身上,促使更多的用户留下来。
    月留存率
  • 评估迭代与优化的效果。砍掉留存率低的产品功能,进行迭代优化。
    5 留存分析参考代码

有些互联网公司面试会出留存SQL题型,该代码仅供参考。

​代码运行结果

语法环境:SparkSql或Impala,其他环境也可以,只是个别函数会略有差别,替换个别函数即可,无需更改代码结构。

参考代码如下:

 --以a表日期作为主体
 select 
 t.date_a
 ,t.date_b
@@ -445,7 +445,7 @@ from
 )t
 group by 1,2,3
 having t.diff_ab>=0
- 

6 留存分析下降的可能性原因

新用户留存下降

  • 新用户并未快速的感受到产品的核心价值。
  • 新手引导模块体验交差
  • 新用户羊毛党居多
  • 界面UI设计影响使用感
  • 产品功能体验较差
  • ......

老用户留存下降

  • 产品迭代功能致使用户体验变差
  • 产品迭代周期较长,用户丧失新鲜感
  • 受竞品影响
  • 未促使用户对产品形成习惯
  • 连续打卡签到送红包模块优惠力度较小,无坚持意义
  • 广告推送较多
  • 客服服务响应较慢、服务较差
  • 无关推送
  • 产品bug较多
  • 受促销活动影响较大
  • ......

7 留存分析方法

​其中产品功能分析:

目的:找出对留存最有价值的功能&最没价值的功能,便于后期迭代优化。

  1. 卓越功能:建议侧重优化用户体验。
  2. 大众功能:重中之重,建议反思该功能的长期价值与实用性
  3. 小众功能:建议保留该功能,但无需过多投入精力
  4. 弱势功能:建议考虑是否砍掉


​8 案例实攻

案例一

该图是我在ppt上加工出来的,选取了两日来对

​

解析:

2021年5月1日注册的新用户在注册的第7日留存率趋向于平稳,此时留存率60%;2021年5月2日注册的新用户在注册的第7日留存率趋向于平稳,此时留存率20%;2日注册的用户稳定留存率较1日差。

改进思路:

应使得趋向于平稳时的留存率尽可能提高,即平稳的这段线尽量往上提。

案例二

数据纯属个人虚构,实际分析时建议多扩展日期,该图重在解析分析方法。

该表留存率:(某新增的用户中,在第N天还进行登录的用户数)/ 该日新增用户数


以8月1日的新增用户留存为例


  • 新手探索期:单纯靠大额优惠吸引的用户会之间流失,产品价值未达到用户预期。
  • 习惯养成期:产品功能&实用性未促使用户养成使用习惯。
  • 活跃用户期:真正留下来的忠实用户。

解析:

  1. 新用户次留骤减60%:没有使得用户迅速发现产品价值
  2. 整体留存率于第10日趋向于平稳,留存率稳定于11%左右:说明8月1日的新增用户中只有11%左右发展成了忠实用户。
  3. 3留&7留出现留存率增长现象(注意:留存率并不会呈现持续下降情况),进一步定位原因,在8月3日与8月7日是否进行了促销活动?

案例三

解析:

  1. 表格中以8月6日注册用户的次留(71%)为起始点,8月1日注册用户的7留(34%)为结束点,二者形成对角线,纵向对比数据,颜色颜色部分留存率都比较高。首先需要确认8月7日这天运营是否做了动作?比如:该日做了促销活动、或者其他特殊活动?因为8月7日正好对应的是8月6日的次留,8月5日的3留......8月1日的7留。
  2. 表格中8月9日的次留是20%,远低于其他日次留,且后续留存也较其他日偏低,警惕羊毛党。
` +

6 留存分析下降的可能性原因

新用户留存下降

  • 新用户并未快速的感受到产品的核心价值。
  • 新手引导模块体验交差
  • 新用户羊毛党居多
  • 界面UI设计影响使用感
  • 产品功能体验较差
  • ......

老用户留存下降

  • 产品迭代功能致使用户体验变差
  • 产品迭代周期较长,用户丧失新鲜感
  • 受竞品影响
  • 未促使用户对产品形成习惯
  • 连续打卡签到送红包模块优惠力度较小,无坚持意义
  • 广告推送较多
  • 客服服务响应较慢、服务较差
  • 无关推送
  • 产品bug较多
  • 受促销活动影响较大
  • ......

7 留存分析方法

​其中产品功能分析:

目的:找出对留存最有价值的功能&最没价值的功能,便于后期迭代优化。

  1. 卓越功能:建议侧重优化用户体验。
  2. 大众功能:重中之重,建议反思该功能的长期价值与实用性
  3. 小众功能:建议保留该功能,但无需过多投入精力
  4. 弱势功能:建议考虑是否砍掉


​8 案例实攻

案例一

该图是我在ppt上加工出来的,选取了两日来对

​

解析:

2021年5月1日注册的新用户在注册的第7日留存率趋向于平稳,此时留存率60%;2021年5月2日注册的新用户在注册的第7日留存率趋向于平稳,此时留存率20%;2日注册的用户稳定留存率较1日差。

改进思路:

应使得趋向于平稳时的留存率尽可能提高,即平稳的这段线尽量往上提。

案例二

数据纯属个人虚构,实际分析时建议多扩展日期,该图重在解析分析方法。

该表留存率:(某新增的用户中,在第N天还进行登录的用户数)/ 该日新增用户数


以8月1日的新增用户留存为例


  • 新手探索期:单纯靠大额优惠吸引的用户会之间流失,产品价值未达到用户预期。
  • 习惯养成期:产品功能&实用性未促使用户养成使用习惯。
  • 活跃用户期:真正留下来的忠实用户。

解析:

  1. 新用户次留骤减60%:没有使得用户迅速发现产品价值
  2. 整体留存率于第10日趋向于平稳,留存率稳定于11%左右:说明8月1日的新增用户中只有11%左右发展成了忠实用户。
  3. 3留&7留出现留存率增长现象(注意:留存率并不会呈现持续下降情况),进一步定位原因,在8月3日与8月7日是否进行了促销活动?

案例三

解析:

  1. 表格中以8月6日注册用户的次留(71%)为起始点,8月1日注册用户的7留(34%)为结束点,二者形成对角线,纵向对比数据,颜色颜色部分留存率都比较高。首先需要确认8月7日这天运营是否做了动作?比如:该日做了促销活动、或者其他特殊活动?因为8月7日正好对应的是8月6日的次留,8月5日的3留......8月1日的7留。
  2. 表格中8月9日的次留是20%,远低于其他日次留,且后续留存也较其他日偏低,警惕羊毛党。
` ); const dialogVisible = ref(false); const list = ref(["-"]); @@ -1558,6 +1558,65 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveSubmitData = () => { + const params = { + stepFiveA: step1Data.value[0].params, + stepFiveB: step1Data.value[1].params, + stepFiveC: step1Data.value[2].params, + stepFiveD: step1Data.value[3].params, + stepSixA: step1Data.value[4].params, + stepSixB: step1Data.value[5].params, + stepSixC: step1Data.value[6].params, + stepSixD: step1Data.value[7].params, + stepSevenA: step1Data.value[8].params, + stepSevenB: step1Data.value[9].params, + stepSevenC: step1Data.value[10].params, + stepSevenD: step1Data.value[11].params, + stepEightA: step1Data.value[12].params, + stepEightB: step2Data.value[0].params1, + stepEightC: step2Data.value[0].params2, + stepEightD: step2Data.value[0].params3, + stepNineA: step2Data.value[0].params4, + stepNineB: dayValue.value, + // stepNineC: step1Data.value[18].params, + // stepNineD: step1Data.value[19].params, + // stepTenA: step2Data.value[0].params, + // stepTenB: step2Data.value[1].params, + // stepTenC: step2Data.value[2].params, + // stepTenD: step4Data.value[0].params1, + // stepElevenA: step4Data.value[0].params2, + // stepOneA: step4Data.value[2].params1, + // stepOneB: step4Data.value[2].params2, + // stepOneC: step4Data.value[4].params1, + // stepOneD: step4Data.value[4].params2, + // stepTwoA: step4Data.value[6].params1, + // stepTwoB: step4Data.value[6].params2, + // stepTwoC: step5DataTable2.value[0].params, + // stepThreeA: step5DataTable2.value[1].params, + // stepThreeB: step5DataTable2.value[2].params, + // stepThreeC: step5DataTable2.value[3].params, + // stepThreeD: step5DataTable2.value[4].params, + // stepFourA: step5DataTable3.value[0].params1, + // stepFourB: step5DataTable3.value[0].params2, + // stepFourC: step5DataTable3.value[1].params1, + // stepFourD: step5DataTable3.value[1].params2, + // stepNewOne: step5DataTable3.value[2].params1, + // stepNewTwo: step5DataTable3.value[2].params2, + // stepNewThree: step5DataTable3.value[3].params1, + // stepNewFive: step5DataTable3.value[3].params2, + // stepNewSix: step5DataTable3.value[4].params1, + // stepNewEight: step5DataTable3.value[4].params2, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "留存分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +}; const getData = () => { portraitModel .getData({ @@ -1611,6 +1670,38 @@ const getData = () => { // step5DataTable3.value[3].params2 = res.data.stepNewFive; // step5DataTable3.value[4].params1 = res.data.stepNewSix; // step5DataTable3.value[4].params2 = res.data.stepNewEight; + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + for (let i in step1Data.value.slice(0, step1Data.value.length - 1)) { + if (step1Data.value[i].params != step1RightAnswer.value[i]) { + step1Data.value[i].isError = true; + } else { + step1Data.value[i].isError = false; + } + } + + if (step2Data.value[0].params1 && step2Data.value[0].params1 != "85") { + step2Data.value[0].isError1 = true; + } else { + step2Data.value[0].isError1 = false; + } + if (step2Data.value[0].params2 && step2Data.value[0].params2 != "73") { + step2Data.value[0].isError2 = true; + } else { + step2Data.value[0].isError2 = false; + } + if (step2Data.value[0].params3 && step2Data.value[0].params3 != "27") { + step2Data.value[0].isError3 = true; + } else { + step2Data.value[0].isError3 = false; + } + if (step2Data.value[0].params4 && step2Data.value[0].params4 != "4") { + step2Data.value[0].isError4 = true; + } else { + step2Data.value[0].isError4 = false; + } + } }) .catch((error) => {}); }; @@ -1631,6 +1722,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveSubmitData(); }) .catch((error) => {}); }; diff --git a/src/views/portraitModel/components/rfmAnalysis.vue b/src/views/portraitModel/components/rfmAnalysis.vue index 894adaa..6c07f61 100644 --- a/src/views/portraitModel/components/rfmAnalysis.vue +++ b/src/views/portraitModel/components/rfmAnalysis.vue @@ -496,7 +496,7 @@ const step7AnswerFlag = ref(false); const showAnswerFlag = ref(false); const knowledgeFlag = ref(false); const knowledge = ref( - '

1.什么是RFM分析方法?

RFM是3个指标的缩写,最近一次消费时间间隔(Recency),消费频率(Frequency),消费金额(Monetary)。通过这3个指标对用户分类。

这里举个例子来说明这3个指标是什么意思。

比如我有一家店铺,小明是这家店铺的用户。现在是这个月的30号。

1)最近1次消费时间间隔(R)是指用户最近一次消费距离现在多长时间了。

小明最近1次在这就店铺买东西是这个月25号,上一次消费距离现在过去了5天,所以小明的最近1次消费时间间隔是5天。

2)消费频率(F)是指用户一段时间内消费了多少次。

比如“一段时间”定义是最近30天,发现小明最近30天在店铺消费了3次。

3)消费金额(M)是指用户一段时间内的消费金额。

比如“一段时间”是最近30天,发现小明最近30天总共在店铺消费了1000元。

这3个指标业务不同,定义也不同,要根据业务来灵活定义。

最近一次消费时间间隔(R),上一次消费离得越近,也就是R的值越小,用户价值越高。

消费频率(F),购买频率越高,也就是F的值越大,用户价值越高。

消费金额(M),消费金额越高,也就是M的值越大,用户价值越高。

我们把这3个指标按价值从低到高排序,并把这3个指标作为XYZ坐标轴,就可以把空间分为8部分,这样就可以把用户分为下图的8类。


我们把这个图里对应的RFM这3个值对应的价值是高还是低,对应到下面这张表里就得到了用户分类的规则。


2.RFM分析方法有什么用?

小明的店铺某个月收入大幅下跌。他赶快分析数据,发行原来几个重要的用户被竞争对手挖走了,而这几个用户贡献了店铺80%的收入。

出现这个问题,是因为小明没有对用户分类,对全部用户采取的都是一样的运营决策。怎么对用户分类,识别出有价值的用户呢?

这时候就可以用RFM分析方法把用户分为8类,这样就可以对不同价值用户使用不同的营销决策,把公司有限的资源发挥最大的效果,这就是我们常常听到的精细化运营。比如第1类是重要价值用户,这类用户最近一次消费较近,消费频率也搞,消费金额也搞,要提供vip服务。

我们日常生活中接触到的会员服务就是这方面的经典案例。根据企鹅智库《2017中国会员经济数据报告》数据显示,用户对信用卡、酒店、航空公司的会员体系最满意。


3.如何使用RFM分析方法?

前面我们说RFM分析方法把用户分为8类,具体是如何做到的呢?

第1步)计算R、F、M值

要得到R、F、M这3个指标,我们一般需要数据的3个字段:用户ID或者用户名称、消费时间、消费金额。从这3个字段我们可以计算出R、F、M这3个指标。

以图片中的原始数据为例,现在比如是2020年1月30日,分析最近30天的用户。

小明最近一次消费是(2020.1.26)与现在时间的间隔是4天。在最近30天消费了2次,总共消费金额是5000元。

用这个方法,假如我们这个案例里计算出的是下面表格里的R、F、M值,并在表格里加了3列,用于后面对计算出的R、F、M3个值打分。


第2步)给R、F、M按价值打分

注意这里是按指标的价值打分,不是按指标数值大小打分。最近一次消费时间间隔(R),上一次消费离得越近,也就是R的值越小,用户价值越高。

将R、F、M3个指标分别按价值从小到大分为1-5分。

这个案例里,将最近消费时间间隔(R)距离越近价值越大,大于20天的打1分,10-20天的打2分,5-10天打2分,3-5天打4分,3天以内打5分。


我们把这3个指标分别是如何打分的,整理到下面这个表里。

最近一次消费时间间隔(R),上一次消费离得越近,也就是R的值越小,用户价值越高。

消费频率(F),购买频率越高,也就是F值越大,用户价值越高。

消费金额(M),消费金额越高,也就是M值越大,用户价值越高。

具体实际业务中,如何定义打分的范围,要根据具体的业务来灵活定,没有统一的标准。

根据这个打分规则,我们可以把前面计算出的R、F、M值表格里的R值分类、F值分类、M值分类这3列填上上对应的RFM值的打分值:


第3步)计算价值平均值

分别计算出R值打分、F值打分、M值打分这3列的平均值


第4步)用户分类

在表格里增加3列,分别用于记录R、F、M3个值是高于平均值,还是低于平均值。

如果一行里的R值打分大于平均值,就在R值高低列里记录为“高”,否则记录为“低”。其他F值,M值也这样比较。最终得到了下面表格里的值

然后和用户分类表格里定义的规则进行比较,就可以得出用户属于哪种类


我们总结下前面的内容:如何用RFM对用户分类?

1)使用原始数据计算出R、F、M值

2)给R、F、M按价值打分,比如按价值从低到高分为1-5分

3)计算价值的平均值,如果某个指标的得分比价值的平均值低,标记为低。如果某个指标的得分比价值的平均值高,标记为高

4)用户分类规则表比较,得出用户分类

我们再回过头看前面这个分类图,你就会恍然大悟。在坐标轴的中心,可以理解为某个指标价值的平均值。


用户分类后,如何精细化运营呢?

对用户分类以后要做什么呢?那就是针对每类用户如何制定运营策略,这个具体公司业务不同,方法也不一样。这里举例说明前4类用户。

1)重要价值用户,RFM三个值都很高,要提供vip服务

2)重要发展用户,消费频率低,但是其他两个值很高,就要想办法提高他的消费频率

3) 重要保持用户,最近消费距离现在时间较远,也就是F值低,但是消费频次和消费金额高。这种用户,是一段时间没来的忠实客户。应该主动和他保持联系,提高复购率

4) 重要挽留客户,最近消费时间距离现在较远、消费频率低,但消费金额高。这种用户,即将流失,要主动联系用户,调查清楚哪里出了问题,并想办法挽回。

这样通过RFM分析方法来分析用户,对用户进行精细化运营。不断将用户转化为重要价值用户。


4.使用RFM分析方法需要注意什么?

第1个问题:什么是? 我们学习了,RFM是3个指标的缩写,最近一次消费的时间间隔(R),消费频率(F),消费金额(M)。RFM分析方法可以把用户分为8类。

第2个问题:有什么用?我们学习了,通RFM分析方法,我们可以把用户分为8类,这样就可以对不同用户使用不同的营销策略。比如信用卡的会员服务。

第3个问题:如何用?通过一个案例学会了如何用RFM分析方法对用户分类,了解了RFM背后的原理。现在有很多工具,比如excel,sql,pytho等都可以实现RFM。工作里用到的时候,可以再用搜索引擎去搜对应的实现工具,就可以快速实现了,现在把基本原理掌握了就可以。

现在我们来看第4个问题:有哪些注意事项?

(1)第1个注意事项:R、F、M指标定义,不同业务定义不同。要根据具体业务灵活应用。

这里举个例子。

很多人会有这样的疑问,朋友之间不经常联系,慢慢就淡了。那么,如何管理人脉关系呢?

我们还可以用RFM分析方法来分析你的人脉,针对不同的人,制定不同的联系决策。

最近一次消费时间间隔(R)可以定义为最近一次见面距离现在的时间。

消费频率(F)可以定义为最近1年见面的频率。

消费金额(M)可以定义为最近1年见面深度,比如见面是只打个招呼,还是一起吃个饭,还是畅谈人生。

按照这个定义,我们可以重点关注前3类人脉关系。

按照这个定义,我们可以重点关注前3类人脉关系

1)重要价值关系:对你的生活和工作非常有价值。他几乎是你最亲密的亲戚、朋友、客户。面对这些人,你应该经常联系,彼此帮助,时不时约出来聊聊天。

2)重要发展关系:联系比较多、一起做过点事,但是聊天是有一句没一句的,这种要重点发展关系。比如把你的困惑或者小秘密和他分享,产生情感连接。

3)重要保持的关系:所谓的熟人,也就是打起电话来记得住这个人,而且也大概了解他的背景,可能很长时间都没有见的那种“朋友”。这种要主动联系,利用节假日登门拜访、共同的朋友持续保持沟通。


冯仑讲过一段话:在正常情况下,人一生交往的关系是三个数:10、30和60。这句话是什么意思呢?其实正好对应我们刚才讲的三种人脉关系。

你遇到危难的时候,能借钱的对象不超过10个人。就算是把亲戚、朋友、父母全算上,你到时候能张口借钱的对象不会超过10个人。

再一层就是属于朋友,经常打交道的、做过点事的,大概不超过30个人。这30人还包括前面说的那10个人。

最外一圈是所谓的熟人,也就是打起电话来记得住这个人,而且也大概了解他的背景,可能很长时间都没有见的那种“朋友”,最多也就是60个。这60个人还包括了前面说的30人。

所以,人这一生,其实不需要太多的关系就能应付得了。需要花精力去了解的人,其实很少,不会很多,不会超过60个。只要把这60个人的名单每天都盘好,能够用一生了。

我有个朋友是这方面高手,他把经常要联系的人记下来,每个月翻出来看看,“这个朋友有3个月没联系了,要约出来聊聊天”。

你看,在关系中,如果两个都被动,那只能慢慢断了联系。所以,保持关系的秘密就是需要其中的一方主动。

人脉比你想象的重要,为了保持有效关系,你必须把自己变成一个在这方面主动的人。


(2)第2个注意事项:R、F、M按价值如何确定打分的规则?

一般分为1-5分,也可以根据具体业务灵活来调整。

每个分数的范围是多少?这要根据具体业务来定范围的阈值,这就好比你在开车,车速控制在哪个范围内,可以根据路况灵活把握。

同样的,FRM打分的规则可以与业务部门沟通,进行头脑风暴。或者使用聚类的方法对R、F、M的值进行分类,然后给每个类别打分。


5.总结

我把前面的知识点总结到下面这张图里

' + '

1.什么是RFM分析方法?

18c201b733cb5541c64e36a6cbb28af.png

RFM是3个指标的缩写,最近一次消费时间间隔(Recency),消费频率(Frequency),消费金额(Monetary)。通过这3个指标对用户分类。

这里举个例子来说明这3个指标是什么意思。

比如我有一家店铺,小明是这家店铺的用户。现在是这个月的30号。

1)最近1次消费时间间隔(R)是指用户最近一次消费距离现在多长时间了。

小明最近1次在这就店铺买东西是这个月25号,上一次消费距离现在过去了5天,所以小明的最近1次消费时间间隔是5天。

2)消费频率(F)是指用户一段时间内消费了多少次。

比如“一段时间”定义是最近30天,发现小明最近30天在店铺消费了3次。

3)消费金额(M)是指用户一段时间内的消费金额。

比如“一段时间”是最近30天,发现小明最近30天总共在店铺消费了1000元。

这3个指标业务不同,定义也不同,要根据业务来灵活定义。

最近一次消费时间间隔(R),上一次消费离得越近,也就是R的值越小,用户价值越高。

消费频率(F),购买频率越高,也就是F的值越大,用户价值越高。

消费金额(M),消费金额越高,也就是M的值越大,用户价值越高。

我们把这3个指标按价值从低到高排序,并把这3个指标作为XYZ坐标轴,就可以把空间分为8部分,这样就可以把用户分为下图的8类。


我们把这个图里对应的RFM这3个值对应的价值是高还是低,对应到下面这张表里就得到了用户分类的规则。


2.RFM分析方法有什么用?

小明的店铺某个月收入大幅下跌。他赶快分析数据,发行原来几个重要的用户被竞争对手挖走了,而这几个用户贡献了店铺80%的收入。

出现这个问题,是因为小明没有对用户分类,对全部用户采取的都是一样的运营决策。怎么对用户分类,识别出有价值的用户呢?

这时候就可以用RFM分析方法把用户分为8类,这样就可以对不同价值用户使用不同的营销决策,把公司有限的资源发挥最大的效果,这就是我们常常听到的精细化运营。比如第1类是重要价值用户,这类用户最近一次消费较近,消费频率也搞,消费金额也搞,要提供vip服务。

我们日常生活中接触到的会员服务就是这方面的经典案例。根据企鹅智库《2017中国会员经济数据报告》数据显示,用户对信用卡、酒店、航空公司的会员体系最满意。


3.如何使用RFM分析方法?

前面我们说RFM分析方法把用户分为8类,具体是如何做到的呢?

第1步)计算R、F、M值

要得到R、F、M这3个指标,我们一般需要数据的3个字段:用户ID或者用户名称、消费时间、消费金额。从这3个字段我们可以计算出R、F、M这3个指标。

以图片中的原始数据为例,现在比如是2020年1月30日,分析最近30天的用户。

小明最近一次消费是(2020.1.26)与现在时间的间隔是4天。在最近30天消费了2次,总共消费金额是5000元。

用这个方法,假如我们这个案例里计算出的是下面表格里的R、F、M值,并在表格里加了3列,用于后面对计算出的R、F、M3个值打分。


第2步)给R、F、M按价值打分

注意这里是按指标的价值打分,不是按指标数值大小打分。最近一次消费时间间隔(R),上一次消费离得越近,也就是R的值越小,用户价值越高。

将R、F、M3个指标分别按价值从小到大分为1-5分。

这个案例里,将最近消费时间间隔(R)距离越近价值越大,大于20天的打1分,10-20天的打2分,5-10天打2分,3-5天打4分,3天以内打5分。


我们把这3个指标分别是如何打分的,整理到下面这个表里。

最近一次消费时间间隔(R),上一次消费离得越近,也就是R的值越小,用户价值越高。

消费频率(F),购买频率越高,也就是F值越大,用户价值越高。

消费金额(M),消费金额越高,也就是M值越大,用户价值越高。

具体实际业务中,如何定义打分的范围,要根据具体的业务来灵活定,没有统一的标准。

根据这个打分规则,我们可以把前面计算出的R、F、M值表格里的R值分类、F值分类、M值分类这3列填上上对应的RFM值的打分值:


第3步)计算价值平均值

分别计算出R值打分、F值打分、M值打分这3列的平均值


第4步)用户分类

在表格里增加3列,分别用于记录R、F、M3个值是高于平均值,还是低于平均值。

如果一行里的R值打分大于平均值,就在R值高低列里记录为“高”,否则记录为“低”。其他F值,M值也这样比较。最终得到了下面表格里的值

然后和用户分类表格里定义的规则进行比较,就可以得出用户属于哪种类


我们总结下前面的内容:如何用RFM对用户分类?

1)使用原始数据计算出R、F、M值

2)给R、F、M按价值打分,比如按价值从低到高分为1-5分

3)计算价值的平均值,如果某个指标的得分比价值的平均值低,标记为低。如果某个指标的得分比价值的平均值高,标记为高

4)用户分类规则表比较,得出用户分类

我们再回过头看前面这个分类图,你就会恍然大悟。在坐标轴的中心,可以理解为某个指标价值的平均值。


用户分类后,如何精细化运营呢?

对用户分类以后要做什么呢?那就是针对每类用户如何制定运营策略,这个具体公司业务不同,方法也不一样。这里举例说明前4类用户。

1)重要价值用户,RFM三个值都很高,要提供vip服务

2)重要发展用户,消费频率低,但是其他两个值很高,就要想办法提高他的消费频率

3) 重要保持用户,最近消费距离现在时间较远,也就是F值低,但是消费频次和消费金额高。这种用户,是一段时间没来的忠实客户。应该主动和他保持联系,提高复购率

4) 重要挽留客户,最近消费时间距离现在较远、消费频率低,但消费金额高。这种用户,即将流失,要主动联系用户,调查清楚哪里出了问题,并想办法挽回。

这样通过RFM分析方法来分析用户,对用户进行精细化运营。不断将用户转化为重要价值用户。


4.使用RFM分析方法需要注意什么?

第1个问题:什么是? 我们学习了,RFM是3个指标的缩写,最近一次消费的时间间隔(R),消费频率(F),消费金额(M)。RFM分析方法可以把用户分为8类。

第2个问题:有什么用?我们学习了,通RFM分析方法,我们可以把用户分为8类,这样就可以对不同用户使用不同的营销策略。比如信用卡的会员服务。

第3个问题:如何用?通过一个案例学会了如何用RFM分析方法对用户分类,了解了RFM背后的原理。现在有很多工具,比如excel,sql,pytho等都可以实现RFM。工作里用到的时候,可以再用搜索引擎去搜对应的实现工具,就可以快速实现了,现在把基本原理掌握了就可以。

现在我们来看第4个问题:有哪些注意事项?

(1)第1个注意事项:R、F、M指标定义,不同业务定义不同。要根据具体业务灵活应用。

这里举个例子。

很多人会有这样的疑问,朋友之间不经常联系,慢慢就淡了。那么,如何管理人脉关系呢?

我们还可以用RFM分析方法来分析你的人脉,针对不同的人,制定不同的联系决策。

最近一次消费时间间隔(R)可以定义为最近一次见面距离现在的时间。

消费频率(F)可以定义为最近1年见面的频率。

消费金额(M)可以定义为最近1年见面深度,比如见面是只打个招呼,还是一起吃个饭,还是畅谈人生。

按照这个定义,我们可以重点关注前3类人脉关系。

按照这个定义,我们可以重点关注前3类人脉关系

1)重要价值关系:对你的生活和工作非常有价值。他几乎是你最亲密的亲戚、朋友、客户。面对这些人,你应该经常联系,彼此帮助,时不时约出来聊聊天。

2)重要发展关系:联系比较多、一起做过点事,但是聊天是有一句没一句的,这种要重点发展关系。比如把你的困惑或者小秘密和他分享,产生情感连接。

3)重要保持的关系:所谓的熟人,也就是打起电话来记得住这个人,而且也大概了解他的背景,可能很长时间都没有见的那种“朋友”。这种要主动联系,利用节假日登门拜访、共同的朋友持续保持沟通。


冯仑讲过一段话:在正常情况下,人一生交往的关系是三个数:10、30和60。这句话是什么意思呢?其实正好对应我们刚才讲的三种人脉关系。

你遇到危难的时候,能借钱的对象不超过10个人。就算是把亲戚、朋友、父母全算上,你到时候能张口借钱的对象不会超过10个人。

再一层就是属于朋友,经常打交道的、做过点事的,大概不超过30个人。这30人还包括前面说的那10个人。

最外一圈是所谓的熟人,也就是打起电话来记得住这个人,而且也大概了解他的背景,可能很长时间都没有见的那种“朋友”,最多也就是60个。这60个人还包括了前面说的30人。

所以,人这一生,其实不需要太多的关系就能应付得了。需要花精力去了解的人,其实很少,不会很多,不会超过60个。只要把这60个人的名单每天都盘好,能够用一生了。

我有个朋友是这方面高手,他把经常要联系的人记下来,每个月翻出来看看,“这个朋友有3个月没联系了,要约出来聊聊天”。

你看,在关系中,如果两个都被动,那只能慢慢断了联系。所以,保持关系的秘密就是需要其中的一方主动。

人脉比你想象的重要,为了保持有效关系,你必须把自己变成一个在这方面主动的人。


(2)第2个注意事项:R、F、M按价值如何确定打分的规则?

一般分为1-5分,也可以根据具体业务灵活来调整。

每个分数的范围是多少?这要根据具体业务来定范围的阈值,这就好比你在开车,车速控制在哪个范围内,可以根据路况灵活把握。

同样的,FRM打分的规则可以与业务部门沟通,进行头脑风暴。或者使用聚类的方法对R、F、M的值进行分类,然后给每个类别打分。


5.总结

我把前面的知识点总结到下面这张图里

' ); const fileTable1 = ref([ { @@ -1118,6 +1118,7 @@ const submitData = () => { .then((res) => { proxy.$modal.msgSuccess("提交成功"); showAnswerFlag.value = true; + saveDataSubmit(); }) .catch((error) => {}); }; @@ -1173,6 +1174,59 @@ const saveData = () => { .then((res) => {}) .catch((error) => {}); }; +const saveDataSubmit = () => { + const params = { + stepFiveA: fileTable1.value[0].params, + stepFiveB: fileTable1.value[1].params, + stepFiveC: fileTable1.value[2].params, + stepFiveD: fileTable1.value[3].params, + stepSixA: fileTable1.value[4].params, + stepSixB: fileTable1.value[5].params, + stepSixC: fileTable1.value[6].params, + stepSixD: step2Data.value[0].params1, + stepSevenA: step2Data.value[0].params2, + stepSevenB: step2Data.value[0].params3, + stepSevenC: step2Data.value[1].params1, + stepSevenD: step2Data.value[1].params2, + stepEightA: step2Data.value[1].params3, + stepEightB: step2Data.value[2].params1, + stepEightC: step2Data.value[2].params2, + stepEightD: step2Data.value[2].params3, + stepNineA: step4Data.value[0].params1, + stepNineB: step4Data.value[0].params2, + stepNineC: step4Data.value[0].params3, + stepNineD: step4Data.value[1].params1, + stepTenA: step4Data.value[1].params2, + stepTenB: step4Data.value[1].params3, + stepTenC: step4Data.value[2].params1, + stepTenD: step4Data.value[2].params2, + stepElevenA: step4Data.value[2].params3, + stepOneA: step5Data.value[0].params1, + stepOneB: step5Data.value[0].params2, + stepOneC: step5Data.value[0].params3, + stepOneD: step6Data.value[0].params1, + stepTwoA: step6Data.value[0].params2, + stepTwoB: step6Data.value[0].params3, + stepTwoC: step6Data.value[1].params1, + stepThreeA: step6Data.value[1].params2, + stepThreeB: step6Data.value[1].params3, + stepThreeC: step6Data.value[2].params1, + stepThreeD: step6Data.value[2].params2, + stepFourA: step6Data.value[2].params3, + stepFourB: step7Data.value[0].params, + stepFourC: step7Data.value[1].params, + stepFourD: step7Data.value[2].params, + }; + portraitModel + .saveData({ + userId: JSON.parse(getUserInfo()).userId, + module: "RFM分析", + ...params, + subState: 1, + }) + .then((res) => {}) + .catch((error) => {}); +} const getData = () => { portraitModel .getData({ @@ -1220,6 +1274,179 @@ const getData = () => { step7Data.value[0].params = res.data.stepFourB; step7Data.value[1].params = res.data.stepFourC; step7Data.value[2].params = res.data.stepFourD; + //回显答案 + if (res.data.subState & (res.data.subState == 1)) { + showAnswerFlag.value = true; + for (let i in fileTable1.value) { + if (i == 0) { + if (fileTable1.value[0].params && fileTable1.value[0].params == "纳入") { + fileTable1.value[0].isError = true; + } else { + fileTable1.value[0].isError = false; + } + // console.log(fileTable1.value[0].params); + } else { + if (fileTable1.value[i].params && fileTable1.value[i].params == "剔除") { + fileTable1.value[i].isError = true; + } else { + fileTable1.value[i].isError = false; + } + } + } + if (step2Data.value[0].params1 && step2Data.value[0].params1 !== "4") { + step2Data.value[0].isError1 = true; + } else { + step2Data.value[0].isError1 = false; + } + if (step2Data.value[0].params2 && step2Data.value[0].params2 !== "2") { + step2Data.value[0].isError2 = true; + } else { + step2Data.value[0].isError2 = false; + } + if (step2Data.value[0].params3 && step2Data.value[0].params3 !== "5000") { + step2Data.value[0].isError3 = true; + } else { + step2Data.value[0].isError3 = false; + } + if (step2Data.value[1].params1 && step2Data.value[1].params1 !== "2") { + step2Data.value[1].isError1 = true; + } else { + step2Data.value[1].isError1 = false; + } + if (step2Data.value[1].params2 && step2Data.value[1].params2 !== "3") { + step2Data.value[1].isError2 = true; + } else { + step2Data.value[1].isError2 = false; + } + if (step2Data.value[1].params3 && step2Data.value[1].params3 !== "1000") { + step2Data.value[1].isError3 = true; + } else { + step2Data.value[1].isError3 = false; + } + if (step2Data.value[2].params1 && step2Data.value[2].params1 !== "1") { + step2Data.value[2].isError1 = true; + } else { + step2Data.value[2].isError1 = false; + } + if (step2Data.value[2].params2 && step2Data.value[2].params2 !== "1") { + step2Data.value[2].isError2 = true; + } else { + step2Data.value[2].isError2 = false; + } + if (step2Data.value[2].params3 && step2Data.value[2].params3 !== "500") { + step2Data.value[2].isError3 = true; + } else { + step2Data.value[2].isError3 = false; + } + + if (step4Data.value[0].params1 && step4Data.value[0].params1 !== "4") { + step4Data.value[0].isError1 = true; + } else { + step4Data.value[0].isError1 = false; + } + if (step4Data.value[0].params2 && step4Data.value[0].params2 !== "2") { + step4Data.value[0].isError2 = true; + } else { + step4Data.value[0].isError2 = false; + } + if (step4Data.value[0].params3 && step4Data.value[0].params3 !== "4") { + step4Data.value[0].isError3 = true; + } else { + step4Data.value[0].isError3 = false; + } + if (step4Data.value[1].params1 && step4Data.value[1].params1 !== "5") { + step4Data.value[1].isError1 = true; + } else { + step4Data.value[1].isError1 = false; + } + if (step4Data.value[1].params2 && step4Data.value[1].params2 !== "2") { + step4Data.value[1].isError2 = true; + } else { + step4Data.value[1].isError2 = false; + } + if (step4Data.value[1].params3 && step4Data.value[1].params3 !== "2") { + step4Data.value[1].isError3 = true; + } else { + step4Data.value[1].isError3 = false; + } + if (step4Data.value[2].params1 && step4Data.value[2].params1 !== "5") { + step4Data.value[2].isError1 = true; + } else { + step4Data.value[2].isError1 = false; + } + if (step4Data.value[2].params2 && step4Data.value[2].params2 !== "1") { + step4Data.value[2].isError2 = true; + } else { + step4Data.value[2].isError2 = false; + } + if (step4Data.value[2].params3 && step4Data.value[2].params3 !== "1") { + step4Data.value[2].isError3 = true; + } else { + step4Data.value[2].isError3 = false; + } + + if (step5Data.value[0].params1 && step5Data.value[0].params1 !== "4.67") { + step5Data.value[0].isError1 = true; + } else { + step5Data.value[0].isError1 = false; + } + if (step5Data.value[0].params2 && step5Data.value[0].params2 !== "1.67") { + step5Data.value[0].isError2 = true; + } else { + step5Data.value[0].isError2 = false; + } + if (step5Data.value[0].params3 && step5Data.value[0].params3 !== "2.33") { + step5Data.value[0].isError3 = true; + } else { + step5Data.value[0].isError3 = false; + } + + if (step6Data.value[0].params1 && step6Data.value[0].params1 !== "低") { + step6Data.value[0].isError1 = true; + } else { + step6Data.value[0].isError1 = false; + } + if (step6Data.value[0].params2 && step6Data.value[0].params2 !== "高") { + step6Data.value[0].isError2 = true; + } else { + step6Data.value[0].isError2 = false; + } + if (step6Data.value[0].params3 && step6Data.value[0].params3 !== "高") { + step6Data.value[0].isError3 = true; + } else { + step6Data.value[0].isError3 = false; + } + if (step6Data.value[1].params1 && step6Data.value[1].params1 !== "高") { + step6Data.value[1].isError1 = true; + } else { + step6Data.value[1].isError1 = false; + } + if (step6Data.value[1].params2 && step6Data.value[1].params2 !== "高") { + step6Data.value[1].isError2 = true; + } else { + step6Data.value[1].isError2 = false; + } + if (step6Data.value[1].params3 && step6Data.value[1].params3 !== "高") { + step6Data.value[1].isError3 = true; + } else { + step6Data.value[1].isError3 = false; + } + if (step6Data.value[2].params1 && step6Data.value[2].params1 !== "高") { + step6Data.value[2].isError1 = true; + } else { + step6Data.value[2].isError1 = false; + } + if (step6Data.value[2].params2 && step6Data.value[2].params2 !== "低") { + step6Data.value[2].isError2 = true; + } else { + step6Data.value[2].isError2 = false; + } + if (step6Data.value[2].params3 && step6Data.value[2].params3 !== "低") { + step6Data.value[2].isError3 = true; + } else { + step6Data.value[2].isError3 = false; + } + } }) .catch((error) => {}); }; diff --git a/src/views/portraitModel/components/viscosityAnalysis.vue b/src/views/portraitModel/components/viscosityAnalysis.vue index 0ec6e72..76894a7 100644 --- a/src/views/portraitModel/components/viscosityAnalysis.vue +++ b/src/views/portraitModel/components/viscosityAnalysis.vue @@ -201,7 +201,7 @@ https://blog.csdn.net/kevin1993best/article/details/106329573"