Pandas對(duì)比Polars,語(yǔ)法和速度大PK
Pandas是數(shù)據(jù)科學(xué)中必不可少的Python庫(kù)。但其最大的缺點(diǎn)是對(duì)大型數(shù)據(jù)集的操作速度較慢。Polars是一種旨在更快地處理數(shù)據(jù)的Pandas替代方案。
Polars是一種旨在更快地處理數(shù)據(jù)的Pandas替代方案。
本文簡(jiǎn)要介紹了Polars Python包,并將其與流行的數(shù)據(jù)科學(xué)庫(kù)Pandas在語(yǔ)法和速度方面進(jìn)行了比較。
- 什么是Polars,為什么它比Pandas更快?
- 基準(zhǔn)測(cè)試設(shè)置
- 開始使用Polars
- Pandas和Polars的比較
- ° 讀取數(shù)據(jù)
- ° 選擇和過(guò)濾數(shù)據(jù)
- ° 創(chuàng)建新列
- ° 分組和聚合
- ° 缺失數(shù)據(jù)
- 結(jié)論
本文的相關(guān)代碼可在Kaggle Notebook中找到,鏈接如下:https://www.kaggle.com/code/iamleonie/pandas-vs-polars
什么是Polars,為什么它比Pandas更快?
根據(jù)Polars的用戶指南[1],其目標(biāo)是“提供一個(gè)利用計(jì)算機(jī)上所有可用核心的閃電般快速的DataFrame庫(kù)。”
與Polars不同,Pandas不會(huì)在計(jì)算機(jī)核心之間進(jìn)行原生的并行處理。其他工具如Dask是建立在Pandas庫(kù)的基礎(chǔ)上嘗試進(jìn)行并行處理。相反,Polars是專為并行化而設(shè)計(jì)的,并從頭開始搭建。雖然它是用Rust編寫的,但Polars有一個(gè)Python包,這使得它成為Pandas的潛在替代方案。
Polars有兩種不同的API:急切API和惰性API。
急切的執(zhí)行方式類似于Pandas。這意味著直接運(yùn)行代碼,并立即返回結(jié)果。
另一方面,惰性執(zhí)行是在你需要結(jié)果時(shí)才運(yùn)行。由于它避免了運(yùn)行不必要的代碼,因此惰性執(zhí)行可以比急切執(zhí)行更高效。
對(duì)于惰性執(zhí)行,必須使用.lazy()方法開始操作。然后可以為你想做的任何事情編寫要執(zhí)行的代碼。最后需要運(yùn)行.collect()方法來(lái)顯示結(jié)果。
df.lazy()
.with_columns([(pl.col("col") * 10).alias("new_col")])
#...
.collect()
如果你不運(yùn)行.collect()方法,則不會(huì)立即執(zhí)行操作。相反,你將看到執(zhí)行圖。
圖片
基準(zhǔn)測(cè)試設(shè)置
本基準(zhǔn)測(cè)試設(shè)置使用了一個(gè)虛構(gòu)的數(shù)據(jù)集,其中包含每種數(shù)據(jù)類型的一列。為了減少時(shí)間上的噪聲以便進(jìn)行比較,該虛構(gòu)數(shù)據(jù)集包含400萬(wàn)行,幾乎有1GB大小,正如[2]中建議的那樣。
圖片
用于比較Pandas和Polars基準(zhǔn)測(cè)試的虛構(gòu)數(shù)據(jù)集頭部
在下文中,我們將使用%%timeit -n32 -r4對(duì)執(zhí)行進(jìn)行計(jì)時(shí)。
開始使用Polars
要設(shè)置Polars,只需使用pip安裝它。
pip install polars
之后,可以像使用Pandas一樣導(dǎo)入Polars Python包。
import polars as pl
import pandas as pd
現(xiàn)在,準(zhǔn)備工作都已完成。
Pandas和Polars的比較
Pandas和Polars(急切API)在語(yǔ)法方面看起來(lái)很相似,因?yàn)樗鼈兙哂泄蚕淼闹饕罱K:Series和DataFrames。
此外,Polars中的許多表達(dá)式也與Pandas表達(dá)式相似:
# 適用于Pandas和Polars的示例表達(dá)式
df.head() # 獲取前n行的數(shù)據(jù)
df.tail() # 獲取最后n行的數(shù)據(jù)
df.unique() # 獲取此表達(dá)式的唯一值。
但是,根據(jù)Polars用戶指南[1],“如果你的Polars代碼看起來(lái)像是Pandas代碼,它可能會(huì)運(yùn)行,但很可能比它應(yīng)該運(yùn)行的速度慢。”
本節(jié)將探討Polars包在語(yǔ)法和執(zhí)行時(shí)間方面與Pandas的主要不同之處:
- 讀取數(shù)據(jù)
- 選擇和過(guò)濾數(shù)據(jù)
- 創(chuàng)建新列
- 分組和聚合
- 缺失數(shù)據(jù)
讀取數(shù)據(jù)
在Polars中讀取CSV文件會(huì)感覺很熟悉,因?yàn)榭梢韵裨赑andas中一樣使用.read_csv()方法:
# Pandas
pd.read_csv('example.csv')
# Polars
pl.read_csv('example.csv')
在Pandas和Polars中讀取樣本數(shù)據(jù)集的結(jié)果執(zhí)行時(shí)間如下所示:
Pandas和Polars之間讀取時(shí)間的比較
對(duì)于我們的樣本數(shù)據(jù)集,使用Pandas讀取數(shù)據(jù)比使用Polars慢8倍左右。
選擇和過(guò)濾數(shù)據(jù)
Pandas和Polars之間的第一個(gè)主要區(qū)別是Polars不使用索引[1]。相反,每行都以其在DataFrame中的整數(shù)位置為索引[1]。
雖然相同的Pandas代碼也可以在Polars中運(yùn)行,但這并不是最佳實(shí)踐。在Polars中,應(yīng)該使用.select()方法來(lái)選擇數(shù)據(jù)。
# Pandas
df[['col1', 'col2']]
# 上述代碼也可以在Polars中運(yùn)行,
# 但在Polars中的正確方式是:
df.select(pl.col(['col1', 'col2']))
在Pandas和Polars中選擇數(shù)據(jù)的執(zhí)行時(shí)間結(jié)果顯示如下:
Pandas和Polars之間選擇時(shí)間的比較
對(duì)于我們的樣本數(shù)據(jù)集,使用Pandas選擇數(shù)據(jù)比使用Polars慢15倍左右(約為70.3微秒)。
下面比較了在文檔中建議的Polars操作語(yǔ)法(使用.select(),左側(cè))和Pandas語(yǔ)法(使用df[['col1','col2']],右側(cè))。令人意外的是,Pandas語(yǔ)法比建議的.select()方法快得多。
圖片
Polars和具有Pandas語(yǔ)法的Polars之間選擇時(shí)間的比較(df[[‘col1’,‘col2’]])
在Pandas中,你可以使用.query()方法過(guò)濾數(shù)據(jù),但在Polars中需要使用.filter()方法。
# Pandas
df.query('col1 > 5')
# Polars
df.filter(pl.col('col') > 5)
在Pandas和Polars中過(guò)濾數(shù)據(jù)的執(zhí)行時(shí)間結(jié)果顯示如下:
Pandas和Polars之間過(guò)濾時(shí)間的比較
對(duì)于我們的樣本數(shù)據(jù)集,使用Pandas和Polars過(guò)濾數(shù)據(jù)的時(shí)間大致相同。
與Pandas不同,Polars可以在.select()和.filter()中并行運(yùn)行操作。
創(chuàng)建新列
在Polars中創(chuàng)建新列也與在Pandas中使用的方式有所不同。在Polars中,需要使用.with_column()或.with_columns()方法,具體取決于你要?jiǎng)?chuàng)建多少列。
# Pandas
df_pd["new_col"] = df_pd["col"] * 10
# Polars
df.with_columns([(pl.col("col") * 10).alias("new_col")])
# 多列的Polars
# df.with_columns([(pl.col("col") * 10).alias("new_col"), ...])
在Pandas和Polars中創(chuàng)建一個(gè)新列的結(jié)果執(zhí)行時(shí)間如下:
Pandas和Polars之間創(chuàng)建新列的時(shí)間比較
對(duì)于我們的樣本數(shù)據(jù)集,在Polars中創(chuàng)建新列需要比Pandas長(zhǎng)兩倍左右的時(shí)間。
分組和聚合
在Pandas和Polars中,分組和聚合在語(yǔ)法上略有不同,但兩者都使用.groupby()和.agg()方法。
# Pandas
df_pd.groupby('col1')['col2'].agg('mean')
# Polars
# df.groupby('col1').agg([pl.col('col2').mean()]) # 建議使用的Polars方法
df.groupby('col1').agg([pl.mean('col2')]) # 簡(jiǎn)短的語(yǔ)法
在Pandas和Polars中對(duì)數(shù)據(jù)進(jìn)行分組和聚合的執(zhí)行時(shí)間如下:
Pandas和Polars之間聚合時(shí)間的比較
對(duì)于我們的樣本數(shù)據(jù)集,使用Pandas聚合數(shù)據(jù)需要比使用Polars長(zhǎng)兩倍左右的時(shí)間。
缺失數(shù)據(jù)
Pandas和Polars之間的另一個(gè)主要區(qū)別是,Pandas使用NaN值表示缺失值,而Polars使用null[1]。
圖片
Pandas和Polars如何在DataFrames中表示缺失值
因此,應(yīng)該使用Polars的.fill_null()方法,而不是Pandas中的.fillna()方法。
# Pandas
df['col2'].fillna(-999)
# Polars
# df_pd.with_column(pl.col('col2').fill_null(pl.lit(-999))) # 建議使用的Polars方法
df_pd.with_column(pl.col('col2').fill_null(-999)) # 簡(jiǎn)短的語(yǔ)法
總結(jié)
那么,Polars是否比Pandas更好?Polars是否會(huì)取代Pandas?
Polars相對(duì)于Pandas的主要優(yōu)勢(shì)是速度。如果你需要在大型數(shù)據(jù)集上進(jìn)行大量數(shù)據(jù)處理,那么你肯定應(yīng)該嘗試Polars。
Polars相對(duì)于Pandas的主要優(yōu)勢(shì)是速度。
但是,正如本文所示,如果要從Pandas切換到Polars,則需要學(xué)習(xí)新的Polars語(yǔ)法。此外,你已經(jīng)看到,對(duì)于相同的操作,Polars代碼通常要比Pandas代碼長(zhǎng)。并且,Polars并沒有涵蓋Pandas的所有功能,例如用于數(shù)據(jù)探索等。
因此,如果你需要使用Pandas提供的所有功能,則可能需要繼續(xù)使用Pandas。
Polars的代碼通常比Pandas的代碼長(zhǎng)。
參考文獻(xiàn)
[1] Polars (2023): User Guide (accessed 8. January 2023)
【網(wǎng)址】:https://pola-rs.github.io/polars-book/user-guide/
[2] “Stackoverflow”, “What are the differences between feather and parquet?”. stackoverflow.com.(accessed July 25, 2022)
【網(wǎng)址】:https://stackoverflow.com/questions/48083405/what-are-the-differences-between-feather-and-parquet