本文介绍路径分析函数的基础概念和函数的基本用法。
路径分析函数适用于选定一段时间范围,分析此时间范围内用户行为路径,在前端展示效果大致如下:
路径分析计算逻辑简介:
pathSplit
生成每个用户所有的路径组合成的数组。array join
**将单个用户单行包含所有路径的二维数组展开成多行单个路径。pathCount
统计所有用户的路径转化情况。使用 pathSplit
生成每个用户所有的路径组合成的数组。
pathSplit(session_time, level)(timestamp, event, prop)
输入参数:
session_time
:路径分析的时间窗口,单位和timestamp
保持一致,下例中为 600 秒,也即只计算首个event 发生后 10 分钟内形成的路径。level
:路径深度,超过 x 个节点截断路径。timestamp
:**时间戳 **所在列,需要先转换成时间戳,再转换成 Uint64 格式。event
:事件列,仅支持数值类型,需要通过 multiIf
显式指定。如下例中的:multiIf(EventName = 'E0001', 1, EventName = 'E0002', 2, EventName = 'E0003', 3, 0) AS e
。prop
:属性列,没有时传 ''
占位。输出参数(中间结果):
返回二维数组:[(e1, p1), (e2, p2), (e1, p2)]
event
列中 multiif
定义的事件编号。prop
。使用 **arrayjoin
**将单个用户单行包含所有路径的二维数组展开成多行单个路径,然后再使用 pathCount
统计所有用户的路径转化情况。示例如下:
pathCount(y, x)(path, cnt1, cnt2)
SELECT pathCount(3, 3)(path, 1, cnt) FROM ( SELECT path AS path, count() AS cnt FROM ( SELECT arrayJoin(pathSplit(600, 3)(toUInt64(toUnixTimestamp(LOG.EventTime)), multiIf(EventName = 'E0001', 1, EventName = 'E0002', 2, EventName = 'E0004', 3, 0) AS e, '')) AS path FROM LOG WHERE (EventTime >= '2022-06-01 00:00:00') AND (EventTime < '2022-06-02 00:00:00') AND (EventName IN ('E0001', 'E0002', 'E0004')) AND (Visitor = 964049) GROUP BY Visitor ) GROUP BY path )
pathCount 返回界面展示需要的数据,x 表示横向的路径深度(如下图为 4 列),前端展示需要的数据是每个浅蓝色方块代表的 node 和浅蓝色方块之间的边 edge,二者均由 pathCount
输出。
y
:纵向单层展示深度,如上图中黄色圈的部分,即 y = 5;x
:横向路径深度,超过 x 个节点截断路径,如上图中 x = 4;path
:pathSplit
的返回 path 列(需通过 arrayJoin 展开);cnt1
:路径权重 1,通常直接传 1 就行,用于计算路径去重后的次数;cnt2
:路径权重 2,通常传pathSplit
子查询里的 count()
求出的 cnt ,用来求路径不去重的总次数。返回格式:
node_index
:格式为:(event, prop)
,事件节点展示。
event
为事件的下标编号,若 之前 pathSplit
时 event
填写 multiIf(EventName = 'E0001', 1, EventName = 'E0002', 2, EventName = 'E0004', 3, 0)
,则此时返回 1 对应 'E0001' ,2对应 'E0002',3对应 'E0004';prop
为 pathSplit 传入的 prop。nodes
:二维数组。表示每个节点在各层级(level,下标从 0 开始)出现的去重次数、次数,格式为:(level, node_index, cnt1, cnt2)
。
level
:当前节点所处的层级 ;node_index
:事件的 index,参考 node_index 数组中和事件的映射关系;cnt1
:节点访问的去重次数;cnt2
:节点访问的次数。edge
:二维数组,每条边在各层级出现的去重次数、次数,格式为:(level, from_node_index, to_node_index, cnt1, cnt2)
。
level
:层级,即输入的 x;from_node_index
:来源节点,参考 node_index
数组中和事件的映射关系;to_node_index
:目标节点,参考 node_index
数组中和事件的映射关系;to_node_index
用 65535 标记 from_node_index
已经是终点的情况,即用户进行到这个事件之后流失的情况;cnt1
:节点访问的去重次数;cnt2
:节点访问的次数。如下示例用表是由用户行为事实表 Log 和用户维度表 User 组成常用的星型模型,后文将基于此表演示留存分析函数的具体用法。
CREATE TABLE -- 事件日志表 default.LOG ( `Visitor` UInt32, `EventName` String, `EventTime` DateTime, `Province` String, `City` String ) ENGINE = CnchMergeTree ORDER BY Visitor; CREATE TABLE -- 用户表 default.User ( `Visitor` UInt32, `Fvisit_Time` DateTime, `Fvisit_Province` String, `Fvisit_City` String ) ENGINE = CnchMergeTree ORDER BY Visitor;
该示例指定一个用户 Visitor = 964049 来说明。
该示例为 2022/6/1-2022/6/2 中,发生 'E0001', 'E0002', 'E0004' 事件的路径数据,展示发生事件后 10 分钟内的页面信息,展示的页面路径深度为 3 层。
首先通过 pathSplit
计算中间结果:
SELECT Visitor,pathSplit(600, 3) (toUInt64(toUnixTimestamp(LOG.EventTime)), multiIf(EventName = 'E0001', 1, EventName = 'E0002', 2, EventName = 'E0004', 3, 0) AS e, '') AS path FROM LOG WHERE (EventTime >= '2022-06-01 00:00:00') AND (EventTime < '2022-06-02 00:00:00') AND (EventName IN ('E0001', 'E0002', 'E0004')) and Visitor = 964049 GROUP BY Visitor --返回: ┌─Visitor─┬─path────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 964049 │ [[(1,''),(2,''),(3,'')],[(1,'')],[(1,''),(2,''),(3,'')],[(1,''),(2,''),(2,'')],[(1,'')],[(1,'')],[(1,'')],[(1,''),(3,''),(3,'')],[(1,''),(2,''),(3,'')],[(1,''),(1,''),(3,'')],[(1,'')],[(1,''),(2,''),(3,'')],[(1,'')],[(1,''),(2,''),(2,'')]] │ └─────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
再通过 arrayJoin 把单行的 path 返回的二维数组表示的多条 path 展开成多行单条 path,计算每一条 path 出现的次数。
SELECT path AS path, count() AS cnt FROM (SELECT arrayJoin(pathSplit(600, 3)(toUInt64(toUnixTimestamp(LOG.EventTime)), multiIf(EventName = 'E0001', 1, EventName = 'E0002', 2, EventName = 'E0004', 3, 0) AS e, '')) AS path FROM LOG WHERE (EventTime >= '2022-06-01 00:00:00') AND (EventTime < '2022-06-02 00:00:00') AND (EventName IN ('E0001', 'E0002', 'E0004')) and Visitor = 964049 GROUP BY Visitor) GROUP BY path --返回 ┌─path───────────────────┬─cnt─┐ │ [(1,'')] │ 6 │ │ [(1,''),(1,''),(3,'')] │ 1 │ │ [(1,''),(2,''),(3,'')] │ 4 │ │ [(1,''),(2,''),(2,'')] │ 2 │ │ [(1,''),(3,''),(3,'')] │ 1 │ └────────────────────────┴─────┘
再通过 pathCount
计算出最终的 3*3 结果(路径深度为 3,同时每层至多展示的节点为 3 个):
SELECT pathCount(3, 3) (path, 1, cnt) FROM (SELECT path AS path, count() AS cnt FROM (SELECT arrayJoin(pathSplit(600, 3)(toUInt64(toUnixTimestamp(LOG.EventTime)), multiIf(EventName = 'E0001', 1, EventName = 'E0002', 2, EventName = 'E0004', 3, 0) AS e, '')) AS path FROM LOG WHERE (EventTime >= '2022-06-01 00:00:00') AND (EventTime < '2022-06-02 00:00:00') AND (EventName IN ('E0001', 'E0002', 'E0004')) and Visitor = 964049 GROUP BY Visitor) GROUP BY path ) --返回 ┌─pathCount(5, 3)(path, 1, cnt)─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ ([(1,''),(3,''),(2,'')], [[0,0,5,14],[1,0,1,1],[1,1,1,1],[1,2,2,6],[2,1,3,6],[2,2,1,2]], [[0,0,0,1,1],[0,0,1,1,1],[0,0,65535,1,6],[0,0,2,2,6],[1,2,2,1,2],[1,1,1,1,1],[1,0,1,1,1],[1,2,1,1,4]]) │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
结果为:
node_index
:[(1,''),(3,''),(2,'')],表示事件编号映射为 0->1(E0001),1->3(E0004),2->2(E0002)
nodes
:[[0,0,5,14],[1,0,1,1],[1,1,1,1],[1,2,2,6],[2,1,3,6],[2,2,1,2]]
edge:[0,0,0,1,1],[0,0,1,1,1],[0,0,65535,1,6],[0,0,2,2,6],[1,2,2,1,2],[1,1,1,1,1],[1,0,1,1,1],[1,2,1,1,4]
可以将结果信息补充为页面路径分析,如