绩效增强:转换漏斗分析
#编程 #sql #database #bigdata

问题描述

转换漏斗分析是一个集体内的时间序列操作,它使许多情况受益,而电子商务购买的转换分析就是其中之一。用户在电子商务网站登录后,执行诸如页面视图,搜索,添加到购物车,订购,付款等的操作。这些事件是在时间上订购的,并且每个用户都将在每个用户之后丢失。转换漏斗分析通常首先在每个事件之后计数用户数,然后进行复杂的计算,例如基于前者的转换率。

假设表T的字段包括ID(事件编号),GID(分组字段),ETIME(时间字段),EventType(事件类型),并且它们每个分别对应于用户事件号,电子商务采购分析的用户编号,操作时间,事件类型。涉及的总数据卷表t通常很大,需要外部存储,而每个组(用户)的数据不大,可以保存在内存中。

在这里说明了转换漏斗解决方案的3个步骤,可以通过类比来推导其他步骤。以Oracle为例,SQL如下:

with e1 as (
       select gid,1 as step1,min(etime) as t1
       from T
       where etime>= to_date('2021-01-10', 'yyyy-MM-dd') and etime<to_date('2021-01-25', 'yyyy-MM-dd') and eventtype='eventtype1' and …
       group by 1
),
e2 as (
      select gid,1 as step2,min(e1.t1) as t1,min(e2.etime) as t2
      from T as e2
      inner join e1 on e2.gid = e1.gid
      where e2.etime>= to_date('2021-01-10', 'yyyy-MM-dd') and e2.etime<to_date('2021-01-25', 'yyyy-MM-dd')
and e2.etime > t1 and e2.etime < t1 + 7 and eventtype='eventtype2' and …
      group by 1
),
e3 as (
      select gid,1 as step3,min(e2.t1) as t1,min(e3.etime) as t3
      from T as e3
      inner join e2 on e3.gid = e2.gid
      where e3.etime>= to_date('2021-01-10', 'yyyy-MM-dd') and e3.etime<to_date('2021-01-25', 'yyyy-MM-dd') and e3.etime > t2     and e3.etime < t1 + 7 and eventtype='eventtype3' and …
      group by 1
)
select
  sum(step1) as step1,
  sum(step2) as step2,
  sum(step3) as step3
from
  e1
  left join e2 on e1.gid = e2.gid
  left join e3 on e2.gid = e3.gid

sub-Query E1首先过滤其事件类型为EventType1在指定的时间表表中的数据。 …………意味着有可能满足其他条件的可能性,而E2和E3子也需要满足相同的条件。然后将数据按GID分组,每个组的最小eTime被检索为转换漏斗的第一步的时间T1,而新场step1的值则定义为1。

子查询E2使用表T(也称为E2)与E1结合,并且联接场是GID。它过滤了其E2的数据。在T1之后不到7天,并且在指定期间内事件类型为EventType2。这里的7天称为漏斗窗口期,也可以是给定参数。然后数据按GID组组,最小的E2。每个组的时段被检索为转换漏斗的第二步的时间T2,而新字段step2的值则定义为1。

sub-Query E3使用T表(也称为E3)与E2结合,并且联接场是GID。它过滤了E3的数据大于T2,并且在T1(漏斗窗口周期)之后不到7天,并且事件类型在指定的时期内为EventType3。然后将数据按GID分组,最小的E3。每个组的时段被检索为转换漏斗的第三步的时间T3,而新字段step3的值则定义为1。

终于,数据库使用E1与E2和E3一起左JOIN,JOIN字段是GID,汇总step1,step2和step3以获取总和。

这是漏斗转换分析的3个步骤。如果您想完成更多漏斗转换分析的步骤,请参阅E3以编写E4,E5等的子查询,并且需要使用相应的JOIN和RECTREGATION的主要主要守则添加主要查询。

一般而言,按GID进行特定时期的表T的结果集往往很大。执行大组需要在外部存储上进行缓冲,这会导致计算性能不良。

解决方案

1。预分类和基于订单的算法
在数据准备过程中,我们必须根据分组字段排序原始数据。执行转换漏斗分析时,算法会遍历满足时间段,事件类型等条件的有序数据,依次将每个组的数据读取依次。具体而言,步骤1:按时间顺序对当前分组数据进行分组;步骤2:它在当前组中找到了第一个事件类型的第一个记录,将时间记录为T1,然后将T1分配给当前分组结果集的第一个成员。如果找不到T1,则当前组将跳过;步骤3:在当前组的第二个事件类型的记录中,它找到了第一个记录,其时间是在T1之后和T1+7之前(漏斗窗口周期),并将其时间T2分配给结果的第二个成员设置(如果找不到T2,则分配为null);步骤4:在当前组的第三个事件类型的记录中,它找到了第一个记录,其时间是在T2之后和T2+7之前,并将其时间T3分配给结果集的第三个成员(如果T2为null或找不到T3,然后分配为null);步骤5:它汇总了每组结果集的三个成员以获得最终结果。此方法仅遍历数据一次,并避免执行以很大的结果进行分组,从而大大提高了性能。

我们还可以事先通过分组字段和时间字段进行对原始数据进行分类,并避免每个组的求解,从而降低了性能,但显然简化了代码。

实际上,许多组内计算的分组字段是确定的(例如用户ID和帐号),而不是随机选择。通过这些某些字段对数据进行分类可以帮助许多组内时间序列计算的情况。其他组内的时间序列计算仅在特定算法上有所不同,该算法将自行引入。

预分类很慢,却是一次性的,只有一种没有冗余数据的存储。

sql,基于无序集,无法确保每个组的数据被有序存储,因此,它不能直接使用基于订单的算法。

