python代码优化指南_扣丁学堂Python视频教程之Pandas初学者代码优化指南
扣丁學(xué)堂Python視頻教程之Pandas初學(xué)者代碼優(yōu)化指南
2018-02-05 10:44:24
1320瀏覽
今天扣丁學(xué)堂給大家介紹一下關(guān)于Python視頻教程之Pandas詳解,首先Pandas是PythonDataAnalysisLibrary的簡(jiǎn)寫(xiě),它是為了解決數(shù)據(jù)分析任務(wù)而創(chuàng)建的工具,本文介紹了五種由慢到快逐步優(yōu)化其效率的方法,如果你用Python語(yǔ)言做過(guò)任何的數(shù)據(jù)分析,那么可能會(huì)用到Pandas,一個(gè)由WesMcKinney寫(xiě)的奇妙的分析庫(kù)。通過(guò)賦予Python數(shù)據(jù)幀以分析功能,Pandas已經(jīng)有效地把Python和一些諸如R或者SAS這樣比較成熟的分析工具置于相同的地位。
不幸的是,在早期,Pandas因“慢”而聲名狼藉。的確,Pandas代碼不可能達(dá)到如完全優(yōu)化的塬始C語(yǔ)言代碼的計(jì)算速度。然而,好消息是,對(duì)于大多數(shù)應(yīng)用程序來(lái)說(shuō),寫(xiě)的好的Pandas代碼已足夠快;Pandas強(qiáng)大的功能和友好的用戶體驗(yàn)彌補(bǔ)了其速度的缺點(diǎn)。
在這篇文章中,應(yīng)用于PandasDataFrame函數(shù)的幾種方法的效率,從最慢到最快:
1.在用索引的DataFrame行上的Crudelooping
2.用iterrows()循環(huán)
3.用apply()循環(huán)
4.PandasSeries矢量化
5.NumPy數(shù)組矢量化
對(duì)于我們的實(shí)例函數(shù),將使用Haversine(半正矢)距離公式。函數(shù)取兩點(diǎn)的經(jīng)緯度,調(diào)整球面的曲率,計(jì)算它們之間的直線距離。這個(gè)函數(shù)看起來(lái)像這樣:
importnumpyasnp
#DefineabasicHaversinedistanceformula
defhaversine(lat1,lon1,lat2,lon2):
MILES=3959
lat1,lon1,lat2,lon2=map(np.deg2rad,[lat1,lon1,lat2,lon2])
dlat=lat2-lat1
dlon=lon2-lon1
a=np.sin(dlat/2)**2+np.cos(lat1)*np.cos(lat2)*np.sin(dlon/2)**2
c=2*np.arcsin(np.sqrt(a))
total_miles=MILES*c
returntotal_miles
Pandas中的Crudelooping,或者你永遠(yuǎn)不應(yīng)該這么做
首先,讓我們快速回顧一下Pandas數(shù)據(jù)結(jié)構(gòu)的基本塬理。Pandas的基本結(jié)構(gòu)有兩種形式:DataFrame和Series。一個(gè)DataFrame是一個(gè)二維數(shù)組標(biāo)記軸,很多功能與R中的data.frame類似,可以將DataFrame理解為Series的容器。換句話說(shuō),一個(gè)DataFrame是一個(gè)有行和列的矩陣,列有列名標(biāo)簽,行有索引標(biāo)簽。在PandasDataFrame中一個(gè)單獨(dú)的列或者行是一個(gè)PandasSeries—一個(gè)帶有軸標(biāo)簽的一維數(shù)組。
幾乎每一個(gè)與我合作過(guò)的Pandas初學(xué)者,都曾經(jīng)試圖通過(guò)一次一個(gè)的遍歷DataFrame行去應(yīng)用自定義函數(shù)。這種方法的優(yōu)點(diǎn)是,它是Python對(duì)象之間交互的一致方式;例如,一種可以通過(guò)列表或數(shù)組循環(huán)的方式。反過(guò)來(lái)說(shuō),不利的一面是,在Pandas中,Crudeloop是最慢的方法。與下面將要討論的方法不同,Pandas中的Crudeloop沒(méi)有利用任何內(nèi)置優(yōu)化,通過(guò)比較,其效率極低(而且代碼通常不那么具有可讀性)
例如,有人可能會(huì)寫(xiě)像下面這樣的代碼:
#Defineafunctiontomanuallyloopoverallrowsandreturnaseriesofdistances
defhaversine_looping(df):
distance_list=[]
foriinrange(0,len(df)):
d=haversine(40.671,-73.985,df.iloc[i]['latitude'],df.iloc[i]['longitude'])
distance_list.append(d)
returndistance_list
為了了解執(zhí)行上述函數(shù)所需要的時(shí)間,我們用%timeit命令。%timeit是一個(gè)“神奇的”命令,專用于Jupyternotebook(所有的魔法命令都以%標(biāo)識(shí)開(kāi)始,如果%命令只應(yīng)用于一行,那么%%命令應(yīng)用于整個(gè)Jupyter單元)。%timeit命令將多次運(yùn)行一個(gè)函數(shù),并打印出獲得的運(yùn)行時(shí)間的平均值和標(biāo)準(zhǔn)差。當(dāng)然,通過(guò)%timeit命令獲得的運(yùn)行時(shí)間,運(yùn)行該函數(shù)的每個(gè)系統(tǒng)都不盡相同。盡管如此,它可以提供一個(gè)有用的基準(zhǔn)測(cè)試工具,用于比較同一系統(tǒng)和數(shù)據(jù)集上不同函數(shù)的運(yùn)行時(shí)間。
%%timeit
#Runthehaversineloopingfunction
df['distance']=haversine_looping(df)
結(jié)果是:
1645ms±31msperloop(mean±std.dev.of7runs,1loopeach)
通過(guò)分析,crudelooping函數(shù)運(yùn)行了大約645ms,標(biāo)準(zhǔn)差是31ms。這似乎很快,但考慮到它僅需要處理大約1600行的代碼,因此它實(shí)際上是很慢的。接下來(lái)看看如何改善這種不好的狀況。
用iterrows()循環(huán)
如果循環(huán)是必須的,找一個(gè)更好的方式去遍歷行,比如用iterrows()方法。iterrows()是一個(gè)生成器,遍歷DataFrame的所有行并返回每一行的索引,除了包含行自身的對(duì)象。iterrows()是用PandasDataFrame優(yōu)化,盡管它是運(yùn)行大多數(shù)標(biāo)準(zhǔn)函數(shù)最不高效的方式(稍后再談),但相對(duì)于Crudelooping,這是一個(gè)重大的改進(jìn)。在我們的案例中,iterrows()解決同一個(gè)問(wèn)題,幾乎比手動(dòng)遍歷行快四倍。
%%timeit
#Haversineappliedonrowsviaiteration
haversine_series=[]
forindex,rowindf.iterrows():
haversine_series.append(haversine(40.671,-73.985,row['latitude'],row['longitude']))
df['distance']=haversine_series
1166ms±2.42msperloop(mean±std.dev.of7runs,1loopeach)
使用apply()方法實(shí)現(xiàn)更好的循環(huán)
一個(gè)比iterrows()更好的選擇是用apply()方法,它應(yīng)用一個(gè)函數(shù),沿著DataFrame某一個(gè)特定的軸線(意思就是行或列)。雖然apply()也固有的通過(guò)行循環(huán),但它通過(guò)采取一些內(nèi)部?jī)?yōu)化比iterrows()更高效,例如在Cython中使用迭代器。我們使用一個(gè)匿名的lambda函數(shù),每一行都用Haversine函數(shù),它允許指向每一行中的特定單元格作為函數(shù)的輸入。為了指定Pandas是否應(yīng)該將函數(shù)應(yīng)用于行(axis=1)或列(axis=0),Lambda函數(shù)包含最終的axis參數(shù)。
%%timeit
#TimingapplyontheHaversinefunction
df['distance']=df.apply(lambdarow:haversine(40.671,-73.985,row['latitude'],row['longitude']),axis=1)
190.6ms±7.55msperloop(mean±std.dev.of7runs,10loopseach)
iterrows()方法用apply()方法替代后,大致可以將函數(shù)的運(yùn)行時(shí)間減半。為了更深入地了解函數(shù)中的實(shí)際運(yùn)行時(shí)間,可以運(yùn)行一個(gè)在線分析器工具(Jupyter中神奇的命令%lprun)
#Haversineappliedonrowswithlineprofiler
%lprun-fhaversinedf.apply(lambdarow:haversine(40.671,-73.985,row['latitude'],row['longitude']),axis=1)
結(jié)果如下:
我們可以從這個(gè)信息中得到一些有用的見(jiàn)解。例如,進(jìn)行叁角計(jì)算的函數(shù)占了總運(yùn)行時(shí)間的近一半。因此,如果想優(yōu)化函數(shù)的各個(gè)組件,可以從這里入手。現(xiàn)在,特別值得注意的是每一行都被循環(huán)了1631次—apply()遍歷每一行的結(jié)果。如果可以減少重復(fù)的工作量,就可以降低整個(gè)運(yùn)行時(shí)間。矢量化提供了一種更有效的替代方案。
PandasSeries矢量化
要了解如何可以減少函數(shù)所執(zhí)行的迭代數(shù)量,就要記得Pandas的基本單位,DataFrame和Series,它們都基于數(shù)組。基本單元的固有結(jié)構(gòu)轉(zhuǎn)換成內(nèi)置的設(shè)計(jì)用于對(duì)整個(gè)數(shù)組進(jìn)行操作的Pandas函數(shù),而不是按各個(gè)值的順序(簡(jiǎn)稱標(biāo)量)。矢量化是對(duì)整個(gè)數(shù)組執(zhí)行操作的過(guò)程。
Pandas包含一個(gè)總體的矢量化函數(shù)集合,從數(shù)學(xué)運(yùn)算到聚合和字符串函數(shù)(可用函數(shù)的擴(kuò)展列表,查看Pandasdocs)。對(duì)PandasSeries和DataFrame的操作進(jìn)行內(nèi)置優(yōu)化。結(jié)果,使用矢量Pandas函數(shù)幾乎總是會(huì)用自定義的循環(huán)實(shí)現(xiàn)類似的功能。
到目前為止,我們僅傳遞標(biāo)量給Haversine函數(shù)。所有的函數(shù)都應(yīng)用在Haversine函數(shù)中,也可以在數(shù)組上操作。這使得距離矢量化函數(shù)的過(guò)程非常的簡(jiǎn)單:不是傳遞個(gè)別標(biāo)量值的緯度和經(jīng)度給它,而是把它傳遞給整個(gè)series(列)。這使得Pandas受益于可用于矢量函數(shù)的全套優(yōu)化,特別是包括同時(shí)執(zhí)行整個(gè)數(shù)組的所有計(jì)算。
%%timeit
#VectorizedimplementationofHaversineappliedonPandasseries
df['distance']=haversine(40.671,-73.985,df['latitude'],df['longitude'])
11.62ms±41.5μsperloop(mean±std.dev.of7runs,1000loopseach)
通過(guò)使用apply()方法,要比用iterrows()方法改進(jìn)50倍的效率,通過(guò)矢量化函數(shù)則改進(jìn)了iterrows()方法100倍—除了改變輸入類型,什么都不要做!
看一眼后臺(tái),看看函數(shù)到底在做什么:
注意,鑒于apply()執(zhí)行函數(shù)1631次,矢量化版本僅執(zhí)行一次,因?yàn)樗瑫r(shí)應(yīng)用于整個(gè)數(shù)組,這就是主要的時(shí)間節(jié)省來(lái)源。
用NumPy數(shù)組矢量化
Pandasseries矢量化可以完成日常計(jì)算優(yōu)化的絕大多數(shù)需要。然而,如果速度是最高優(yōu)先級(jí),那么可以以NumPyPython庫(kù)的形式調(diào)用援軍。
NumPy庫(kù),將自己描述為一個(gè)“Python科學(xué)計(jì)算的基本包”,在后臺(tái)執(zhí)行優(yōu)化操作,預(yù)編譯C語(yǔ)言代碼。跟Pandas一樣,NumPy操作數(shù)組對(duì)象(簡(jiǎn)稱ndarrays);然而,它省去了Pandasseries操作所帶來(lái)的大量資源開(kāi)銷(xiāo),如索引、數(shù)據(jù)類型檢查等。因此,NumPy數(shù)組的操作可以明顯快于pandasseries的操作。
當(dāng)Pandasseries提供的額外功能不是很關(guān)鍵的時(shí)候,NumPy數(shù)組可以用于替代Pandasseries。例如,Haversine函數(shù)矢量化實(shí)現(xiàn)不使用索引的經(jīng)度和緯度系列,因此沒(méi)有那些索引,也不會(huì)導(dǎo)致函數(shù)中斷。通過(guò)比較,我們所做的操作如DataFrame的連接,它需要按索引來(lái)引用值,可能需要堅(jiān)持使用Pandas對(duì)象。
僅僅是使用Pandasseries的values的方法,把緯度和經(jīng)度數(shù)組從Pandasseries轉(zhuǎn)換到NumPy數(shù)組。就像series矢量化一樣,通過(guò)NumPy數(shù)組直接進(jìn)入函數(shù)將可以讓Pandas對(duì)整個(gè)矢量應(yīng)用函數(shù)。
%%timeit
#VectorizedimplementationofHaversineappliedonNumPyarrays
df['distance']=haversine(40.671,-73.985,df['latitude'].values,df['longitude'].values)
1370μs±18μsperloop(mean±std.dev.of7runs,1000loopseach)
NumPy數(shù)組操作運(yùn)行取得了又一個(gè)四倍的改善。總之,通過(guò)looping改進(jìn)了運(yùn)行時(shí)間超過(guò)半秒,通過(guò)NumPy矢量化,運(yùn)行時(shí)間改進(jìn)到了叁分之一毫秒級(jí)!
以上就是關(guān)于扣丁學(xué)堂Python視頻教程之Pandas初學(xué)者代碼優(yōu)化指南的詳細(xì)介紹,最后想要了解更多關(guān)于Python發(fā)展前景趨勢(shì),請(qǐng)關(guān)注扣丁學(xué)堂官網(wǎng)、微信等平臺(tái),扣丁學(xué)堂IT職業(yè)在線學(xué)習(xí)教育平臺(tái)為您提供最新的Python培訓(xùn)視頻教程系統(tǒng),通過(guò)千鋒扣丁學(xué)堂金牌講師在線錄制的Python視頻教程課程,讓你快速掌握Python從入門(mén)到精通開(kāi)發(fā)實(shí)戰(zhàn)技能。扣丁學(xué)堂Python開(kāi)發(fā)工程師技術(shù)交流群:279521237。
【關(guān)注微信公眾號(hào)獲取更多學(xué)習(xí)資料】
標(biāo)簽:
Python視頻教程
Python基礎(chǔ)教程
Python爬蟲(chóng)
Python培訓(xùn)
Python開(kāi)發(fā)工程師
總結(jié)
以上是生活随笔為你收集整理的python代码优化指南_扣丁学堂Python视频教程之Pandas初学者代码优化指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java如何用LOG打印日志并输出信息到
- 下一篇: python socket编程之双方相互