Polars Python 2026: Hướng Dẫn DataFrame Siêu Tốc Từ Cơ Bản Đến Nâng Cao

Hướng dẫn chi tiết Polars — thư viện DataFrame viết bằng Rust, nhanh hơn Pandas từ 5-100 lần. Từ expressions, lazy evaluation, streaming engine đến GPU acceleration với ví dụ code thực tế.

Giới Thiệu Polars: "Kẻ Thách Thức" Pandas Trong Thế Giới DataFrame

Nếu bạn đã đọc bài viết trước của chúng tôi về Pandas 3.0 và những cải tiến đáng kể của nó, có lẽ bạn đang tự hỏi: "Pandas đã tốt lên rồi, vậy cần gì phải thử cái khác?" Câu trả lời nằm ở một thư viện mang tên Polars — và thành thật mà nói, nó không chỉ "khác" mà còn nhanh hơn đáng kinh ngạc.

Polars là thư viện DataFrame mã nguồn mở, được viết bằng Rust và thiết kế từ đầu để tận dụng tối đa phần cứng hiện đại. Đây không phải bản sao của Pandas, cũng không phải wrapper gì cả — Polars là một kiến trúc hoàn toàn mới. Triết lý thiết kế của nó khác biệt rõ rệt: lazy evaluation, xử lý song song tự động, và tối ưu truy vấn giống như một cơ sở dữ liệu SQL.

Tính đến tháng 2 năm 2026, Polars đã đạt phiên bản 1.38 với hơn 35.000 sao trên GitHub. Dự án được hậu thuẫn bởi vòng gọi vốn Series A trị giá 18 triệu Euro từ Accel, và đã cho ra mắt Polars Cloud — nền tảng đám mây cho xử lý dữ liệu quy mô lớn. Các công ty lớn như Decathlon đã chuyển từ Spark sang Polars để tối ưu pipeline dữ liệu.

Vậy nên, hãy cùng đi từ những khái niệm cơ bản nhất cho đến các tính năng nâng cao nhé. Mình sẽ kèm theo ví dụ mã nguồn thực tế và so sánh hiệu suất với Pandas. Dù bạn là người mới hay đã có kinh nghiệm, bài viết này sẽ giúp bạn hiểu rõ khi nào và tại sao nên dùng Polars.

1. Cài Đặt Và Bắt Đầu Với Polars

1.1 Cài Đặt

Việc cài đặt Polars cực kỳ đơn giản — chỉ một dòng pip thôi:

# Cài đặt cơ bản
pip install polars

# Cài đặt với tất cả tính năng bổ sung
pip install "polars[all]"

# Cài đặt với hỗ trợ GPU (yêu cầu NVIDIA GPU)
pip install "polars[gpu]"

Gói polars[all] bao gồm các phụ thuộc bổ sung cho đọc/ghi nhiều định dạng file (Excel, Delta Lake, IPC...), tích hợp với NumPy, và các tính năng nâng cao khác. Nói chung nếu bạn không chắc mình cần gì, cứ cài polars[all] cho lành.

1.2 Tạo DataFrame Đầu Tiên

Cú pháp tạo DataFrame trong Polars khá giống Pandas, nên bạn sẽ thấy quen thuộc ngay:

import polars as pl

# Tạo DataFrame từ dictionary
df = pl.DataFrame({
    "ten": ["Nguyễn Văn A", "Trần Thị B", "Lê Văn C", "Phạm Thị D"],
    "tuoi": [28, 34, 22, 45],
    "thanh_pho": ["Hà Nội", "TP.HCM", "Đà Nẵng", "Hà Nội"],
    "luong": [15_000_000, 22_000_000, 12_000_000, 35_000_000]
})

print(df)
# shape: (4, 4)
# ┌──────────────┬──────┬───────────┬────────────┐
# │ ten          ┆ tuoi ┆ thanh_pho ┆ luong      │
# │ ---          ┆ ---  ┆ ---       ┆ ---        │
# │ str          ┆ i64  ┆ str       ┆ i64        │
# ╞══════════════╪══════╪═══════════╪════════════╡
# │ Nguyễn Văn A ┆ 28   ┆ Hà Nội   ┆ 15000000   │
# │ Trần Thị B   ┆ 34   ┆ TP.HCM   ┆ 22000000   │
# │ Lê Văn C     ┆ 22   ┆ Đà Nẵng  ┆ 12000000   │
# │ Phạm Thị D   ┆ 45   ┆ Hà Nội   ┆ 35000000   │
# └──────────────┴──────┴───────────┴────────────┘

Bạn có nhận ra điều khác biệt đầu tiên không? Polars hiển thị bảng với kiểu dữ liệu rõ ràng (str, i64) và sử dụng ký hiệu để phân cách cột. Trực quan hơn nhiều so với output mặc định của Pandas — mình thích điểm này lắm.

1.3 Đọc Dữ Liệu Từ File

Polars hỗ trợ đọc nhiều định dạng file phổ biến:

import polars as pl

# Đọc file CSV
df = pl.read_csv("du_lieu_ban_hang.csv")

