Pandas 計算連續(xù)行為天數(shù)的幾種思路
大家好,我是才哥。
最近在處理數(shù)據(jù)的時候遇到一個需求,核心就是求取最大連續(xù)行為天數(shù)。類似需求在去年筆者剛接觸 pandas 的時候也做過《利用Python統(tǒng)計連續(xù)登錄N天或以上用戶》,這里可以用同樣的方法進行實現(xiàn)。
這里用北京空氣質(zhì)量數(shù)據(jù)作為案例進行演示,需求是找出北京空氣質(zhì)量連續(xù)污染最長持續(xù)多久并確定其周期。

以上圖中數(shù)據(jù)來算,可以看到從1月21日-1月26日空氣質(zhì)量連續(xù)污染持續(xù)了6天。
不過,在實際的數(shù)據(jù)處理中,原始數(shù)據(jù)往往會較大,并不一定能直接看出來。接下來,介紹幾種解決方案供大家參考。
1. 獲取案例數(shù)據(jù)
大家可以通過以下方式獲取案例數(shù)據(jù)。
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()

由于只需要用到aqi,并按照國際標(biāo)準(zhǔn)進行優(yōu)良與污染定級,這里簡單做下數(shù)據(jù)處理如下:
import pandas as pd
# 重置索引
aqi = air_quality_hist_df['aqi'].reset_index()
# 將aqi列改為int類型
aqi.aqi = aqi.aqi.astype('int')
# 使用分箱進行空氣質(zhì)量定級
aqi['空氣質(zhì)量'] = pd.cut(aqi.aqi,
bins=[0,100,500],
labels=['優(yōu)良','污染'])
# 取10個樣本預(yù)覽
aqi.sample(10)

2. 求連續(xù)污染持續(xù)天數(shù)
結(jié)合上次的《利用Python統(tǒng)計連續(xù)登錄N天或以上用戶》案例,本文再提供1種新的解題思路,合計2種解題思路。
2.1. 思路1:按時間排序求差值再分組計數(shù)
才哥上次的解法就是這種思路,回看當(dāng)初的代碼顯得比較稚嫩,今天我們看看小明哥的解法,非常精彩。
步驟1:篩選空氣質(zhì)量為污染的數(shù)據(jù)
t = aqi.query('空氣質(zhì)量=="污染"')
t.sample(5)

步驟2:新增輔助列(輔助列可以不用加到原數(shù)據(jù)t上)
這里的邏輯大概如下:
輔助排名列(按照時間順序排序)為間隔天數(shù) 然后用時間字段(time)與間隔天數(shù)求差值得到一個日期 如果得到的這個日期相同,則這幾天是連續(xù)污染天
groupids = pd.to_datetime(aqi.time)-pd.to_timedelta(aqi.time.rank(),unit='d')
groupids.sample(5)

步驟3:分組計數(shù)獲得連續(xù)天數(shù),分組求最小最大值獲得連續(xù) 污染起止日期
t.groupby(groupids).agg({
'time': lambda x:f'{x.min()}~{x.max()}', # 求起止日期
'空氣質(zhì)量':"count", # 求連續(xù)天數(shù)
}).nlargest(5,'空氣質(zhì)量') # 取 空氣質(zhì)量 字段最大的前5組數(shù)據(jù)

以上完整代碼如下:
t = aqi.query('空氣質(zhì)量=="污染"')
t.groupby(
pd.to_datetime(t.time)-pd.to_timedelta(t.time.rank(),unit='d')
).agg(
{
'time': lambda x:f'{x.min()}~{x.max()}',
'空氣質(zhì)量':"count",
}
).nlargest(5,'空氣質(zhì)量')
2.2. 思路2:比對相鄰兩天空氣質(zhì)量標(biāo)記
思路2有兩種解法,其一是利用循環(huán)創(chuàng)建輔助列,其二是利用shift和cumsum創(chuàng)建輔助列,具體我們可以往下看。
解法1:利用循環(huán)創(chuàng)建輔助列
創(chuàng)建一個輔助列,輔助列的值按照以下思路創(chuàng)建函數(shù)獲取 如果空氣質(zhì)量為優(yōu)良,則輔助列值+1;若當(dāng)前空氣質(zhì)量和上一日不同,則輔助列值也+1 以上均不滿足,則輔助列值不變
last = None
num = 0
groupids = []
for v in aqi.空氣質(zhì)量.values:
if v != last or v != '污染':
num += 1
groupids.append(num)
last = v
根據(jù)這個邏輯可以得到輔助列數(shù)據(jù)如下:

可以發(fā)現(xiàn),按照輔助列分組進行計數(shù)即可獲得連續(xù)污染天數(shù),如上紅色標(biāo)記區(qū)域。
aqi.groupby(groupids).agg(
{
'time': lambda x:f'{x.min()}~{x.max()}',
'空氣質(zhì)量':"count",
}
).nlargest(5,'空氣質(zhì)量')

解法2:利用shift和cumsum創(chuàng)建輔助列
先創(chuàng)建空氣質(zhì)量的shift列,下移動一位 如果shift列和空氣質(zhì)量列相等,則判斷列為0,否則為1 輔助列為判斷列累加求和

我們也可以發(fā)現(xiàn),按照輔助列分組計數(shù)即可獲取空氣質(zhì)量連續(xù)天數(shù)(優(yōu)良和污染均可),如上紅色區(qū)域。
(
aqi.query('空氣質(zhì)量=="污染"') # 這里篩選 污染 天氣
.groupby((aqi.空氣質(zhì)量.shift() != aqi.空氣質(zhì)量).cumsum()) # 輔助列
.time.agg(['count','min','max']) # 計數(shù)及獲取日期區(qū)間
.nlargest(5,'count')
)

按照小明哥的輸出結(jié)果,調(diào)整代碼如下:
(aqi.query("空氣質(zhì)量=='污染'").groupby((aqi.空氣質(zhì)量 != aqi.空氣質(zhì)量.shift()).cumsum()).agg({'time': lambda x: f"{x.min()}~{x.max()}",'空氣質(zhì)量': "count"}).nlargest(5, '空氣質(zhì)量'))

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