Son zamanlarda pandas a göre ciddi performans avantajları olan Polars kütüphanesinin popülerliği giderek artıyor. Ancak DuckDB karşısında nasıl bir benchmark çıkaracağını anlayabilmek için küçük bir karşılaştırma yapmak gerekir diye düşünüyorum.
İlk olarak kendi kişisel bilgisayarımda yaptığım bu benchmark da sürelerden ziyade iki sürenin arasındaki orana bakmanın daha sağlıklı olacağını düşünüyorum.
Öncelikle nasıl bir karşılaştırma tasarladığımı anlatarak başlayayım. Kaagle da aşağıda linkini verdiğim yaklaşık 113 milyon satırlık bir CSV dosyası ile çalışmaya karar verdim. Dosyanın boyutu da yaklaşık olarak 4.5 GB. İlk olarak bu CSV dosyasını okumayı sonrasında belli bir kolona göre gruplamayı ve sonuçların karşılaştırması sağlıklı olabilsin diye sıralamayı hedefledim. Polars da grupladıktan sonra sıralamak yerine önce sıralayıp sonra gruplamak gerektiğinden tam SQL yazmak gibi kodlayamadım. Ancak diğer yandan varitabanlalarında gruplama işlemleri otomatik bir sort içerdiğinden çalışma planları açısından birbirlerinden bir farkları olduğunu düşünmüyorum.
Bir diğer konu da, kullandığım CSV dosyasında başlık satırı olmadığı için Polars ve DuckDB birbirinden farklı kolon isimlendirme metodolojileri kullandırlar. Bu yüzden kodun detayında grupladığım kolon adlarının birbirinden farklı olduğunu göreceksiniz.
Kodumuz aşağıdaki gibidir.
import duckdb as dbimport polars as plimport datetimedef polars_job(): now=datetime.datetime.now() print("Polars Timing (read text data)") print("===================================") print(f'Start time: {now.strftime("%H:%M:%S")}') df = pl.read_csv("custom_1988_2020.csv",has_header=False,columns = [ 'column_1', 'column_2' ,'column_3','column_4','column_5','column_6','column_7','column_8']) print(df.sort("column_3").group_by("column_3").len().head(10)) now = datetime.datetime.now() print(f'End time: {now.strftime("%H:%M:%S")}') del df print("***********************************")def duckdb_job(): conn = db.connect() now=datetime.datetime.now() print("Duckdb Timing (read text data)") print("===================================") print(f'Start time: {now.strftime("%H:%M:%S")}') df = conn.execute("""select "column2",count(*) from read_csv('custom_1988_2020.csv', AUTO_DETECT=TRUE) group by "column2" order by "column2" """).df() print(df.head(10)) now = datetime.datetime.now() print(f'End time: {now.strftime("%H:%M:%S")}') conn.close() print("***********************************")if __name__ == '__main__': print(f"Polars version: {pl.__version__}") print(f"DuckDB version: {db.__version__}") print("***********************************") polars_job() duckdb_job()import duckdb as dbimport polars as plimport datetimedef polars_job(): now=datetime.datetime.now() print("Polars Timing (read text data)") print("===================================") print(f'Start time: {now.strftime("%H:%M:%S")}') df = pl.read_csv("custom_1988_2020.csv",has_header=False,columns = [ 'column_1', 'column_2' ,'column_3','column_4','column_5','column_6','column_7','column_8']) print(df.sort("column_3").group_by("column_3").len().head(10)) now = datetime.datetime.now() print(f'End time: {now.strftime("%H:%M:%S")}') del df print("***********************************")def duckdb_job(): conn = db.connect() now=datetime.datetime.now() print("Duckdb Timing (read text data)") print("===================================") print(f'Start time: {now.strftime("%H:%M:%S")}') df = conn.execute("""select "column2",count(*) from read_csv('custom_1988_2020.csv', AUTO_DETECT=TRUE) group by "column2" order by "column2" """).df() print(df.head(10)) now = datetime.datetime.now() print(f'End time: {now.strftime("%H:%M:%S")}') conn.close() print("***********************************")if __name__ == '__main__': print(f"Polars version: {pl.__version__}") print(f"DuckDB version: {db.__version__}") print("***********************************") polars_job() duckdb_job()import duckdb as dbimport polars as plimport datetimedef polars_job(): now=datetime.datetime.now() print("Polars Timing (read text data)") print("===================================") print(f'Start time: {now.strftime("%H:%M:%S")}') df = pl.read_csv("custom_1988_2020.csv",has_header=False,columns = [ 'column_1', 'column_2' ,'column_3','column_4','column_5','column_6','column_7','column_8']) print(df.sort("column_3").group_by("column_3").len().head(10)) now = datetime.datetime.now() print(f'End time: {now.strftime("%H:%M:%S")}') del df print("***********************************")def duckdb_job(): conn = db.connect() now=datetime.datetime.now() print("Duckdb Timing (read text data)") print("===================================") print(f'Start time: {now.strftime("%H:%M:%S")}') df = conn.execute("""select "column2",count(*) from read_csv('custom_1988_2020.csv', AUTO_DETECT=TRUE) group by "column2" order by "column2" """).df() print(df.head(10)) now = datetime.datetime.now() print(f'End time: {now.strftime("%H:%M:%S")}') conn.close() print("***********************************")if __name__ == '__main__': print(f"Polars version: {pl.__version__}") print(f"DuckDB version: {db.__version__}") print("***********************************") polars_job() duckdb_job()Polars 0.20.6 ve DuckDB 0.9.2 versiyonları ile ilerledik.

Polars gerçekten de olası bir pandas performansına göre çok daha iyi performans verdi. Ancak yine de 130 milyon satırı belli bir kolona göre gruplaması ve sıralaması 1 dakikadan biraz uzun sürdü. Pandas a göre API bazı farklılıklar gösteriyor ama pandas bilenler için alışması çok kolay olacaktır.
Bir okuyucunun uyarısı ile aşağıdaki değişikliği yaptım ve polars tarafında sürenin 40 sn civarına düştüğünü gördüm. ancak yine de çok yorumumu çok değiştirmiyor bu değişiklik
- print(df.sort("column_3").group_by("column_3").len().head(10))+ print(df.group_by("column_3").len().sort("column_3").head(10))- print(df.sort("column_3").group_by("column_3").len().head(10))+ print(df.group_by("column_3").len().sort("column_3").head(10))- print(df.sort("column_3").group_by("column_3").len().head(10))+ print(df.group_by("column_3").len().sort("column_3").head(10))
DuckDB ise aynı işlemi yaklaşık 12 saniyede yaptı. Ayrıca Polars için kodlama yapıldığı için DuckDB nin SQL dilinde sorgulayabiliyor olması benimin için çok daha pratik oldu. Özellikle SQL bilgisi olan kişiler için DuckDB nin hem olduça hızlı hem de çok daha pratik bir alternatif olduğu kanaatindeyim.

Siz de yukarıdaki kodu ve aşağıdaki bağlantıda verdiğim veri setini kullanarak kendi ortamınızda deneyebilirsiniz.
https://www.kaggle.com/datasets/zanjibar/100-million-data-csv/data