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