【Python基础】Pandas笔记---深入Groupby,它的功能没有你想的这么简单
點(diǎn)擊上方“潛心的Python小屋”關(guān)注我們,第一時(shí)間推送優(yōu)質(zhì)文章。
前言
大家好,我是潛心。上篇文章提到了Groupby,但其中舉例的代碼有點(diǎn)問題,在提取序列時(shí)用到了for循環(huán),效率很慢,后來查找了官方文檔,才明白apply的重要性,再次對Groupby進(jìn)行深入并總結(jié)。
本文約2.1k字,預(yù)計(jì)閱讀15分鐘。
Groupby: split-apply-combine
Pandas中Groupby定義如下:
def?groupby(by=None,?axis=0,?level=None,?as_index=True,?sort=True,?group_keys=True,?squeeze=False,?observed=False)Groupby具體來說指的是涉及以下一個(gè)或多個(gè)步驟的過程:
分割(Splitting):根據(jù)一些標(biāo)準(zhǔn)將數(shù)據(jù)劃分為多個(gè)組。
應(yīng)用(Applying):獨(dú)立地對每個(gè)組應(yīng)用一個(gè)函數(shù)。
組合(Combing):將結(jié)果組合成數(shù)據(jù)結(jié)構(gòu)。
其中,分割是最直接的,也是最常用的(如上圖)。但是事實(shí)上,在許多情況下,我們可能希望將數(shù)據(jù)集分成組,并對這些組做一些統(tǒng)計(jì)值計(jì)算。在Applying中,可能希望做以下其中一項(xiàng):
聚集(Aggregation):計(jì)算每個(gè)組的匯總統(tǒng)計(jì)信息(或統(tǒng)計(jì)信息),例如計(jì)算分組的和、均值、個(gè)數(shù)等。
轉(zhuǎn)換(Transformation):執(zhí)行一些特定組的計(jì)算并返回一個(gè)like-indexed對象。
過濾(Filteration):根據(jù)判斷為真或假的組計(jì)算,丟棄一些組。
此次我們不討論Group的分割問題,主要聚焦它的Applying,并結(jié)合實(shí)際的大數(shù)據(jù)量的實(shí)際表來比較它們的效率問題。采用的數(shù)據(jù)表為用戶與他點(diǎn)擊的廣告,共100w條:
| 0 | 30920 | 567330 |
| 1 | 30920 | 3072255 |
| 2 | 30920 | 3072255 |
| 3 | 30920 | 3879497 |
| 4 | 30920 | 3751623 |
| ... | ... | ... |
Aggregation
當(dāng)創(chuàng)建了GroupBy對象,根據(jù)需求我們可以對分組的數(shù)據(jù)執(zhí)行計(jì)算。最簡單的是我們通過agg()方法來調(diào)用一些Python內(nèi)置函數(shù)進(jìn)行聚合計(jì)算,常用的內(nèi)置函數(shù)為:mean、median、sum、size、count、std、describe、min、max等。
這里我們基于user_id對數(shù)據(jù)進(jìn)行劃分,簡單應(yīng)用部分內(nèi)置函數(shù),統(tǒng)計(jì)對每個(gè)用戶他點(diǎn)擊過的最大和最小的廣告id:
%%time In[1]:?df.groupby('user_id').agg(['count',?'min',?'max'])CPU?times:?user?72?ms,?sys:?0?ns,?total:?72?ms Wall?time:?70.9?ms Out[1]:user_id?count?min?max??? 31?26?109090?4440651 34?63?3983?4266235 36?19?67988?3999372 310?12?10039?3042631 312?28?24918?3904071 ...?...?...?... 363305?16?42555?4187970 363311?19?187530?4151703 363317?15?63052?4307786 363318?41?7400?4079814 363319?167?10257?4389117 30025?rows?×?3?columns以上我們發(fā)現(xiàn)索引變?yōu)榱藆ser_id,若不想改變DataFrame的索引,則需要在groupby方法中的參數(shù)as_index設(shè)置為False。
當(dāng)然如果你需要進(jìn)一步進(jìn)行統(tǒng)計(jì)運(yùn)算,則panda允許提供多個(gè)lambdas自定義計(jì)算。在這種情況下,panda將混淆lambda函數(shù)的名稱,將_<i>附加到每個(gè)后續(xù)的lambda。可以通過rename()更改列名。
%%time In[2]:?df.groupby('user_id').agg([lambda?x:?x.max()?-?x.min(),?lambda?x:?x.mean()?-?x.median()])CPU?times:?user?20.4?s,?sys:?0?ns,?total:?20.4?s Wall?time:?20.4?s Out[2]:creative_id????? user_id?<lambda_0>?<lambda_1> 31?4331561?-107949.846154 34?4262252?430452.825397 36?3931384?-16653.473684 310?3032592?-121110.916667 312?3879153?208385.392857 ...?...?... 363305?4145415?364584.500000 363311?3964173?524387.842105 363317?4244734?787231.666667 363318?4072414?-110147.829268 363319?4378860?250463.107784 30025?rows?×?2?columns對不同的列使用不同的聚合可以通過字典的方式實(shí)現(xiàn)(這里采用文檔的Code,C、D為列名):
In?[3]:?grouped.agg({'C':?np.sum,....:??????????????'D':?lambda?x:?np.std(x,?ddof=1)})....:? Out[3]:?C?????????D A?????????????????????? bar??0.392940??1.366330 foo?-1.796421??0.884785Transformation
transform方法返回一個(gè)與正在分組的對象索引相同(大小相同)的對象。
我們通過對表進(jìn)行與agg()相同的內(nèi)置函數(shù)進(jìn)行比較:
%%time In[4]:?df.groupby('user_id').transform('count')CPU?times:?user?28?ms,?sys:?0?ns,?total:?28?ms Wall?time:?29?ms Out[4]: creative_id 0?42 1?42 2?42 3?42 4?42 ...?... 999995?167 999996?167 999997?167 999998?167 999999?167 1000000?rows?×?1?columns我們發(fā)現(xiàn)返回了一個(gè)與原數(shù)據(jù)表大小相同的對象,并且把groupby中的by參數(shù)給省略了。(并且它不能一次使用多個(gè)內(nèi)置函數(shù))。
實(shí)際用途:?如果我們需要為原數(shù)據(jù)表添加一列count或其他內(nèi)容,則需要使用該方法。
In[5]:?df['ad_count']?=?df.groupby('user_id').transform('count') Out[5]:?user_id?creative_id?ad_count 0????30920??567330?42 1????30920??3072255?42 2????30920??2361327?42 3????30920??3879497?42 4????30920??3751623?42 ...?...?...?... 999995?363319?121860?167 999996?363319?413801?167 999997?363319?415805?167 999998?363319?487803?167 999999?363319?655613?167 1000000?rows?×?3?columnsFilteration
filter方法是通過一些布爾判斷對分組后的內(nèi)容進(jìn)行篩選,返回一個(gè)原始對象的子集。
假設(shè)我們需要過濾得到用戶點(diǎn)擊廣告數(shù)小于100的樣本:
%%time In[6]:?df.groupby('user_id').filter(lambda?x:?len(x)?<?100)CPU?times:?user?7.48?s,?sys:?0?ns,?total:?7.48?s Wall?time:?7.49?s Out[6]:user_id?creative_id?ad_count 0????30920??567330?42 1????30920??3072255?42 2????30920??2361327?42 3????30920??3879497?42 4????30920??3751623?42 ...?...?...?... 999828?363318?1779084?41 999829?363318?1985010?41 999830?363318?66606??41 999831?363318?1056195?41 999832?363318?1435072?41 838425?rows?×?3?columns可以發(fā)現(xiàn),樣本數(shù)目減少,每個(gè)用戶點(diǎn)擊廣告數(shù)大于100的已被去除。
Apply
apply方法相比更加靈活,它可以完成上述的agg、transform以及filter,具體取決于傳遞給它的是什么。并且它可以使用自定義函數(shù)。
例如返回每個(gè)用戶最大的廣告id(即agg方法)。
%%time def?change_ad_count(df):return?df['creative_id'].max() In[7]:?df.groupby('user_id').apply(change_ad_count)CPU?times:?user?4.93?s,?sys:?0?ns,?total:?4.93?s Wall?time:?4.93?s Out[7]: user_id 31????????4440651 34????????4266235 36????????3999372 310???????3042631 312???????3904071...??? 363305????4187970 363311????4151703 363317????4307786 363318????4079814 363319????4389117 Length:?30025,?dtype:?int64比較
因?yàn)樯掀恼绿岬搅薟ord2vec,通過每個(gè)用戶所點(diǎn)擊的廣告來構(gòu)造句子,因此需要將按照user_id進(jìn)行分組,并將每個(gè)用戶點(diǎn)擊的廣告構(gòu)造一個(gè)列表。之前用了for循環(huán),效率極慢,現(xiàn)在改用agg和apply方法進(jìn)行比較。
agg:
%%time sentences?=?df.groupby(['user_id'])['creative_id'].agg(lambda?x:?x.tolist()).tolist()CPU?times:?user?4.13?s,?sys:?64?ms,?total:?4.2?s Wall?time:?4.2?sapply:
%%time sentences?=?df.groupby(['user_id'])['creative_id'].apply(lambda?x:?x.tolist()).tolist() CPU?times:?user?4.18?s,?sys:?52?ms,?total:?4.23?s Wall?time:?4.23?s %%time sentences?=?df.groupby(['user_id']).apply(lambda?x:?x['creative_id'].tolist()).tolist() CPU?times:?user?2.32?s,?sys:?44?ms,?total:?2.37?s Wall?time:?2.37?s我們發(fā)現(xiàn)apply方法與agg相差無幾,但我們?nèi)魧reative_id放入lambda中,則效率更高。個(gè)人認(rèn)為應(yīng)該是和處理的對象不同。
df.groupby(['user_id'])['creative_id']<pandas.core.groupby.generic.SeriesGroupBy?object?at?0x7f1495e8d9e8>df.groupby(['user_id'])<pandas.core.groupby.generic.DataFrameGroupBy?object?at?0x7f1495e80a20> ##?apply中再處理Series對象總結(jié)
groupby是pandas最有效的方法之一,經(jīng)常與agg、transform、filter、apply相結(jié)合使用。
???????????????????????????????????????????
往期精彩回顧適合初學(xué)者入門人工智能的路線及資料下載機(jī)器學(xué)習(xí)及深度學(xué)習(xí)筆記等資料打印機(jī)器學(xué)習(xí)在線手冊深度學(xué)習(xí)筆記專輯《統(tǒng)計(jì)學(xué)習(xí)方法》的代碼復(fù)現(xiàn)專輯 AI基礎(chǔ)下載機(jī)器學(xué)習(xí)的數(shù)學(xué)基礎(chǔ)專輯獲取一折本站知識星球優(yōu)惠券,復(fù)制鏈接直接打開:https://t.zsxq.com/662nyZF本站qq群1003271085。加入微信群請掃碼進(jìn)群(如果是博士或者準(zhǔn)備讀博士請說明):總結(jié)
以上是生活随笔為你收集整理的【Python基础】Pandas笔记---深入Groupby,它的功能没有你想的这么简单的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python基础】Python基础语法
- 下一篇: 【Python基础】Pandas三种实现