pandas 計算連續行爲天數的幾種思路
大家好,我是才哥。
最近在處理數據的時候遇到一個需求,核心就是_**求取最大連續行爲天數**_。類似需求在去年筆者剛接觸 pandas 的時候也做過《利用 Python 統計連續登錄 N 天或以上用戶》,這裏我們可以用同樣的方法進行實現。
這裏我們用北京空氣質量數據作爲案例進行演示,需求是找出北京空氣質量連續污染最長持續多久並確定其週期。
圖 1:案例數據
以上圖中數據來算,我們可以看到從 1 月 21 日 - 1 月 26 日空氣質量連續污染持續了 6 天。
不過,在實際的數據處理中,我們的原始數據往往會較大,並不一定能直接看出來。接下來,我們介紹幾種解決方案供大家參考。
- 獲取案例數據
大家可以直接在後臺回覆 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:處理後數據
- 求連續污染持續天數
結合上次的《利用 Python 統計連續登錄 N 天或以上用戶》案例,我們這裏再提供 1 種新的解題思路,合計 2 種解題思路。
以下解法來自小明哥
和才哥
2.1. 思路 1:按時間排序求差值再分組計數
才哥上次的解法就是這種思路,回看當初的代碼顯得比較稚嫩,今天我們看看小明哥的解法,非常精彩。
步驟 1:篩選空氣質量爲污染的數據
t = aqi.query('空氣質量=="污染"')
t.sample(5)
圖 4:篩選空氣質量污染的數據
步驟 2:新增輔助列(輔助列可以不用加到原數據t
上)
這裏的邏輯大概如下:
-
輔助排名列(按照時間順序排序)爲間隔天數
-
然後用時間字段(time)與間隔天數求差值得到一個日期
-
如果得到的這個日期相同,則這幾天是連續污染天
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:利用循環創建輔助列
-
創建一個輔助列,輔助列的值按照以下思路創建函數獲取
-
如果空氣質量爲優良,則輔助列值 + 1;若當前空氣質量和上一日不同,則輔助列值也 + 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 創建輔助列
-
先創建空氣質量的 shift 列,下移動一位
-
如果 shift 列和空氣質量列相等,則判斷列爲 0,否則爲 1
-
輔助列爲判斷列累加求和
圖 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