pandas 計算連續行爲天數的幾種思路

大家好,我是才哥。

最近在處理數據的時候遇到一個需求,核心就是_**求取最大連續行爲天數**_。類似需求在去年筆者剛接觸 pandas 的時候也做過《利用 Python 統計連續登錄 N 天或以上用戶》,這裏我們可以用同樣的方法進行實現。

這裏我們用北京空氣質量數據作爲案例進行演示,需求是找出北京空氣質量連續污染最長持續多久並確定其週期。

圖 1:案例數據

以上圖中數據來算,我們可以看到從 1 月 21 日 - 1 月 26 日空氣質量連續污染持續了 6 天。

不過,在實際的數據處理中,我們的原始數據往往會較大,並不一定能直接看出來。接下來,我們介紹幾種解決方案供大家參考。

  1. 獲取案例數據

大家可以直接在後臺回覆 0427 獲取案例數據,同樣也可以通過以下方式獲取案例數據。

import akshare as ak

air_quality_hist_df = ak.air_quality_hist(city="北京"period="day"start_date="2021-01-01"end_date="2021-04-26")
air_quality_hist_df.head()

圖 2:akshare 數據預覽

由於我們只需要用到 aqi,並按照國際標準進行優良與污染定級,這裏簡單做下數據處理如下:(後臺直接回復0427獲取的數據是處理後的數據哈)

import pandas as pd

# 重置索引
aqi = air_quality_hist_df['aqi'].reset_index()
# 將aqi列改爲int類型
aqi.aqi = aqi.aqi.astype('int')
# 使用分箱進行空氣質量定級
aqi['空氣質量'] = pd.cut(aqi.aqi,
                        bins=[0,100,500],
                        labels=['優良','污染'])
# 取10個樣本預覽
aqi.sample(10)

圖 3:處理後數據

  1. 求連續污染持續天數

結合上次的《利用 Python 統計連續登錄 N 天或以上用戶》案例,我們這裏再提供 1 種新的解題思路,合計 2 種解題思路。

以下解法來自小明哥才哥

2.1. 思路 1:按時間排序求差值再分組計數

才哥上次的解法就是這種思路,回看當初的代碼顯得比較稚嫩,今天我們看看小明哥的解法,非常精彩。

步驟 1:篩選空氣質量爲污染的數據

t = aqi.query('空氣質量=="污染"')
t.sample(5)

圖 4:篩選空氣質量污染的數據

步驟 2:新增輔助列(輔助列可以不用加到原數據t上)

這裏的邏輯大概如下:

groupids = pd.to_datetime(aqi.time)-pd.to_timedelta(aqi.time.rank(),unit='d')
groupids.sample(5)

圖 5:輔助列

步驟 3:分組計數獲得連續天數,分組求最小最大值獲得連續 污染起止日期

t.groupby(groupids).agg({
    'time': lambda x:f'{x.min()}~{x.max()}'# 求起止日期
    '空氣質量':"count"# 求連續天數
}).nlargest(5,'空氣質量') # 取 空氣質量 字段最大的前5組數據

圖 6:解法 1 的結果

以上完整代碼如下:

t = aqi.query('空氣質量=="污染"')
t.groupby(
    pd.to_datetime(t.time)-pd.to_timedelta(t.time.rank(),unit='d')
         ).agg(
    {
    'time': lambda x:f'{x.min()}~{x.max()}',
    '空氣質量':"count",
    }
).nlargest(5,'空氣質量')

2.2. 思路 2:比對相鄰兩天空氣質量標記

思路 2 有兩種解法,其一是利用循環創建輔助列,其二是利用 shift 和 cumsum 創建輔助列,具體我們可以往下看。

解法 1:利用循環創建輔助列

last = None
num = 0
groupids = []
for v in aqi.空氣質量.values:
    if v != last or v != '污染':
        num += 1
    groupids.append(num)
    last = v

我們根據這個邏輯可以得到輔助列數據如下:

圖 7:輔助列值預覽

我們可以發現,按照輔助列分組進行計數即可獲得連續污染天數,如上紅色標記區域。

aqi.groupby(groupids).agg(
    {
    'time': lambda x:f'{x.min()}~{x.max()}',
    '空氣質量':"count",
    }
).nlargest(5,'空氣質量')

圖 8:思路 2 的解法 1 結果

解法 2:利用 shift 和 cumsum 創建輔助列

圖 9:輔助列創建思路預覽

我們也可以發現,按照輔助列分組計數即可獲取空氣質量連續天數(優良和污染均可),如上紅色區域。

(
    aqi.query('空氣質量=="污染"') # 這裏篩選 污染 天氣
    .groupby((aqi.空氣質量.shift() != aqi.空氣質量).cumsum()) # 輔助列
    .time.agg(['count','min','max']) # 計數及獲取日期區間
    .nlargest(5,'count')
)

圖 9:思路 2 的解法 2 結果

按照小明哥的輸出結果,調整代碼如下:

(
    aqi.query("空氣質量=='污染'")
    .groupby((aqi.空氣質量 != aqi.空氣質量.shift()).cumsum())
    .agg(
    {
        'time': lambda x: f"{x.min()}~{x.max()}", 
        '空氣質量': "count"}
    ).nlargest(5, '空氣質量')
)

圖 10:思路 2 的解法 2 小明哥結果

以上就是本次全部內容,其實我們在日常工作生活中還可能遇到類似場景如:計算用戶連續登錄天數計算用戶連續付費天數計算南方梅雨季節連續下雨天數等等!

如果你有更好的方案,歡迎添加作者微信一起交流學習!

作者微信號:gdc2918

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/2EjzpV38-4KlQ7fyrUbRww