1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        基于ClickHouse的用戶行為分析實(shí)踐

        共 3213字,需瀏覽 7分鐘

         ·

        2020-08-26 23:02

        點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)

        回復(fù)”資源“獲取更多資源

        大數(shù)據(jù)技術(shù)與架構(gòu)
        點(diǎn)擊右側(cè)關(guān)注,大數(shù)據(jù)開發(fā)領(lǐng)域最強(qiáng)公眾號(hào)!

        暴走大數(shù)據(jù)
        點(diǎn)擊右側(cè)關(guān)注,暴走大數(shù)據(jù)!



        前言

        ClickHouse為用戶提供了豐富的多參聚合函數(shù)(parametric aggregate function)和基于數(shù)組+Lambda表達(dá)式的高階函數(shù)(higher-order function),將它們靈活使用可以達(dá)到魔法般的效果。在我們的體系中,ClickHouse定位點(diǎn)擊流數(shù)倉,所以下面舉幾個(gè)用它來做用戶行為(路徑)分析的實(shí)戰(zhàn)例子,包括:

        • 路徑匹配

        • 智能路徑檢測

        • 有序漏斗轉(zhuǎn)化

        • 用戶留存

        • Session統(tǒng)計(jì)


        路徑匹配

        CK默認(rèn)提供了sequenceMatch函數(shù)檢查是否有事件鏈滿足輸入的模式,sequenceCount函數(shù)則統(tǒng)計(jì)滿足輸入模式的事件鏈的數(shù)量。示例:

        SELECT 
        site_id,
        sequenceMatch('(?1)(?t<=15)(?2).*(?3)')(
        ts_date_time,
        event_type = 'shtLogon',
        event_type = 'shtKkclick' AND column_type = 'homePage',
        event_type = 'shtAddCart'
        ) AS is_match
        FROM ods.analytics_access_log_all
        WHERE ts_date >= '2020-07-01'
        AND site_id IN (10266,10022,10339,10030)
        GROUP BY site_id;

        ┌─site_id─┬─is_match─┐
        100301
        103391
        102661
        100221
        └─────────┴──────────┘
        SELECT 
        site_id,
        sequenceCount('(?1)(?t<=15)(?2).*(?3)')(
        ts_date_time,
        event_type = 'shtLogon',
        event_type = 'shtKkclick' AND column_type = 'homePage',
        event_type = 'shtAddCart'
        ) AS seq_count
        FROM ods.analytics_access_log_all
        WHERE ts_date >= '2020-07-01'
        AND site_id IN (10266,10022,10339,10030)
        GROUP BY site_id;

        ┌─site_id─┬─seq_count─┐
        1003033611
        1033914045
        1026674542
        1002231534
        └─────────┴───────────┘

        這兩個(gè)函數(shù)都需要指定模式串、時(shí)間列和期望的事件序列(最多可指定32個(gè)事件)。模式串的語法有以下三種:

        • (?N):表示時(shí)間序列中的第N個(gè)事件,從1開始。例如上述SQL中,(?2)即表示event_type = 'shtKkclick' AND column_type = 'homePage'

        • (?t op secs):插入兩個(gè)事件之間,表示它們發(fā)生時(shí)需要滿足的時(shí)間條件(單位為秒)。例如上述SQL中,(?1)(?t<=15)(?2)即表示事件1和2發(fā)生的時(shí)間間隔在15秒以內(nèi)。

        • .*:表示任意的非指定事件。

        智能路徑檢測

        CK內(nèi)置的sequenceMatch和sequenceCount函數(shù)只能滿足部分需求,現(xiàn)有一個(gè)更復(fù)雜的需求:

        給定期望的路徑終點(diǎn)、途經(jīng)點(diǎn)和最大事件時(shí)間間隔,查詢出符合條件的路徑詳情及符合路徑的用戶數(shù)(按用戶數(shù)降序排列)。

        目前并沒有現(xiàn)成的函數(shù)可以直接出結(jié)果,但是我們可以曲線救國,用數(shù)組和高階函數(shù)的組合間接實(shí)現(xiàn)。完整SQL語句如下,略長:

        SELECT
        result_chain,
        uniqCombined(user_id) AS user_count
        FROM (
        WITH
        toUInt32(maxIf(ts_date_time, event_type = 'shtOrderDone')) AS end_event_maxt,
        arrayCompact(arraySort(
        x -> x.1,
        arrayFilter(
        x -> x.1 <= end_event_maxt,
        groupArray((toUInt32(ts_date_time), (event_type, column_type)))
        )
        )) AS sorted_events,
        arrayEnumerate(sorted_events) AS event_idxs,
        arrayFilter(
        (x, y, z) -> z.1 <= end_event_maxt AND (z.2.1 = 'shtOrderDone' OR y > 600),
        event_idxs,
        arrayDifference(sorted_events.1),
        sorted_events
        ) AS gap_idxs,
        arrayMap(x -> x + 1, gap_idxs) AS gap_idxs_,
        arrayMap(x -> if(has(gap_idxs_, x), 1, 0), event_idxs) AS gap_masks,
        arraySplit((x, y) -> y, sorted_events, gap_masks) AS split_events
        SELECT
        user_id,
        arrayJoin(split_events) AS event_chain_,
        arrayCompact(event_chain_.2) AS event_chain,
        hasAll(event_chain, [('shtKkClick', 'homePage')]) AS has_midway_hit,
        arrayStringConcat(arrayMap(
        x -> concat(x.1, '#', x.2),
        event_chain
        ), ' -> ') AS result_chain
        FROM (
        SELECT ts_date,ts_date_time,event_type,column_type,user_id
        FROM ods.analytics_access_log_all
        WHERE ts_date >= '2020-06-30' AND ts_date <= '2020-07-02'
        AND site_id IN (10266,10022,10339,10030)
        )
        GROUP BY user_id
        HAVING length(event_chain) > 1
        )
        WHERE event_chain[length(event_chain)].1 = 'shtOrderDone'
        AND has_midway_hit = 1
        GROUP BY result_chain
        ORDER BY user_count DESC LIMIT 20;

        簡述思路:

        1. 將用戶的行為用groupArray函數(shù)整理成<時(shí)間, <事件名, 頁面名>>的元組,并用arraySort函數(shù)按時(shí)間升序排序;

        2. 利用arrayEnumerate函數(shù)獲取原始行為鏈的下標(biāo)數(shù)組;

        3. 利用arrayFilter和arrayDifference函數(shù),過濾出原始行為鏈中的分界點(diǎn)下標(biāo)。分界點(diǎn)的條件是路徑終點(diǎn)或者時(shí)間差大于最大間隔;

        4. 利用arrayMap和has函數(shù)獲取下標(biāo)數(shù)組的掩碼(由0和1組成的序列),用于最終切分,1表示分界點(diǎn);

        5. 調(diào)用arraySplit函數(shù)將原始行為鏈按分界點(diǎn)切分成單次訪問的行為鏈。注意該函數(shù)會(huì)將分界點(diǎn)作為新鏈的起始點(diǎn),所以前面要將分界點(diǎn)的下標(biāo)加1;

        6. 調(diào)用arrayJoin和arrayCompact函數(shù)將事件鏈的數(shù)組打平成多行單列,并去除相鄰重復(fù)項(xiàng)。

        7. 調(diào)用hasAll函數(shù)確定是否全部存在指定的途經(jīng)點(diǎn)。如果要求有任意一個(gè)途經(jīng)點(diǎn)存在即可,就換用hasAny函數(shù)。當(dāng)然,也可以修改WHERE謂詞來排除指定的途經(jīng)點(diǎn)。

        8. 將最終結(jié)果整理成可讀的字符串,按行為鏈統(tǒng)計(jì)用戶基數(shù),完成。


        有序漏斗轉(zhuǎn)化

        CK提供了windowFunnel函數(shù)實(shí)現(xiàn)漏斗,以指定時(shí)長(單位為秒)滑動(dòng)窗口按序匹配事件鏈,并返回在窗口內(nèi)轉(zhuǎn)化到的步數(shù)。如有多種匹配,以步數(shù)最大(轉(zhuǎn)換最深)的為準(zhǔn)。

        通過對(duì)該步數(shù)進(jìn)行統(tǒng)計(jì),即可得到漏斗中每步的轉(zhuǎn)化率。SQL語句如下,查詢結(jié)果是敏感數(shù)據(jù),不再貼出來了。

        SELECT 
        level,user_count,conv_rate_percent
        FROM (
        SELECT
        level,
        uniqCombined(user_id) AS user_count,
        neighbor(user_count, -1) AS prev_user_count,
        if (prev_user_count = 0, -1, round(user_count / prev_user_count * 100, 3)) AS conv_rate_percent
        FROM (
        SELECT
        user_id,
        windowFunnel(900)(
        ts_date_time,
        event_type = 'shtLogon',
        event_type = 'shtKkClick' AND column_type = 'homePage',
        event_type = 'shtOpenGoodsDetail',
        event_type = 'shtAddCart',
        event_type = 'shtOrderDone'
        ) AS level
        FROM (
        SELECT ts_date,ts_date_time,event_type,column_type,user_id
        FROM ods.analytics_access_log_all
        WHERE ts_date >= '2020-06-30' AND ts_date <= '2020-07-02'
        AND site_id IN (10266,10022,10339,10030)
        )
        GROUP BY user_id
        )
        WHERE level > 0
        GROUP BY level
        ORDER BY level ASC
        );

        如果想要更準(zhǔn)確一些,實(shí)現(xiàn)漏斗步驟之間的字段關(guān)聯(lián)(如商品詳情→加入購物車→下單三步中的商品ID關(guān)聯(lián))怎么辦呢?可以利用https://github.com/housepower/olap2018項(xiàng)目中提出的xFunnel函數(shù)。它是windowFunnel函數(shù)的鼻祖,不過需要修改ClickHouse源碼并重新編譯之,今后有時(shí)間的話會(huì)簡單寫一下過程。

        用戶留存

        retention函數(shù)可以方便地計(jì)算留存情況。該函數(shù)接受多個(gè)條件,以第一個(gè)條件的結(jié)果為基準(zhǔn),觀察后面的各個(gè)條件是否也滿足,若滿足則置1,不滿足則置0,最終返回0和1的數(shù)組。通過統(tǒng)計(jì)1的數(shù)量,即可計(jì)算出留存率。

        下面的SQL語句計(jì)算次日重復(fù)下單率與七日重復(fù)下單率(語義與留存相同)。

        SELECT
        sum(ret[1]) AS original,
        sum(ret[2]) AS next_day_ret,
        round(next_day_ret / original * 100, 3) AS next_day_ratio,
        sum(ret[3]) AS seven_day_ret,
        round(seven_day_ret / original * 100, 3) AS seven_day_ratio
        FROM (
        WITH toDate('2020-06-24') AS first_date
        SELECT
        user_id,
        retention(
        ts_date = first_date,
        ts_date = first_date + INTERVAL 1 DAY,
        ts_date = first_date + INTERVAL 7 DAY
        ) AS ret
        FROM ods.ms_order_done_all
        WHERE ts_date >= first_date AND ts_date <= first_date + INTERVAL 7 DAY
        GROUP BY user_id
        );

        Session統(tǒng)計(jì)

        Session,即"會(huì)話",是指在指定的時(shí)間段內(nèi)在網(wǎng)站/H5/小程序/APP上發(fā)生的一系列用戶行為的集合。例如,一次會(huì)話可以包含多個(gè)頁面瀏覽、交互事件等。Session是具備時(shí)間屬性的,根據(jù)不同的切割規(guī)則,可以生成不同長度的Session。

        可見,Session統(tǒng)計(jì)與上述智能路徑檢測的場景有相似之處,都需要尋找用戶行為鏈的邊界并進(jìn)行切割。以下SQL語句以30分鐘為超時(shí)時(shí)間,按天統(tǒng)計(jì)所有用戶的Session總數(shù)(跨天的Session也會(huì)被切割)。

        SELECT 
        ts_date,
        sum(length(session_gaps)) AS session_cnt
        FROM (
        WITH
        arraySort(groupArray(toUInt32(ts_date_time))) AS times,
        arrayDifference(times) AS times_diff
        SELECT
        ts_date,
        arrayFilter(x -> x > 1800, times_diff) AS session_gaps
        FROM ods.analytics_access_log_all
        WHERE ts_date >= '2020-06-30'
        GROUP BY ts_date,user_id
        )
        GROUP BY ts_date;

        版權(quán)聲明:

        本文為大數(shù)據(jù)技術(shù)與架構(gòu)整理,原作者獨(dú)家授權(quán)。未經(jīng)原作者允許轉(zhuǎn)載追究侵權(quán)責(zé)任。
        編輯|胡曉
        微信公眾號(hào)|import_bigdata


        歡迎點(diǎn)贊+收藏+轉(zhuǎn)發(fā)朋友圈素質(zhì)三連


        文章不錯(cuò)?點(diǎn)個(gè)【在看】吧!??


        瀏覽 75
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            sandrarusso精品艳妇 | 青青草视频精品 | 91黄色在线 | 日日骚影院| 91视色 | 全黄h全肉短篇禁乱np慕浅浅 | 日韩欧美一区二区三区久久精品 | 免费看欧美三级片 | 国产乱子伦无码视频免费 | 精品无码在线观看 |