# Đọc file Parquet — định dạng được khuyến nghị
df = pl.read_parquet("du_lieu_ban_hang.parquet")

# Đọc file Excel
df = pl.read_excel("du_lieu_ban_hang.xlsx")

# Đọc file JSON
df = pl.read_json("du_lieu_ban_hang.json")

# Đọc từ nhiều file Parquet cùng lúc
df = pl.read_parquet("data/*.parquet")

Và đây là điểm gây ấn tượng: Polars đọc file CSV nhanh hơn Pandas khoảng 5 lần nhờ xử lý đa luồng và phân tích cú pháp tối ưu bằng Rust. Lần đầu thấy kết quả benchmark này, mình cũng hơi ngỡ ngàng.

2. Expressions Và Contexts — Trái Tim Của Polars

Đây là phần quan trọng nhất khi học Polars. Thật sự mà nói, nếu bạn hiểu được expressionscontexts, bạn sẽ nắm được khoảng 80% cốt lõi của thư viện này.

2.1 Expression Là Gì?

Trong Polars, một expression là một đại diện lười (lazy representation) cho một phép biến đổi dữ liệu. Expression không thực thi ngay — nó chỉ mô tả bạn muốn làm gì. Điều này cho phép Polars tối ưu hóa trước khi thực sự chạy.

import polars as pl

# Đây là các expressions — chưa thực thi gì cả
expr1 = pl.col("tuoi")                    # Tham chiếu cột "tuoi"
expr2 = pl.col("luong") * 1.1             # Tăng lương 10%
expr3 = pl.col("ten").str.to_uppercase()  # Chuyển tên thành chữ hoa
expr4 = pl.col("tuoi").mean()             # Tính trung bình tuổi

Hãy hình dung expression giống như một "công thức" — bạn viết công thức trước, và Polars sẽ thực thi nó trong ngữ cảnh phù hợp. Khá là gọn gàng phải không?

2.2 Contexts — Ngữ Cảnh Thực Thi

Context là môi trường mà expression được thực thi. Polars có ba context chính:

Context 1: select — Chọn và biến đổi cột

import polars as pl

df = pl.DataFrame({
    "ten": ["Nguyễn Văn A", "Trần Thị B", "Lê Văn C"],
    "tuoi": [28, 34, 22],
    "luong": [15_000_000, 22_000_000, 12_000_000]
})

# select: chỉ giữ lại các cột được chọn
ket_qua = df.select(
    pl.col("ten"),
    pl.col("luong") / 1_000_000  # Chuyển sang đơn vị triệu
)

print(ket_qua)
# shape: (3, 2)
# ┌──────────────┬───────┐
# │ ten          ┆ luong │
# │ ---          ┆ ---   │
# │ str          ┆ f64   │
# ╞══════════════╪═══════╡
# │ Nguyễn Văn A ┆ 15.0  │
# │ Trần Thị B   ┆ 22.0  │
# │ Lê Văn C     ┆ 12.0  │
# └──────────────┴───────┘

Context 2: with_columns — Thêm hoặc sửa cột

# with_columns: giữ tất cả cột cũ + thêm/sửa cột mới
ket_qua = df.with_columns(
    (pl.col("luong") * 1.1).alias("luong_moi"),
    (pl.col("tuoi") + 1).alias("tuoi_nam_sau"),
    pl.col("ten").str.to_uppercase().alias("ten_in_hoa")
)

print(ket_qua)
# shape: (3, 6)
# ┌──────────────┬──────┬────────────┬────────────┬──────────────┬──────────────┐
# │ ten          ┆ tuoi ┆ luong      ┆ luong_moi  ┆ tuoi_nam_sau ┆ ten_in_hoa   │
# │ ---          ┆ ---  ┆ ---        ┆ ---        ┆ ---          ┆ ---          │
# │ str          ┆ i64  ┆ i64        ┆ f64        ┆ i64          ┆ str          │
# ╞══════════════╪══════╪════════════╪════════════╪══════════════╪══════════════╡
# │ Nguyễn Văn A ┆ 28   ┆ 15000000  ┆ 16500000.0 ┆ 29           ┆ NGUYỄN VĂN A│
# │ Trần Thị B   ┆ 34   ┆ 22000000  ┆ 24200000.0 ┆ 35           ┆ TRẦN THỊ B  │
# │ Lê Văn C     ┆ 22   ┆ 12000000  ┆ 13200000.0 ┆ 23           ┆ LÊ VĂN C    │
# └──────────────┴──────┴────────────┴────────────┴──────────────┴──────────────┘

Context 3: group_by — Nhóm và tổng hợp

df = pl.DataFrame({
    "ten": ["A", "B", "C", "D", "E", "F"],
    "phong_ban": ["IT", "HR", "IT", "HR", "IT", "Marketing"],
    "luong": [15, 22, 18, 20, 25, 17]
})

ket_qua = df.group_by("phong_ban").agg(
    pl.col("luong").mean().alias("luong_tb"),
    pl.col("luong").max().alias("luong_cao_nhat"),
    pl.col("ten").count().alias("so_nhan_vien")
)

print(ket_qua)
# shape: (3, 4)
# ┌───────────┬──────────┬────────────────┬──────────────┐
# │ phong_ban ┆ luong_tb ┆ luong_cao_nhat ┆ so_nhan_vien │
# │ ---       ┆ ---      ┆ ---            ┆ ---          │
# │ str       ┆ f64      ┆ i64            ┆ u32          │
# ╞═══════════╪══════════╪════════════════╪══════════════╡
# │ IT        ┆ 19.33    ┆ 25             ┆ 3            │
# │ HR        ┆ 21.0     ┆ 22             ┆ 2            │
# │ Marketing ┆ 17.0     ┆ 17             ┆ 1            │
# └───────────┴──────────┴────────────────┴──────────────┘

2.3 Chuỗi Expression (Method Chaining)

Một trong những điểm mình thích nhất ở Polars là khả năng xâu chuỗi các expression lại với nhau. Nó tạo nên các pipeline xử lý dữ liệu rõ ràng và dễ đọc — kiểu viết code mà nhìn vào là hiểu ngay luồng xử lý:

import polars as pl

df = pl.DataFrame({
    "san_pham": ["Laptop", "Điện thoại", "Tablet", "Laptop", "Điện thoại"],
    "doanh_thu": [25_000_000, 15_000_000, 8_000_000, 30_000_000, 18_000_000],
    "so_luong": [2, 5, 3, 1, 4],
    "ngay_ban": ["2026-01-15", "2026-01-16", "2026-01-15", "2026-01-17", "2026-01-17"]
})

# Chuỗi xử lý phức tạp — rõ ràng và dễ đọc
ket_qua = (
    df
    .with_columns(
        pl.col("ngay_ban").str.to_date("%Y-%m-%d")
    )
    .filter(pl.col("doanh_thu") > 10_000_000)
    .group_by("san_pham")
    .agg(
        pl.col("doanh_thu").sum().alias("tong_doanh_thu"),
        pl.col("so_luong").sum().alias("tong_so_luong"),
        (pl.col("doanh_thu").sum() / pl.col("so_luong").sum()).alias("gia_tb_moi_sp")
    )
    .sort("tong_doanh_thu", descending=True)
)

print(ket_qua)

So với Pandas thì cú pháp này gọn gàng hơn đáng kể. Không cần reset_index(), không cần rename() sau khi group, và logic xử lý đọc từ trên xuống dưới rất tự nhiên. Ai đã từng viết những chuỗi .groupby().agg().reset_index().rename() trong Pandas chắc sẽ thấm lắm.

3. Lazy Evaluation — Sức Mạnh Thực Sự Của Polars

3.1 Eager vs Lazy Mode

Polars có hai chế độ thực thi, và hiểu sự khác biệt giữa chúng là rất quan trọng:

  • Eager mode: Thực thi ngay lập tức, giống Pandas. Phù hợp cho khám phá dữ liệu tương tác.
  • Lazy mode: Xây dựng kế hoạch truy vấn trước, tối ưu hóa, rồi mới thực thi. Đây là chế độ được khuyến nghị cho production.
import polars as pl

# --- EAGER MODE ---
# Mỗi dòng thực thi ngay lập tức
df = pl.read_csv("du_lieu.csv")
df_filtered = df.filter(pl.col("tuoi") > 25)
df_selected = df_filtered.select("ten", "luong")

# --- LAZY MODE ---
# Xây dựng kế hoạch, chưa thực thi gì
lazy_df = pl.scan_csv("du_lieu.csv")    # Chú ý: scan thay vì read
lazy_result = (
    lazy_df
    .filter(pl.col("tuoi") > 25)
    .select("ten", "luong")
)

# Xem kế hoạch truy vấn
print(lazy_result.explain())

# Thực thi khi sẵn sàng
df_result = lazy_result.collect()

Sự khác biệt giữa read_scan_ là mấu chốt ở đây. scan_ không đọc dữ liệu ngay — nó chỉ lập kế hoạch, và bạn gọi .collect() khi thật sự cần kết quả.

3.2 Tại Sao Lazy Mode Nhanh Hơn?

Khi bạn dùng lazy mode, Polars sẽ tự động áp dụng nhiều kỹ thuật tối ưu khá thú vị:

  • Predicate pushdown: Bộ lọc được đẩy xuống gần nguồn dữ liệu nhất có thể. Ví dụ: nếu bạn đọc file Parquet rồi lọc, Polars sẽ chỉ đọc các row cần thiết thay vì tải toàn bộ file.
  • Projection pushdown: Chỉ đọc các cột bạn thực sự dùng. Nếu file có 100 cột nhưng bạn chỉ select 3, Polars sẽ bỏ qua 97 cột còn lại ngay từ bước đọc file.
  • Query parallelization: Tự động chia công việc ra nhiều luồng CPU.
  • Common subexpression elimination: Nếu cùng một expression xuất hiện nhiều lần, Polars tính một lần rồi tái sử dụng kết quả.
import polars as pl

# Ví dụ: Polars tự động tối ưu truy vấn
ket_qua = (
    pl.scan_parquet("du_lieu_lon.parquet")  # File 10GB, 200 cột
    .filter(pl.col("nam") == 2026)           # Predicate pushdown: chỉ đọc rows năm 2026
    .select("ten", "doanh_thu", "phong_ban") # Projection pushdown: chỉ đọc 3 cột
    .group_by("phong_ban")
    .agg(pl.col("doanh_thu").sum())
    .sort("doanh_thu", descending=True)
    .collect()
)

# Kết quả: thay vì đọc 10GB, Polars có thể chỉ đọc vài trăm MB
# Tốc độ nhanh hơn hàng chục lần so với đọc toàn bộ file

Cái hay là tất cả những tối ưu này đều xảy ra tự động. Bạn không cần phải lo nghĩ gì — cứ viết query logic rõ ràng là Polars sẽ lo phần còn lại.

3.3 Xem Kế Hoạch Truy Vấn

Một tính năng khá tuyệt vời (mà mình hay dùng) là bạn có thể xem kế hoạch truy vấn trước và sau khi tối ưu:

import polars as pl

query = (
    pl.scan_csv("nhan_vien.csv")
    .filter(pl.col("luong") > 20_000_000)
    .select("ten", "phong_ban", "luong")
    .sort("luong", descending=True)
)

# Xem kế hoạch đã tối ưu
print(query.explain())
# Output sẽ cho thấy filter và projection đã được pushdown

# Xem kế hoạch chưa tối ưu để so sánh
print(query.explain(optimized=False))

Việc xem trước kế hoạch truy vấn cực kỳ hữu ích khi debug hiệu suất. Bạn sẽ biết chính xác Polars đang làm gì "phía sau hậu trường", và đôi khi kết quả tối ưu sẽ khiến bạn bất ngờ.

4. Streaming Engine — Xử Lý Dữ Liệu Vượt Quá RAM

4.1 Vấn Đề Với Dữ Liệu Lớn

Khi dữ liệu lớn hơn RAM thì sao? Cả Pandas lẫn Polars ở chế độ eager đều gặp khó. Pandas thường cần RAM gấp 5-10 lần kích thước dữ liệu, Polars cần 2-4 lần. Nhưng nếu file 50GB mà bạn chỉ có 16GB RAM thì... vấn đề lớn rồi.

Đây là lúc Streaming Engine của Polars tỏa sáng.

4.2 Sử Dụng Streaming

import polars as pl

# Xử lý file lớn hơn RAM bằng streaming
ket_qua = (
    pl.scan_parquet("du_lieu_50gb.parquet")
    .filter(pl.col("khu_vuc") == "Mien_Bac")
    .group_by("thanh_pho")
    .agg(
        pl.col("doanh_thu").sum().alias("tong_doanh_thu"),
        pl.col("ma_don_hang").n_unique().alias("so_don_hang")
    )
    .sort("tong_doanh_thu", descending=True)
    .collect(engine="streaming")  # Kích hoạt streaming engine
)

print(ket_qua)

Với engine="streaming", Polars xử lý dữ liệu từng phần (chunk) thay vì tải toàn bộ vào RAM. Nói đơn giản là bạn có thể xử lý dataset hàng trăm GB trên máy tính thông thường — nghe hơi "ảo" nhưng nó hoạt động thật.

4.3 Streaming Với Nhiều File

import polars as pl

# Đọc và xử lý nhiều file Parquet cùng lúc
ket_qua = (
    pl.scan_parquet("data/nam_2025/**/*.parquet")
    .filter(
        (pl.col("trang_thai") == "hoan_thanh") &
        (pl.col("gia_tri") > 1_000_000)
    )
    .group_by("danh_muc")
    .agg(
        pl.col("gia_tri").sum().alias("tong_gia_tri"),
        pl.col("gia_tri").mean().alias("gia_tri_tb"),
        pl.len().alias("so_giao_dich")
    )
    .collect(engine="streaming")
)

print(ket_qua)

Polars tự động phát hiện và xử lý song song nhiều file, kết hợp với predicate pushdown để chỉ đọc dữ liệu cần thiết từ mỗi file. Rất tiện khi bạn có dữ liệu được phân partition theo ngày hoặc tháng.

5. So Sánh Hiệu Suất: Polars vs Pandas

5.1 Benchmark Thực Tế

OK, nói nhanh thì không ai tin. Hãy cùng xem benchmark thực tế trên một dataset 10 triệu dòng, chạy trên máy 8 nhân CPU, 16GB RAM:

import polars as pl
import pandas as pd
import numpy as np
import time

# Tạo dataset test: 10 triệu dòng
n = 10_000_000
np.random.seed(42)

data = {
    "id": np.arange(n),
    "nhom": np.random.choice(["A", "B", "C", "D", "E"], n),
    "gia_tri_1": np.random.randn(n),
    "gia_tri_2": np.random.randn(n),
    "thanh_pho": np.random.choice(
        ["Ha Noi", "HCM", "Da Nang", "Hue", "Can Tho",
         "Hai Phong", "Nha Trang", "Vung Tau", "Da Lat", "Quang Ninh"], n
    )
}

# ===== PANDAS =====
df_pd = pd.DataFrame(data)

start = time.time()
result_pd = (
    df_pd
    .query("gia_tri_1 > 0")
    .groupby("nhom")
    .agg({"gia_tri_1": "mean", "gia_tri_2": "sum", "id": "count"})
)
time_pandas = time.time() - start
print(f"Pandas: {time_pandas:.3f}s")

# ===== POLARS =====
df_pl = pl.DataFrame(data)

start = time.time()
result_pl = (
    df_pl.lazy()
    .filter(pl.col("gia_tri_1") > 0)
    .group_by("nhom")
    .agg(
        pl.col("gia_tri_1").mean(),
        pl.col("gia_tri_2").sum(),
        pl.col("id").count()
    )
    .collect()
)
time_polars = time.time() - start
print(f"Polars: {time_polars:.3f}s")

print(f"Polars nhanh hơn {time_pandas / time_polars:.1f}x")

Bạn hoàn toàn có thể chạy đoạn code trên ở máy mình để tự kiểm chứng. Kết quả có thể khác đôi chút tùy cấu hình, nhưng xu hướng thì luôn nhất quán.

5.2 Kết Quả Benchmark Điển Hình

Dựa trên các benchmark độc lập từ nhiều nguồn khác nhau, đây là tổng hợp so sánh giữa Polars và Pandas:

Phép Toán Pandas Polars Tỷ Lệ Nhanh Hơn
Đọc CSV (1GB) 12.3s 2.5s ~5x
Lọc hàng (10M rows) 1.84s 0.40s ~4.6x
Inner Join (10M rows) 7.2s 2.0s ~3.6x
Sắp xếp (10M rows) 11.7s 1.0s ~11.7x
GroupBy + Aggregation 3.5s 0.35s ~10x

Polars nhanh hơn từ 3.6 đến 11.7 lần tùy phép toán, và chênh lệch càng tăng khi dữ liệu càng lớn. Sắp xếp (sorting) là nơi Polars vượt trội nhất — lý do đơn giản là Pandas chỉ dùng một luồng CPU, trong khi Polars tận dụng song song tất cả các nhân.

5.3 Bộ Nhớ Sử Dụng

Ngoài tốc độ, Polars cũng tiết kiệm RAM hơn đáng kể:

  • Pandas: Cần RAM gấp 5-10 lần kích thước dataset (ví dụ: file 2GB cần 10-20GB RAM)
  • Polars: Cần RAM gấp 2-4 lần (ví dụ: file 2GB cần 4-8GB RAM)
  • Polars streaming: Lượng RAM gần như cố định, không phụ thuộc vào kích thước dữ liệu

Điểm cuối cùng là lý do mình đặc biệt ấn tượng — streaming mode thực sự giải phóng bạn khỏi giới hạn RAM.

6. Tích Hợp GPU — Tăng Tốc Với NVIDIA RAPIDS

6.1 Polars GPU Engine

Từ cuối năm 2024, Polars hợp tác với NVIDIA để ra mắt GPU Engine, sử dụng RAPIDS cuDF làm backend. Kết quả? Tăng tốc lên đến 13 lần so với CPU cho các phép toán nặng. Con số đó không hề nhỏ.

import polars as pl

# Cài đặt: pip install "polars[gpu]"

# Sử dụng GPU — chỉ cần thêm engine="gpu"
ket_qua = (
    pl.scan_parquet("du_lieu_lon.parquet")
    .filter(pl.col("nam") == 2026)
    .group_by("danh_muc", "khu_vuc")
    .agg(
        pl.col("doanh_thu").sum().alias("tong_dt"),
        pl.col("doanh_thu").mean().alias("dt_trung_binh"),
        pl.len().alias("so_giao_dich")
    )
    .sort("tong_dt", descending=True)
    .collect(engine="gpu")  # Chạy trên GPU!
)

print(ket_qua)

Điểm đặc biệt ở đây: bạn không cần thay đổi code. Chỉ cần đổi collect() thành collect(engine="gpu") là xong. Polars tự động chuyển các phép toán phù hợp sang GPU và fallback về CPU cho những phần không hỗ trợ. Đẹp.

6.2 Khi Nào GPU Engine Hiệu Quả?

GPU Engine đặc biệt hữu ích cho:

  • Grouped aggregations: Phép nhóm và tổng hợp trên hàng triệu nhóm
  • Joins: Phép nối bảng lớn (hàng chục triệu dòng)
  • Phép toán số học: Tính toán trên cột số với khối lượng lớn

Tuy nhiên, các truy vấn bị giới hạn bởi I/O (đọc/ghi file) thì hiệu suất GPU và CPU sẽ tương đương, vì bottleneck nằm ở tốc độ đĩa chứ không phải tính toán. Nên đừng kỳ vọng GPU sẽ "cứu" mọi thứ nhé — hãy đo trước rồi quyết định.

7. Polars Cloud — Xử Lý Dữ Liệu Quy Mô Doanh Nghiệp

7.1 Tổng Quan

Ra mắt chính thức vào tháng 9 năm 2025 trên AWS, Polars Cloud là nền tảng đám mây cho phép bạn chạy Polars queries từ xa trên hạ tầng cloud, với autoscaling và quản lý cluster tự động.

# pip install polars-cloud
import polars as pl
import polars_cloud as pc

# Cấu hình kết nối cloud
pc.configure(api_key="your_api_key")

# Chạy query trên cloud — cú pháp giống hệt Polars thông thường
ket_qua = (
    pl.scan_parquet("s3://my-bucket/du_lieu_petabytes/")
    .filter(pl.col("khu_vuc").is_in(["Mien_Bac", "Mien_Trung"]))
    .group_by("thanh_pho", "nam")
    .agg(pl.col("doanh_thu").sum())
    .collect(engine=pc.GPUEngine())  # Chạy trên cloud với GPU
)

Cú pháp gần như không đổi so với Polars thông thường — đó chính là điểm mạnh lớn nhất. Bạn không cần học thêm framework mới.

7.2 Tính Năng Doanh Nghiệp

Polars Cloud cung cấp nhiều tính năng dành cho môi trường production:

  • Distributed Engine (Open Beta): Hỗ trợ xử lý dữ liệu petabyte với horizontal, vertical và diagonal scaling
  • Autoscaling: Tự động mở rộng/thu hẹp cluster theo nhu cầu
  • Dashboard giám sát: Xem tóm tắt truy vấn, thông tin cluster, vCPU, và tracing chi tiết
  • Fault tolerance: Tự động phục hồi khi có lỗi
  • Hỗ trợ on-premises: Triển khai trên hạ tầng riêng của doanh nghiệp

Nếu bạn đang xử lý dữ liệu ở quy mô terabyte trở lên, đây là một lựa chọn đáng cân nhắc nghiêm túc.

8. Các Kỹ Thuật Nâng Cao

8.1 Window Functions

Polars hỗ trợ window functions mạnh mẽ thông qua method .over() — và nói thật, đây là một trong những tính năng khiến mình "mê" Polars:

import polars as pl

df = pl.DataFrame({
    "phong_ban": ["IT", "IT", "IT", "HR", "HR", "Marketing"],
    "nhan_vien": ["An", "Bình", "Cường", "Dung", "Em", "Phúc"],
    "luong": [20, 25, 18, 22, 28, 19]
})

# Window function: xếp hạng lương trong từng phòng ban
ket_qua = df.with_columns(
    pl.col("luong")
        .rank(descending=True)
        .over("phong_ban")
        .alias("xep_hang_luong"),
    pl.col("luong")
        .mean()
        .over("phong_ban")
        .alias("luong_tb_phong"),
    (pl.col("luong") - pl.col("luong").mean().over("phong_ban"))
        .alias("chenh_lech_vs_tb")
)

print(ket_qua)
# shape: (6, 6)
# ┌───────────┬──────────┬───────┬────────────────┬───────────────┬──────────────────┐
# │ phong_ban ┆ nhan_vien┆ luong ┆ xep_hang_luong ┆ luong_tb_phong┆ chenh_lech_vs_tb │
# │ ---       ┆ ---      ┆ ---   ┆ ---            ┆ ---           ┆ ---              │
# │ str       ┆ str      ┆ i64   ┆ u32            ┆ f64           ┆ f64              │
# ╞═══════════╪══════════╪═══════╪════════════════╪═══════════════╪══════════════════╡
# │ IT        ┆ An       ┆ 20    ┆ 2              ┆ 21.0          ┆ -1.0             │
# │ IT        ┆ Bình     ┆ 25    ┆ 1              ┆ 21.0          ┆ 4.0              │
# │ IT        ┆ Cường    ┆ 18    ┆ 3              ┆ 21.0          ┆ -3.0             │
# │ HR        ┆ Dung     ┆ 22    ┆ 2              ┆ 25.0          ┆ -3.0             │
# │ HR        ┆ Em       ┆ 28    ┆ 1              ┆ 25.0          ┆ 3.0              │
# │ Marketing ┆ Phúc     ┆ 19    ┆ 1              ┆ 19.0          ┆ 0.0              │
# └───────────┴──────────┴───────┴────────────────┴───────────────┴──────────────────┘

So với Pandas thì gọn hơn rất nhiều. Trong Pandas, bạn phải dùng transform() hoặc groupby().apply() — cú pháp dài dòng và thường chậm hơn đáng kể.

8.2 Xử Lý Dữ Liệu Thời Gian

import polars as pl

df = pl.DataFrame({
    "thoi_gian": [
        "2026-01-01 08:30:00", "2026-01-01 09:15:00",
        "2026-01-01 10:00:00", "2026-01-02 08:00:00",
        "2026-01-02 14:30:00", "2026-01-03 11:00:00"
    ],
    "gia_tri": [100, 150, 200, 120, 180, 160]
}).with_columns(
    pl.col("thoi_gian").str.to_datetime("%Y-%m-%d %H:%M:%S")
)

# Group by dynamic: nhóm theo khoảng thời gian
ket_qua = (
    df
    .sort("thoi_gian")
    .group_by_dynamic("thoi_gian", every="1d")
    .agg(
        pl.col("gia_tri").sum().alias("tong"),
        pl.col("gia_tri").mean().alias("trung_binh"),
        pl.len().alias("so_ban_ghi")
    )
)

print(ket_qua)
# Tự động nhóm theo từng ngày, tính tổng, trung bình và đếm

Tính năng group_by_dynamic cực kỳ hữu ích khi làm việc với dữ liệu time series. Bạn có thể nhóm theo bất kỳ khoảng thời gian nào — từ "1m" (phút), "1h" (giờ), đến "1w" (tuần) hay "1mo" (tháng).

8.3 Struct Và Nested Data

Polars xử lý dữ liệu lồng nhau (nested data) rất tốt — và đây là điểm mà Pandas thường gặp khó khăn:

import polars as pl

# Tạo DataFrame với cột kiểu List
df = pl.DataFrame({
    "hoc_sinh": ["An", "Bình", "Cường"],
    "diem_so": [[8.5, 9.0, 7.5], [6.0, 7.0, 8.0], [9.5, 9.0, 10.0]]
})

# Xử lý dữ liệu list một cách tự nhiên
ket_qua = df.with_columns(
    pl.col("diem_so").list.mean().alias("diem_tb"),
    pl.col("diem_so").list.max().alias("diem_cao_nhat"),
    pl.col("diem_so").list.len().alias("so_mon")
)

print(ket_qua)
# shape: (3, 5)
# ┌─────────┬───────────────────┬─────────┬───────────────┬────────┐
# │ hoc_sinh┆ diem_so           ┆ diem_tb ┆ diem_cao_nhat ┆ so_mon │
# │ ---     ┆ ---               ┆ ---     ┆ ---           ┆ ---    │
# │ str     ┆ list[f64]         ┆ f64     ┆ f64           ┆ u32    │
# ╞═════════╪═══════════════════╪═════════╪═══════════════╪════════╡
# │ An      ┆ [8.5, 9.0, 7.5]  ┆ 8.33    ┆ 9.0           ┆ 3      │
# │ Bình    ┆ [6.0, 7.0, 8.0]  ┆ 7.0     ┆ 8.0           ┆ 3      │
# │ Cường   ┆ [9.5, 9.0, 10.0] ┆ 9.5     ┆ 10.0          ┆ 3      │
# └─────────┴───────────────────┴─────────┴───────────────┴────────┘

Trong Pandas, để đạt được kết quả tương tự, bạn phải dùng apply() hoặc explode() rồi group lại — vừa chậm vừa khó đọc. Polars xử lý trực tiếp trên kiểu List, nhanh gọn hơn nhiều.

9. Tích Hợp Polars Với Hệ Sinh Thái Python

9.1 Chuyển Đổi Giữa Polars Và Pandas

Một câu hỏi rất thực tế mà mình thường nhận được: "Tôi đang dùng Pandas, muốn thử Polars nhưng nhiều thư viện chỉ hỗ trợ Pandas DataFrame. Phải làm sao?"

Rất đơn giản — Polars hỗ trợ chuyển đổi qua lại liền mạch:

import polars as pl
import pandas as pd

# Polars -> Pandas
df_polars = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
df_pandas = df_polars.to_pandas()

# Pandas -> Polars
df_polars_2 = pl.from_pandas(df_pandas)

# Dùng Polars cho xử lý nặng, chuyển sang Pandas cho visualization
import matplotlib.pyplot as plt

# Xử lý dữ liệu nhanh với Polars
ket_qua = (
    pl.scan_parquet("doanh_thu.parquet")
    .group_by("thang")
    .agg(pl.col("doanh_thu").sum())
    .sort("thang")
    .collect()
)

# Chuyển sang Pandas để vẽ biểu đồ
df_plot = ket_qua.to_pandas()
plt.bar(df_plot["thang"], df_plot["doanh_thu"])
plt.title("Doanh thu theo tháng")
plt.show()

Chiến thuật này (Polars xử lý, Pandas visualize) là cách mà khá nhiều team đang dùng trong thực tế. Tận dụng được thế mạnh của cả hai.

9.2 Tích Hợp Với NumPy

import polars as pl
import numpy as np

# NumPy -> Polars
arr = np.random.randn(1000, 3)
df = pl.DataFrame(
    arr,
    schema=["feature_1", "feature_2", "feature_3"]
)

# Polars -> NumPy
arr_back = df.to_numpy()

# Dùng trong pipeline scikit-learn
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

# Chuẩn bị dữ liệu bằng Polars (nhanh hơn)
df = (
    pl.scan_parquet("ml_data.parquet")
    .filter(pl.col("label").is_not_null())
    .select("feature_1", "feature_2", "feature_3", "label")
    .collect()
)

# Chuyển sang NumPy cho scikit-learn
X = df.select("feature_1", "feature_2", "feature_3").to_numpy()
y = df["label"].to_numpy()

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = RandomForestClassifier().fit(X_train, y_train)
print(f"Accuracy: {model.score(X_test, y_test):.4f}")

Tích hợp Polars vào ML pipeline không hề phức tạp. Bước chuẩn bị dữ liệu (thường là phần tốn thời gian nhất) được Polars tăng tốc đáng kể, rồi bạn chuyển sang NumPy/Pandas cho bước training.

10. Khi Nào Nên Dùng Polars, Khi Nào Nên Dùng Pandas?

Đây có lẽ là câu hỏi thiết thực nhất. Và câu trả lời ngắn gọn là: tùy trường hợp.

10.1 Chọn Polars Khi:

  • Dữ liệu lớn (>1GB): Polars xử lý hiệu quả hơn rất nhiều nhờ Rust backend và xử lý song song
  • Pipeline ETL/production: Lazy evaluation và tối ưu truy vấn giúp code chạy nhanh và ổn định hơn
  • Cần hiệu suất cao: GroupBy, join, sort trên dataset lớn — Polars nhanh hơn 5-100 lần
  • Dữ liệu vượt quá RAM: Streaming engine cho phép xử lý mà không cần tải toàn bộ dữ liệu vào bộ nhớ
  • Dự án mới: Không có legacy code ràng buộc, tận dụng API hiện đại ngay từ đầu

10.2 Chọn Pandas Khi:

  • Khám phá dữ liệu tương tác (EDA): Pandas có hệ sinh thái phong phú hơn, tích hợp tốt với Jupyter, matplotlib, seaborn
  • Dữ liệu nhỏ (<100MB): Với dataset nhỏ, sự khác biệt hiệu suất không đáng kể
  • Tích hợp ML pipeline: Hầu hết thư viện ML (scikit-learn, XGBoost, LightGBM) vẫn nhận Pandas DataFrame trực tiếp
  • Code base hiện có: Chuyển đổi toàn bộ sang Polars tốn thời gian — hãy chuyển dần từng phần
  • Logic Python phức tạp: Khi cần apply() với hàm Python tùy chỉnh, Pandas vẫn linh hoạt hơn

10.3 Chiến Lược "Hai Trong Một"

Thực tế mà nói, nhiều team (bao gồm cả team mình) dùng cả hai: Polars cho xử lý dữ liệu nặng (ETL, aggregation, join) và Pandas cho EDA, visualization, và tích hợp ML. Chiến lược "mỗi thứ một việc" này hiệu quả nhất trong thực tế:

import polars as pl
import pandas as pd

# Bước 1: Dùng Polars để xử lý dữ liệu nặng
df_processed = (
    pl.scan_parquet("raw_data/*.parquet")
    .filter(pl.col("valid") == True)
    .with_columns(
        (pl.col("revenue") - pl.col("cost")).alias("profit"),
        pl.col("date").dt.month().alias("month")
    )
    .group_by("category", "month")
    .agg(
        pl.col("profit").sum().alias("total_profit"),
        pl.col("profit").mean().alias("avg_profit"),
        pl.len().alias("num_transactions")
    )
    .collect(engine="streaming")
)

# Bước 2: Chuyển sang Pandas cho phân tích và visualization
df_analysis = df_processed.to_pandas()

# Vẽ biểu đồ, chạy ML, xuất báo cáo...
import seaborn as sns
import matplotlib.pyplot as plt

pivot = df_analysis.pivot_table(
    values="total_profit",
    index="category",
    columns="month",
    aggfunc="sum"
)
sns.heatmap(pivot, annot=True, fmt=".0f", cmap="YlOrRd")
plt.title("Lợi nhuận theo danh mục và tháng")
plt.show()

Kết Luận

Polars không phải là "Pandas killer" — mà đúng hơn, nó là sự tiến hóa của cách chúng ta xử lý dữ liệu trong Python. Với kiến trúc viết bằng Rust, lazy evaluation, streaming engine, GPU acceleration, và giờ là cả cloud platform, Polars đang định nghĩa lại giới hạn của những gì một thư viện DataFrame có thể làm.

Nếu bạn đang bắt đầu dự án mới với dữ liệu trung bình đến lớn, hãy thử Polars. Nếu bạn đã có hệ thống Pandas, hãy bắt đầu dùng Polars cho các phần xử lý nặng nhất — ETL pipeline, aggregation trên dữ liệu lớn, hoặc batch processing. Sự chuyển đổi dần dần này sẽ mang lại lợi ích ngay mà không cần đập đi xây lại.

Với vòng gọi vốn 18 triệu Euro Series A, cộng đồng đang phát triển nhanh, và Polars Cloud đã Generally Available — tương lai của Polars rất sáng sủa. Theo mình thì đây là thời điểm tốt nhất để bắt đầu.

Hãy bắt đầu với pip install polars và thử trên chính dữ liệu của bạn — bạn sẽ ngạc nhiên với tốc độ đấy!

Về Tác Giả Editorial Team

Our team of expert writers and editors.