数据分析基础——关于埋点和数据的生成

数据分析一般从ETL开始,数据分析师一般较少关注埋点和数据的生成、存储。我们补个缺,聊一聊数据埋点相关细节。希望看完本文之后你可以更好的跟前端、产品同学battle。

一、埋点类型

从数据生成方式,我们可以分为三类:
1、全自动埋点
2、半自动埋点
3、手动埋点

为了理解这三种埋点类型,我们先了解下技术背景。一般可以把一个页面或者模块的可视化生命周期抽象为:

def init()    # 初始化
def onShow()  # 可见
def onHide()  # 隐藏到后台
def onClose() # 关闭退出

从程序实现角度,埋点的处理可以插入这4个可视化周期之一,这4个周期主要生成曝光、启动等非用户主动操作的指标。而曝光指标埋点上报写在onShow()中,即在页面渲染完上报。有一个关键点,手速太快曝光过多对数据有一定影响,从经验角度,需要卡延迟,比如显示500毫秒以上该模块曝光才上报。

而用户行为产生一般有以下交互场景:

def onHover()       # 获焦点
def onClick()       # 点击
def onLongTouch()   #  长按点击
def onDoubleClick() # 双击

从统计角度,一般只需获焦、点击行为,特别在移动端场景基本没有长按、双击场景,所以用户行为主要贡献点击类指标,通过点击的模块衍生出兴趣相关的指标。

回来讲三种埋点类型:
1、全自动埋点
全自动意味着埋点求全,因为不知道你要什么,所以给你所有。埋点sdk通过代码插桩的方式mix in上述的几个可视化和点击事件。根据控件树,获取控件id和上下文的控件id,进行全量上报。

2、半自动埋点
只对可完全抽象的场景进行自动化埋点,比如主要页面和入口的曝光、点击。由于业务场景一般涉及自定义参数,比如渠道、活动、内容、场景等程序无法自动生成的上报进行自定义埋点开发。

3、手动埋点
即根据产品需求,case by case埋点。

所有的埋点数据最后需要解析回来,也就是数据解读,全自动埋点解放了前端人力,但数据解读比较困难,并且带来的问题是控件id随着前端的重构会变得十分脆弱,ETL逻辑维护困难;而手动埋点又过于死板。相对合理的方式是:半自动埋点。

然而,我们仍然需要优化埋点管理,我们需要抽象出脱离前端模块实现的埋点方案,即前端模块重构、下线不至于所有后续的指标统计推倒重来。下面我们继续聊怎么做到这一点。

可以从4w来阐述,即who、where、when、what

二、who: 我是谁

我是谁包含:用户是谁、模块是谁。
用户是谁,包含:appid、openid、sessionid,即app标识、用户标识、会话临时标识。更进一步,除了用户 标识,关于“用户是谁”可以包含终端信息,比如:终端品牌、型号、硬件id、imei等。

模块是谁,包含:页面、模块两个维度,遵从树状结构设计,需要知道来源是谁、去向是谁。一个页面一般有多个模块,每个模又细分多种元素,所以模块需要统一的标识,前面说到前端控件ID存在重构丢失、重复的问题的,所以每个控件的生成需要从类对象的维度统一抽象,即定义抽象基类:

class base_module
{
     var pre_page     # 上一级页面
     var next_page    # 下一级页面
     var element_id   # 模块id
}

模块ID和模块的上下文是很关键的信息,ID不单单只是一个标记,ID隐含的信息可以包括:
1、归属的页面
2、归属的模块
3、元素标识
从这三个层面看,ID是一个多级结构,是有规则生成的,可以参考以下结构:

    页面.模块.元素

由于数据上报和存储统一utf8存储,可以直接用中文定义模块id,或者换成字母递增:

    首页-热门推荐-运营位
    a1.b1.c1

上面两者的定义是等效的。另外pre_page、next_page直接取a1这一维度的信息。归属的模块提取a1.b1这个维度的信息,a1.b1.c1则可定位到元素。如果模块之间嵌套多个模块,原则上可以继续拆分到4级,即a1.b1.c1.d1,过多层级需要考虑实际的使用场景。

三、where: 在哪里

who从某种意义上解决了部分的where的问题,从产品维度一般where指的是模块的位置信息。大部分的产品为tab页面,即多个页面,页面内容相似,页面内分为多个楼层,每个楼层有多个区块,可以定义以下结构:

class base_module
{
     var tab_idx         # tab页面id或者顺序,可缺省为0
     var pos_x           # 区块位置
     var pos_y           # 楼层位置
}

每个楼层可能出现两行,位置pos_x递增即可。位置信息可用于热力图绘制。

where信息又包含用户或者设备所在的地域信息,即ip或者经纬度信息:

class base_module
{
     var ip              # ip,粗粒度的用户位置
     var longitude       # 经度
     var latitude        # 维度
}

三、when: 什么时候发生

when是时间信息,从数据维度有2种时间:用户侧产生的时间、数据到达服务器的时间。分别记录即可。

四、what: 内容是什么

模块是个容器,可以承载文本、图片、多媒体,模块也可以只是一个简单的占位符。我们统称为内容,那么内容可以包含content-type、content_id。

class base_module
{
     var content-type     # 内容类型
     var content_id       # 内容id或者叫资源id
}

内容是个业务相关的,一般由业务cgi直接下发,也即前端、后台根据协议直接透传上报内容信息,可以是json结构。内容id涉及到内容分类或者更多的结构化分类信息 ,一般通过业务DB关联提取。

综上所述,我们得到,数据埋点的通用结构如下:

class base_module
{
    var app_id
    var open_id
    var session_id

    var device_id
    var device_model
    var device_imei

    var pre_page         # 上一级页面
     var next_page       # 下一级页面
     var element_id      # 模块id

     var tab_idx         # tab页面id或者顺序,可缺省为0
     var pos_x           # 区块位置
     var pos_y           # 楼层位置
     var ip              # ip,粗粒度的用户位置
     var longitude       # 经度
     var latitude        # 维度

     var content-type    # 内容类型
     var content_id      # 内容id或者叫资源id

     var time_stamp      # 前端数据时间
}

五、不同终端的埋点问题

现在的应用大概可以分为五种:h5、pc、安卓、ios、小程序。不同的终端类型获取数据的能力是有限的,比如:imei是移动端特有,h5、小程序一般获取不了硬件id,用户id体系较弱。安卓体系可以获取的信息最全,但安卓定制化较高,不同厂商之间硬件id可能重复。

六、埋点数据质量

统一的接口规范,那么埋点数据才有可测性,也减少不同人员之间的沟通成本。数据的质量从行和列两个维度看,“列”是约定定义集合,基本不动,可以自动化查是否缺漏。“行”即数据是否多报或者少报,这块是需要一个实时日志系统进行辅助验证的。该系统可根据openid白名单,实时反映数据上报情况即可。

七、统一指标的设计

埋点数据最终落地到数据集群,形成ods层,后续的指标计算基于该层进行二次封装。要实现统一指标计算,上述的埋点数据需要进一步抽象,可以抽象为:时间、维度、度量。

时间:所有数据的计算是一定带有时间的,比如:分钟、天、周、月
维度:即分类,可以有渠道分类、区块分类、用户群分类
度量:大抵可以分为计数度量、求和度量、人数度量

如果时间明确、维度一致、度量固定,是可以实现一个统一的指标计算系统的。当应用方需要使用到哪一方面的数据,直接提取即可。

数据分析基础——关于埋点和数据的生成