2。新的数据
新数据的数据并不总是由分组字段订购,因此不应简单地将它们附加到已经订购的数据的末尾。直接对现有数据和新数据进行全面分类将非常耗时。

相反,我们可以先对新添加的数据进行排序,然后再与已经订购的数据一起执行基于低成本的合并算法以生成新的有序数据表。这样,整体复杂性等于读取和编写整个数据,从而避免了普通的全面分类中频繁的临时外部缓冲,从而大大提高了性能。

此外,我们可以保留一个小规模的有序表(以下称为补丁数据),该表将与新添加的数据合并,同时使旧有序数据保持不变。补丁数据将与旧的有序数据合并,直到一段时间后它们积累到合适的尺寸。当在组中执行时间序列计算时,该算法分别从旧的有序数据和补丁数据读取,将它们合并,然后计算。通过这种方式,与处理一个有序数据表相比,性能会慢一些,但是数据顺序仍然可以使计算更快。

在应添加数据时,将补丁数据与旧排序数据合并的时间与周期有关。如果每天添加新数据,我们可以每月进行一次合并。一个月前,一个月的补丁数据存储数据最多包含所有数据。也就是说,补丁数据可能比旧有序数据要小得多,因此每天合并的数据相对较小,并且附加数据的附加数据很快。当我们每月仅进行整个DATA订单合并一次时,相当可接受的是,需要更长的时间才能完成。

代码示例

  1. 当数据通过分组字段排序并通过时间字段未排序时,计算转换漏斗。
    A   B
1   =["eventtype1","eventtype2","eventtype3"]   =file("T.ctx").open()
2   =B1.cursor(gid,etime,eventtype;etime>=date("2021-01-10") && etime<date("2021-01-25") && A1.contain(eventtype) && …)
3   =A2.group(gid).(~.sort(etime))  =A3.new(~.select@1(eventtype==A1(1)):first,~:all).select(first)
4   =B3.(A1.(t=if(#==1,t1=first.etime,if(t,all.select@1(eventtype==A1.~ && etime>t && etime<t1+7).etime, null))))
5   =A4.groups(;count(~(1)):STEP1,count(~(2)):STEP2,count(~(3)):STEP3)

a1:定义三种事件类型,也可以给出参数。
B1:打开复合表T.Ctx。
A2:创建一个光标来过滤满足时间段,事件类型等条件的数据。这些条件可以给出参数。
A3:定义分组操作,然后按Etime对每个组进行排序。
B3:创建一个新的光标,将每个组中的第一个事件类型的第一个数据命名为第一个数据,将原始组命名为ALL。然后定义过滤以跳过其第一个字段为null的数据。
A4:定义B3的计算。根据A1循环遍历每个记录。第一个循环分配了第一个事件类型的最早时间的第一个时间,即变量T1,t和结果共同设置的第一个成员。第二个循环找到了第一个记录,其事件类型是All的第二种事件类型,Etime大于T,并且小于T1+7。它的ETIME被分配给T,并且结果集的第二个成员(如果找不到t,则分配将为null)。第三个循环找到了第一个记录,其EventType是ALL的第三个事件类型,而ETIME大于T,如果T不为NULL,则ETIME大于T,而小于T1+7。它的ETIME被分配给T,并且结果集的第三个成员(如果T为null或找不到,则分配为null)。请注意,这里的7天(漏斗窗口)也可以给出参数。
A5:执行先前定义的计算并汇总每个组中每个结果的三个成员,以返回一个小结果集并计数数字。

这仍然是转换漏斗分析的3个步骤。为了完成更多步骤,您可以在A1的事件类型序列中添加以下步骤的事件类型,例如[“ EventType1”,“ EventType2”,“ EventType3”,“ EventType3”,“ EventType4”,“ EventType5”,“ EventType5”â!]。如果事件类型序列是给定参数,则代码可以保持不变。与添加子查询和修改SQL中的主要查询相比,此方法要简单得多。

  1. 当分组字段和时间字段排序数据时,计算转换漏斗。

,如果提前分组字段和时间字段订购数据,则可以在A3中省略(〜。sort(etime))的上述代码。

3。数据是预分类的,并存储了有序结果集。

    A
1   =file("T-original.ctx")
2   =A1.open().cursor().sortx(gid,etime).new(gid,etime,…)
3   =file("T.ctx").create(#gid,#etime,…)
4   =A3.append@i(A2)

a1:定义原始的复合表T-Original.ctx。
A2:打开复合表T-Original.ctx,基于IT创建一个光标,按Field Gid和Field Etime对光标进行排序,其中场GID和Field Etime处于领先位置。如果仅通过GID对光标进行排序,则可以将sortx写为sortx(gid)。
A3:创建一个带有磅标牌#在字段名称之前的新复合表T.CTX,这意味着该复合表由Field Gid和Field Etime排序。如果表仅被GID排序。
A4:将有序数据写入复合表T.Ctx。

  1. 附加新数据 假设表T的每日新添加数据存储在t_new.btx中,并且具有与T.CTX相同顺序的字段名称。
    A   B
1   =file("T.ctx").open()   
2   =file("T_new.btx").cursor@b().sortx(gid,etime)
3   if (day(now())==1)  >A1.reset()
4   >A1.append@a(A2)    

a1:打开复合表T.CTX。
A2:根据t_new.btx定义光标并对其进行排序。由于新数据的每日卷很小,因此分类是一种快速且内存的操作,尽管使用了sortx函数。如果仅通过GID订购光标,则应删除sortx中的eTime。
A3:确定当前日期是否为第1天:如果没有,请执行A4并使用@a仅与补丁数据合并记录;如果是这样,请执行B3并使用RESET函数将现有的有序数据与补丁数据合并以生成新的有序数据表。

SPL源代码:https://github.com/SPLWare/esProc