Polars vs Pandas: Tăng tốc xử lý DataFrame gấp 10 lần với sức mạnh từ Rust

Python tutorial - IT technology blog
Python tutorial - IT technology blog

Vì sao Pandas thường xuyên ‘hụt hơi’ trước dữ liệu lớn?

Nếu làm Data bằng Python, chắc hẳn bạn đã quá quen với lỗi Memory Error. Pandas rất tuyệt vời và linh hoạt, nhưng nó mang trong mình một hạn chế lịch sử. Nó được thiết kế từ thời kỳ mà CPU đa nhân và những tập dữ liệu hàng chục GB chưa phổ biến như hiện nay.

Điểm yếu chí mạng của Pandas là cơ chế xử lý đơn luồng (single-threaded). Hãy tưởng tượng bạn có một con CPU 8 nhân, nhưng khi chạy lệnh lọc trên DataFrame 5GB, chỉ có đúng 1 nhân làm việc. 7 nhân còn lại hoàn toàn ‘ngồi chơi xơi nước’. Chưa kể, Pandas thường tốn lượng RAM gấp 2-3 lần dung lượng thực tế của file dữ liệu.

Mình từng gặp trường hợp máy 16GB RAM treo cứng khi cố load file CSV 6GB bằng Pandas. Đó là lúc mình nhận ra cần một giải pháp khác hiện đại hơn. Polars xuất hiện để giải quyết đúng bài toán này. Được viết bằng Rust, Polars tận dụng tối đa kiến trúc đa nhân của CPU hiện đại. Nó sử dụng định dạng Apache Arrow để tối ưu hóa việc truy xuất và quản lý bộ nhớ cực kỳ hiệu quả.

Cài đặt Polars cho dự án Python

Cài đặt Polars rất nhanh gọn vì nó đã có sẵn trên PyPI. Bạn không cần cài thêm Rust để sử dụng.

pip install polars

Để làm việc mượt mà với các định dạng như Excel hay Parquet, bạn nên cài thêm các thư viện hỗ trợ này:

pip install connectorx fsspec openpyxl

Kinh nghiệm của mình là luôn dùng môi trường ảo (venv) sạch sẽ. Polars cập nhật tính năng mới rất nhanh, việc tách biệt môi trường giúp bạn tránh được các lỗi xung đột phiên bản không đáng có.

Khai thác sức mạnh Polars: Đừng bê nguyên tư duy Pandas

Để thấy Polars nhanh thế nào, bạn cần thay đổi cách tiếp cận. Nếu chỉ dùng Polars như một bản sao của Pandas, bạn đang lãng phí 80% sức mạnh của nó.

1. Chuyển từ Eager sang Lazy Evaluation

Trong Pandas, mọi lệnh bạn viết đều chạy ngay lập tức (Eager). Với Polars, sức mạnh thực sự nằm ở LazyFrame. Thay vì nạp toàn bộ dữ liệu vào RAM, Polars sẽ chỉ quét cấu trúc file và chờ lệnh của bạn.

import polars as pl

# Tiếp cận theo kiểu Lazy
q = (
    pl.scan_csv("log_he_thong_lon.csv")
    .filter(pl.col("status") == "error")
    .select([
        pl.col("timestamp"),
        pl.col("user_id")
    ])
)

# Chỉ khi gọi collect(), tính toán mới thực sự bắt đầu
df = q.collect()

Cơ chế này nhanh vì Polars sở hữu bộ tối ưu hóa truy vấn (Query Optimizer). Nếu bạn lọc dữ liệu trước khi chọn cột, nó sẽ chỉ đọc đúng những dòng/cột cần thiết từ ổ cứng. Khi xử lý tập dữ liệu khoảng 100 triệu dòng, cách này giúp mình tiết kiệm tới 70% RAM so với thông thường.

2. Dùng Expression API thay cho Indexing

Pandas phụ thuộc nhiều vào .loc.iloc, vốn khá dễ nhầm lẫn. Polars khuyến khích dùng Expressions – cách viết code cực kỳ trong sáng và dễ bảo trì.

# Pandas: df[df['price'] > 500]
# Polars:
df.filter(pl.col("price") > 500)

# GroupBy trong Polars cực kỳ mạnh mẽ
result = df.group_by("region").agg([
    pl.col("sales").sum().alias("total_revenue"),
    pl.col("customer_id").n_unique().alias("unique_users")
])

Một điểm cộng lớn là Polars tự động chạy song song các phép tính trong agg. Bạn có 10 phép tính tổng, đếm hay trung bình? Polars sẽ đẩy chúng vào các luồng khác nhau để xử lý đồng thời trên các nhân CPU.

3. Kiểm soát kiểu dữ liệu nghiêm ngặt

Polars áp dụng Strict typing, giúp bạn tránh khỏi những lỗi ‘ngớ ngẩn’ khi dữ liệu lẫn lộn giữa chuỗi và số. Bạn có thể định nghĩa cách xử lý giá trị thiếu ngay khi đọc file:

df = pl.read_csv("data.csv", null_values=["N/A", "?", "NULL"])

Kiểm tra hiệu năng và theo dõi tài nguyên

Đừng tin vào lời quảng cáo, hãy tin vào con số. Mình thường dùng module timeit để so sánh trực tiếp tốc độ giữa hai thư viện.

Trong một dự án thực tế, mình chuyển script xử lý 10 triệu dòng từ Pandas sang Polars. Kết quả thật kinh ngạc: thời gian thực thi giảm từ 45 giây xuống còn đúng 3.5 giây. Thay vì ngồi pha cafe chờ script chạy, giờ đây kết quả hiện ra gần như tức thì.

Nếu dùng Linux, bạn hãy mở htop khi Polars đang chạy collect(). Bạn sẽ thấy tất cả các vạch CPU nhảy vọt lên 100%. Đây là minh chứng rõ nhất cho việc Polars đang vắt kiệt sức mạnh phần cứng để phục vụ bạn.

Nếu dữ liệu quá lớn so với RAM, hãy dùng collect(streaming=True). Tính năng này cho phép Polars xử lý dữ liệu theo từng đợt nhỏ (batch). Đây là thứ mà Pandas không thể làm được nếu thiếu các thư viện bên thứ ba như Dask hay Ray.

Tóm lại, nếu dataset của bạn vượt ngưỡng vài trăm MB hoặc bạn mệt mỏi vì tốc độ ‘rùa bò’ của Pandas, hãy thử Polars ngay. Học lại cú pháp một chút là cái giá quá rẻ để đổi lấy hiệu năng vượt trội này.

Share: