如何統計連續打卡天數


  以下文章經過少量修改,主要來源於俊紅的數據分析之路 ,作者張俊紅,以及參考文獻:https://mp.weixin.qq.com/s/sg7VbRkS5xmJJo8FI6Hcvg

今天來解一道題面試中可能經常會被一些面試官拿來“刁難”的題,就是《如何統計連續打卡天數》,當然了這里面的打卡可以換成任意其他行為,比如連續登陸天數,連續學習天數,連續購買天數,這里的天數也是可以換成小時或者別的時間單位的。這個問題的邏輯還是有點復雜,如果要是之前沒遇到過這種問題,當場被問到的時候,肯定會一臉懵。

直接來看實戰,現在有一張表t,這張表存儲了每個員工每天的打卡情況,現在需要統計截止目前每個員工的連續打卡天數,表t如下表所示:

uid tdate is_flag
1 2020/2/1 1
1 2020/2/2 0
1 2020/2/3 1
1 2020/2/4 1
1 2020/2/5 0
1 2020/2/6 1
1 2020/2/7 1
1 2020/2/8 1
2 2020/2/1 1
2 2020/2/2 0
2 2020/2/3 0
2 2020/2/4 1
2 2020/2/5 1
2 2020/2/6 1
2 2020/2/7 1
2 2020/2/8 1

上表中uid是用戶id,tdate是日期,is_flag是記錄用戶當天是否打卡,1為打卡,0為未打卡。

我們希望得到的結果為:

uid flag_days
1 3
2 5

這個邏輯還是挺難想的,第一個想法就是通過前后數據偏移來實現,就是將is_flag向前移動一行或者向后移動一行,然后和原來的is_flag標簽做差,如果結果為0,說明前后兩天的值是相同的,要么都是0,要么都是1。但是還是不能夠得出我們想要的結果。

再換一種思路:如果是連續打卡,那么打卡日期與一個遞增的數字依次做差的結果值應該是相等的,不理解這句話沒關系,看具體結果你就明白了。

分析思路:

1.先篩選條件打卡is_flag=1,然后所有打了卡的用窗口函數按照用戶id分組按時間排序

2.然后時間日期中的天與排序做差,得到的相同數字即為連續打卡

3.再聚合函數count(日期)一下,按連續打卡分組可得每段時間的連續打卡天數

4.接着就可隨意查看最近的連續打卡天數,歷史最高連續打卡天數,打卡天數大於某個數值的人

我們先獲取每個用戶在這一段時間內所有打卡的排名,是所有打卡的排名哦,利用的是窗口函數的row_number(),代碼如下:

select  
    uid,
    tdate,
    row_number() over(partition by uid order by tdate) date_rank
from
    t
where is_flag=1

運行上面的代碼,可以得到如下結果:

uid tdate date_rank
1 2020/2/1 1
1 2020/2/3 2
1 2020/2/4 3
1 2020/2/6 4
1 2020/2/7 5
1 2020/2/8 6
2 2020/2/1 1
2 2020/2/4 2
2 2020/2/5 3
2 2020/2/6 4
2 2020/2/7 5
2 2020/2/8 6

接着再獲取每個打卡日期(tdate)中的日與其打卡日期排名(date_rank)之間的差,比如uid=1的2020/2/3的打卡日期中的3號與其排名(date_rank)2做差等於1,實現代碼如下:

select 
    uid,
    tdate,
    date_rank,
    (date_format(tdate,"%e") - date_rank) as day_cha
from 
    (
    select  
        uid,
        tdate,
        row_number() over(partition by uid order by tdate) date_rank
    from
        demo.newtable
    where is_flag=1
    )t1

date_format() 函數:用於以不同的格式顯示日期/時間數據。%e 輸出為月的天數,數值(0-31)

時間日期其他函數還可參考https://www.cnblogs.com/lverkou/p/13055614.html

運行上面的代碼,最后可以得到如下結果:

uid tdate date_rank day_cha
1 2020/2/1 1 0
1 2020/2/3 2 1
1 2020/2/4 3 1
1 2020/2/6 4 2
1 2020/2/7 5 2
1 2020/2/8 6 2
2 2020/2/1 1 0
2 2020/2/4 2 2
2 2020/2/5 3 2
2 2020/2/6 4 2
2 2020/2/7 5 2
2 2020/2/8 6 2

看上面的結果表,有沒有看出點意思來,連續打卡日期的day_cha都是相等的,比如uid=1的2020/2/3和2020/2/4是連續的,他們的day_cha都是1。到這里,如果我們要獲取連續打卡天數是不是就很容易了。

不過這里面還有一個問題,就是連續打卡天數是截止目前最近的一個 連續打卡天數還是歷史堅持最長的打卡天數,這就是傳說中的口徑問題哈。雖然在我們這個例子里面,這兩種打卡天數的出來的結果是一樣的,但是有的時候會是不一樣的,比如下面這樣的例子:

uid tdate is_flag
1 2020/2/1 1
1 2020/2/2 0
1 2020/2/3 1
1 2020/2/4 1
1 2020/2/5 1
1 2020/2/6 0
1 2020/2/7 1
1 2020/2/8 1

上面這個例子中,最近連續打卡天數是2,歷史最長的連續打卡天數卻是3。

好了,我們繼續回到解題上,我們先獲取每個用戶歷史所有連續過得的打卡情況,實現代碼如下:

select 
    uid,
    day_cha,
    count(tdate) flag_days     //后面分組后日期是不一樣的,可以統計數量
from 
    (select 
    uid,
    tdate,
    date_rank,
    (date_format(tdate,"%e") - date_rank) as day_cha
from 
    (
    select  
        uid,
        tdate,
        row_number() over(partition by uid order by tdate) date_rank
    from
        demo.newtable
    where is_flag=1
    )t1
    )t2
group by 
    uid,
    day_cha;

運行上面的代碼,得到如下結果:

uid day_cha flag_days
1 0 1
1 1 2
1 2 3
2 0 1
2 2 5

要獲取最近的連續打卡天數,我們只需要把上表中day_cha這一列最大的值對應的flag_days取出來就可以;要獲取歷史最久的連續打卡天數,我們只需要把上表中flag_days的最大值取出來就可以。直接再來個子查詢就好了。

類似的需求可能還有獲取過去連續打卡天數大於某個值的人,只需要篩選上表中的flag_days即可達到目的。只要能夠生成上面這樣每個人歷史所有連續打卡的情況表,那么大部分連續打卡相關的需求都可以通過上表來獲得。

很經典的一道題,或者是一種業務場景,大家各自多多練習。

 

 
        

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM