【Python】Pandas中的寶藏函數(shù)-transform()
Pandas具有很多強(qiáng)大的功能,transform就是其中之一,利用它可以高效地匯總數(shù)據(jù)且不改變數(shù)據(jù)行數(shù),transform是一種什么數(shù)據(jù)操作?如果熟悉SQL的窗口函數(shù),就非常容易理解了,該函數(shù)的核心功能是,既計(jì)算了統(tǒng)計(jì)值,又保留了明細(xì)數(shù)據(jù)。為了更好地理解transform和agg的不同,下面從實(shí)際的應(yīng)用場景出發(fā)進(jìn)行對(duì)比。
aggregation會(huì)返回?cái)?shù)據(jù)的縮減版本,而transformation能返回完整數(shù)據(jù)的某一變換版本供我們重組。這樣的transformation,輸出的形狀和輸入一致。一個(gè)常見的例子是通過減去分組平均值來居中數(shù)據(jù)。
#數(shù)據(jù)構(gòu)造data = pd.DataFrame({"company":['百度', '阿里', '百度', '阿里', '百度', '騰訊', '騰訊', '阿里', '騰訊', '阿里'],"salary":[43000, 24000, 40000, 39000, 8000, 47000, 25000, 16000, 21000, 38000],"age":[25, 34, 49, 42, 28, 23, 45, 21, 34, 29]})datacompany salary age0 百度 43000 251 阿里 24000 342 百度 40000 493 阿里 39000 424 百度 8000 285 騰訊 47000 236 騰訊 25000 457 阿里 16000 218 騰訊 21000 349 阿里 38000 29
1、transform作用于Series
1)單個(gè)變換函數(shù)
當(dāng)transform作用于單列Series時(shí)較為簡單 ,對(duì)salary列進(jìn)行transform變換我們可以傳入任意的非聚合類函數(shù),比如對(duì)工資列對(duì)數(shù)化
import pandas as pdimport numpy as np# 對(duì)工資對(duì)數(shù)化data['salary'].transform(np.log)0 10.6689551 10.0858092 10.5966353 10.5713174 8.9871975 10.7579036 10.1266317 9.6803448 9.9522789 10.545341Name: salary, dtype: float64
除了內(nèi)置函數(shù),還可以傳入lambda函數(shù)
# lambda函數(shù)data['salary'].transform(lambda s: s+1)0 430011 240012 400013 390014 80015 470016 250017 160018 210019 38001Name: salary, dtype: int64
2)多個(gè)變換函數(shù)
也可以傳入包含多個(gè)變換函數(shù)的列表來一口氣計(jì)算出多列結(jié)果:
data['salary'].transform([np.log, lambda s: s+1, np.sqrt])log <lambda> sqrt0 10.668955 43001 207.3644141 10.085809 24001 154.9193342 10.596635 40001 200.0000003 10.571317 39001 197.4841774 8.987197 8001 89.4427195 10.757903 47001 216.7948346 10.126631 25001 158.1138837 9.680344 16001 126.4911068 9.952278 21001 144.9137679 10.545341 38001 194.935887
而又因?yàn)閠ransform傳入的函數(shù),在執(zhí)行運(yùn)算時(shí)接收的輸入?yún)?shù)是對(duì)應(yīng)的整列數(shù)據(jù),所以我們可以利用這個(gè)特點(diǎn)實(shí)現(xiàn)諸如數(shù)據(jù)標(biāo)準(zhǔn)化、歸一化等需要依賴樣本整體統(tǒng)計(jì)特征的變換過程:
# 利用transform進(jìn)行數(shù)據(jù)標(biāo)準(zhǔn)化data['salary'].transform(lambda s: (s - s.mean()) / s.std())0 0.9910381 -0.4686302 0.7605643 0.6837394 -1.6978255 1.2983376 -0.3918067 -1.0832288 -0.6991049 0.606915Name: salary, dtype: float64
2、 transform作用于DataFrame
當(dāng)transform作用于整個(gè)DataFrame時(shí),實(shí)際上就是將傳入的所有變換函數(shù)作用到每一列中:
data.loc[:,'salary':'age'].transform(lambda s:(s-s.mean()) /s.std())salary age0 0.991038 -0.8320501 -0.468630 0.1040062 0.760564 1.6641013 0.683739 0.9360574 -1.697825 -0.5200315 1.298337 -1.0400636 -0.391806 1.2480757 -1.083228 -1.2480758 -0.699104 0.1040069 0.606915 -0.416025
而當(dāng)傳入多個(gè)變換函數(shù)時(shí),對(duì)應(yīng)的返回結(jié)果格式類似agg中的機(jī)制,會(huì)生成MultiIndex格式的字段名
data.loc[:, 'salary': 'age'].transform([np.log, lambda s: s+1])salary agelog <lambda> log <lambda>0 10.668955 43001 3.218876 261 10.085809 24001 3.526361 352 10.596635 40001 3.891820 503 10.571317 39001 3.737670 434 8.987197 8001 3.332205 295 10.757903 47001 3.135494 246 10.126631 25001 3.806662 467 9.680344 16001 3.044522 228 9.952278 21001 3.526361 359 10.545341 38001 3.367296 30
而且由于作用的是DataFrame,還可以利用字典以鍵值對(duì)的形式,一口氣為每一列配置單個(gè)或多個(gè)變換函數(shù):
(data.loc[:, 'salary': 'age'].transform({'age': lambda s: (s - s.mean()) / s.std(),'salary': [np.log, np.sqrt]}))age salary<lambda> log sqrt0 -0.832050 10.668955 207.3644141 0.104006 10.085809 154.9193342 1.664101 10.596635 200.0000003 0.936057 10.571317 197.4841774 -0.520031 8.987197 89.4427195 -1.040063 10.757903 216.7948346 1.248075 10.126631 158.1138837 -1.248075 9.680344 126.4911068 0.104006 9.952278 144.9137679 -0.416025 10.545341 194.935887
3、transform作用于groupby分組后
在原來的數(shù)據(jù)中,我們知道了如何求不同公司的平均薪水,假如需要在原數(shù)據(jù)集中新增一列salary_mean,代表該公司的平均薪水,該怎么實(shí)現(xiàn)呢?
data['salary_mean'] = data.groupby('company')[['salary']].transform('mean')datacompany salary age salary_mean0 百度 43000 25 30333.3333331 阿里 24000 34 29250.0000002 百度 40000 49 30333.3333333 阿里 39000 42 29250.0000004 百度 8000 28 30333.3333335 騰訊 47000 23 31000.0000006 騰訊 25000 45 31000.0000007 阿里 16000 21 29250.0000008 騰訊 21000 34 31000.0000009 阿里 38000 29 29250.000000
通過上面的數(shù)據(jù)可以看出,利用transform輸出,既得到了統(tǒng)計(jì)數(shù)據(jù),形狀也沒有變化。
當(dāng)然,也可對(duì)多個(gè)數(shù)據(jù)列進(jìn)行計(jì)算
data.groupby('company')[['salary', 'age']].transform('mean')salary age0 30333.333333 34.01 29250.000000 31.52 30333.333333 34.03 29250.000000 31.54 30333.333333 34.05 31000.000000 34.06 31000.000000 34.07 29250.000000 31.58 31000.000000 34.09 29250.000000 31.5
我們也可以用map函數(shù)實(shí)現(xiàn)類似的功能,但是稍微復(fù)雜點(diǎn),但是有助于我們理解transform的含義。
avg_dict = data.groupby('company')['salary'].mean().to_dict()avg_dict#得到了一個(gè)平均工資的字典{'百度': 30333.333333333332, '騰訊': 31000.0, '阿里': 29250.0}#利用map函數(shù),將得到的字典映射到對(duì)應(yīng)的列data['salary_mean'] = data['company'].map(avg_dict)datacompany salary age salary_mean0 百度 43000 25 30333.3333331 阿里 24000 34 29250.0000002 百度 40000 49 30333.3333333 阿里 39000 42 29250.0000004 百度 8000 28 30333.3333335 騰訊 47000 23 31000.0000006 騰訊 25000 45 31000.0000007 阿里 16000 21 29250.0000008 騰訊 21000 34 31000.0000009 阿里 38000 29 29250.000000
以圖解的方式來看看進(jìn)行g(shù)roupby后transform的實(shí)現(xiàn)過程(公司列包含ABC,salary列為每個(gè)員工的工資明細(xì)):

上圖中的大方框是transform和agg 所不一樣的地方,對(duì)agg而言,會(huì)計(jì)算并聚合得到 A,B,C 公司對(duì)應(yīng)的均值并直接返回,每個(gè)公司一條數(shù)據(jù),但對(duì)transform而言,則會(huì)對(duì)每一條數(shù)據(jù)求得相應(yīng)的結(jié)果,同一組內(nèi)的樣本會(huì)有相同的值,組內(nèi)求完均值后會(huì)按照原索引的順序返回結(jié)果
往期精彩回顧 本站qq群851320808,加入微信群請(qǐng)掃碼:
