
Khi bức tranh khoa học dữ liệu ngày càng phức tạp với các công nghệ mới xuất hiện liên tục, lựa chọn bộ công cụ phù hợp là yếu tố then chốt cho dự án của bạn. Dự án càng phức tạp, bạn càng cần thận trọng khi chọn công cụ, vì các lựa chọn này sẽ ảnh hưởng đáng kể đến quá trình phát triển và cuối cùng là thành công của dự án.
Nếu bạn đang làm một dự án khoa học dữ liệu, các quyết định thiết kế vượt xa tranh luận Python vs R. Đó cũng là câu chuyện về việc chọn thư viện nào sau khi bạn đã quyết định ngôn ngữ lập trình làm nền tảng cho dự án.
Với Python cho khoa học dữ liệu, pandas là công cụ tiêu chuẩn de facto để thao tác dữ liệu. Lý do sử dụng pandas rất nhiều và thuyết phục, nhưng nó cũng có những hạn chế có thể dẫn tới các nút thắt cổ chai quan trọng trong dự án. Đặc biệt, pandas nổi tiếng với hiệu năng hạn chế khi xử lý các tập dữ liệu lớn.
Để giải quyết các vấn đề này, pandas đã phát hành phiên bản 2.0 gần đây, đi kèm các điều chỉnh quan trọng và cải thiện hiệu năng (hãy xem bài viết này để biết mọi điều bạn cần biết về pandas 2.0).
Song song đó, những lựa chọn thay thế mới đã xuất hiện trong hệ sinh thái Python để thách thức vị thế thống trị của pandas. Một trong những cái tên phổ biến là polars, một thư viện dựa trên Python và Rust để phân tích dữ liệu nhanh hơn.
Bài viết này sẽ phân tích sự khác biệt giữa pandas 2.0 và polars trong thao tác dữ liệu. Chúng ta sẽ bắt đầu bằng việc phân tích các chiến lược cải thiện hiệu năng tính toán và kết thúc với loạt thử nghiệm so sánh hiệu năng của hai công cụ. Bắt đầu nhé!
Cách lựa chọn giữa pandas và polars
Không có công nghệ đơn lẻ nào tốt nhất trong mọi hoàn cảnh để giải quyết tất cả vấn đề có thể phát sinh trong quy trình khoa học dữ liệu của bạn. Cần cân nhắc nhiều yếu tố trước khi đưa ra lựa chọn. Dưới đây là một số yếu tố quan trọng nhất:
- Hiệu năng tính toán. Quy tắc ngầm: dự án càng lớn và phức tạp, bạn càng nên cân nhắc các công cụ hiệu năng cao để tránh nút thắt cổ chai.
- Phù hợp với ngăn xếp công nghệ. Các công cụ trong một dự án khoa học dữ liệu hiếm khi vận hành độc lập mà thường kết hợp với các công cụ khác trong ngăn xếp công nghệ. Trước khi lựa chọn, bạn nên lưu ý đến sự tương thích và bổ trợ giữa các công nghệ.
- Tương thích mã. Chọn công nghệ mới cũng liên quan đến tính tương thích và hiệu quả của mã. Nếu áp dụng công cụ mới khiến bạn phải viết lại mã ở các dự án hiện có, bạn cần đưa yếu tố đó vào bài toán chi phí – lợi ích.
- Độ phổ biến. Cuối cùng, độ phổ biến là chỉ dấu về số người dùng, có lợi cho việc tuyển dụng nhà phát triển mới hoặc giải quyết thắc mắc trên các nền tảng như Stack Overflow.
Làm thế nào cải thiện hiệu năng thao tác dữ liệu?
Phần này sẽ điểm qua các kỹ thuật chính giúp tăng hiệu năng thao tác dữ liệu.
Giảm sao chép với copy-on-write
Trong các công cụ thao tác dữ liệu truyền thống, mỗi phép toán dữ liệu đều tạo ra một bản sao mới của toàn bộ dữ liệu. Hành vi này có thể gây vấn đề về bộ nhớ khi xử lý tập dữ liệu lớn.
Copy-on-write là kỹ thuật phổ biến nhất để giải quyết vấn đề này. Về bản chất, copy-on-write là kỹ thuật quản lý tài nguyên cho phép bạn triển khai hiệu quả thao tác "nhân đôi" hoặc "sao chép" trên các tài nguyên có thể chỉnh sửa. Nghĩa là nếu một tập dữ liệu được nhân đôi nhưng không (hoàn toàn) bị chỉnh sửa, thì không cần tạo tài nguyên mới; thay vào đó có thể chia sẻ bộ nhớ giữa bản sao và bản gốc.
Tính toán song song
Một nhược điểm chính của pandas là nó chạy theo luồng đơn, nghĩa là không tận dụng được các kỹ thuật tính toán song song vốn đã phổ biến trong khoa học dữ liệu. Tính toán song song chủ yếu được thực hiện nhờ máy đa lõi hoặc tính toán phân tán (tức là một nhóm máy tính làm việc cùng nhau trong một cụm).
Hãy xét ví dụ sau với hàm group by phổ biến. Nếu bạn muốn tính tuổi trung bình của các cầu thủ bóng rổ theo đội, bạn có thể dùng tính toán song song để gán việc tính trung bình của mỗi nhóm cho một lõi hoặc một máy thay vì tính tuần tự từng nhóm, từ đó tăng hiệu năng.

Tối ưu hóa nhập dữ liệu
Điểm khởi đầu của phân tích dữ liệu thường là đọc các tập dữ liệu bạn muốn nghiên cứu. Tuy nhiên, đây thường là nút thắt trong các dự án có tập dữ liệu lớn. Dưới đây là một số kỹ thuật có thể giúp cải thiện hiệu năng khi đọc tệp dữ liệu:
- Chia khối dữ liệu (chunking). Thay vì đọc toàn bộ tệp, bạn có thể chia thành các phần nhỏ (chunk). Kỹ thuật này cho phép nhập dữ liệu nhanh hơn đồng thời giảm bộ nhớ cần dùng để lưu trữ cùng lúc.
- Chỉ đọc các cột cần thiết. Đôi khi bạn biết trước các cột cần cho phân tích. Thay vì đọc toàn bộ dữ liệu, bạn có thể lọc sơ bộ và chỉ đọc các cột cần thiết.
- Thu hẹp kiểu dữ liệu (downcasting). Các công cụ như pandas có hành vi mặc định khi gán kiểu dữ liệu cho các cột nhập vào. Đôi khi có những kiểu khác dùng ít bộ nhớ hơn mà không mất thông tin. Downcasting là quá trình đổi sang kiểu dữ liệu nhỏ nhất vẫn chứa được giá trị.
Đánh giá lười (Lazy Evaluation) và lập kế hoạch truy vấn (Query Planning)
Thứ tự thực hiện các phép toán có thể ảnh hưởng đến hiệu năng. Thường thì bạn có thể sắp xếp lại một số tính toán để đạt cùng kết quả nhưng hiệu quả hơn.
Có hai chiến lược để đạt được điều này: đánh giá lười và lập kế hoạch truy vấn. Chiến lược đầu đề cập đến việc trì hoãn tính toán cho đến khi thật sự cần thiết. Ngược lại, lập kế hoạch truy vấn là quá trình tìm cách hiệu quả nhất để thực hiện nhiều thao tác dữ liệu dựa trên một tập truy vấn khác nhau. Để làm vậy, lập kế hoạch truy vấn thường dùng bộ tối ưu hóa truy vấn.
Xử lý ngoài bộ nhớ chính (Out-of-Core)
Các công cụ như pandas lưu toàn bộ tập dữ liệu trong bộ nhớ, điều này có thể gây khó nếu bạn làm việc với tập dữ liệu lớn không vừa với bộ nhớ máy hoặc thực hiện các phép tính tiêu tốn bộ nhớ.
Đây là lúc xử lý ngoài bộ nhớ trở nên hữu ích. Nói đơn giản, xử lý out-of-core đề cập đến việc dùng bộ nhớ ngoài (ví dụ đĩa ngoài) để lưu dữ liệu vốn không thể đặt trong bộ nhớ chính. Out-of-core cho phép bạn xử lý dữ liệu lưu ngoài theo từng khối, nhờ đó cuối cùng vẫn thu được kết quả như khi xử lý toàn bộ trên máy.
Một số lựa chọn thay thế pandas có hiệu năng cao?
Bài viết tập trung so sánh pandas 2.0 và polars, nhưng cũng đáng nhắc đến các công cụ khác có thể đáp ứng nhu cầu như một lựa chọn thay thế pandas. Dưới đây là một số công cụ triển vọng:
- Koalas. API tương thích pandas xây dựng trên PySpark. Nếu bạn dùng Spark, nên cân nhắc công cụ này.
- Vaex. API pandas cho tính toán ngoài bộ nhớ, rất phù hợp để phân tích dữ liệu bảng lớn ở mức hàng tỷ dòng mỗi giây.
- Modin. API pandas cho lập trình song song, dựa trên Dask hoặc Ray cho các dự án dữ liệu lớn. Nếu bạn dùng Dask hoặc Ray, Modin là nguồn lực tuyệt vời.
- cuDF. Một phần của dự án RAPIDS, cuDF là API giống pandas cho tính toán trên GPU, dựa vào GPU NVIDIA hoặc các thành phần khác của RAPIDS để thao tác dữ liệu tốc độ cao.
Vì sao chọn polars?
Pandas là lựa chọn quen thuộc cho người làm dữ liệu với Python, nhưng như đã nêu, nó có một số hạn chế quan trọng khi xử lý tập dữ liệu lớn. Sứ mệnh cốt lõi của polars là cung cấp thư viện dataframe cực nhanh giải quyết mọi vấn đề của pandas.
Được viết bằng Rust (một ngôn ngữ lập trình mạnh mẽ mang lại hiệu năng C/C++ và cho phép kiểm soát hoàn toàn các phần quan trọng về hiệu năng trong engine truy vấn), polars tích hợp các phương pháp tối tân để tăng hiệu năng, nhằm:
- Tăng cường tính toán song song để bạn tận dụng mọi lõi sẵn có trên máy.
- Tối ưu truy vấn để giảm công việc/phân bổ bộ nhớ không cần thiết nhờ đánh giá lười và lập kế hoạch truy vấn.
- Cải thiện hiệu năng lưu trữ bằng ngữ nghĩa copy-on-write và Apache Arrow.
- Xử lý các tập dữ liệu lớn hơn nhiều nhờ khả năng dữ liệu out-of-core thông qua API streaming.
Cuối cùng nhưng không kém phần quan trọng, một điểm hay của polars là cú pháp khá giống pandas và chia sẻ các khối xây dựng như Series và DataFrame. Điều này sẽ giúp bạn dễ dàng hơn nếu quyết định chuyển sang polars.
pandas phản ứng thế nào trước cạnh tranh với pandas 2.0?
Để giải quyết vấn đề hiệu năng và giảm áp lực từ đối thủ, pandas đã phát hành phiên bản 2.0 được mong đợi từ lâu. Là một bản cập nhật lớn, pandas 2.0 mang đến nhiều tính năng mới. Nó cũng áp dụng các ngừng hỗ trợ bắt buộc, dẫn đến thay đổi API.
Backend PyArrow mới là canh bạc lớn của pandas nhằm khắc phục hạn chế về hiệu năng. Người dùng giờ có thể chọn giữa backend NumPy truyền thống hoặc PyArrow hoàn toàn mới. PyArrow là thư viện Python cung cấp giao diện xử lý tập dữ liệu lớn bằng cấu trúc bộ nhớ Arrow.
pd.read_csv(my_file, engine='pyarrow')
Một tính năng quan trọng khác là tích hợp nhiều cải tiến copy-on-write nhằm cải thiện hiệu năng bằng cách tiết kiệm tài nguyên bộ nhớ.
Kết quả là nhiều thao tác của pandas nay có thêm tham số mặc định “copy” sẽ trả về một bản sao lười của các đối tượng cơ bản như series và dataframe thay vì các bản sao y hệt đã được lưu bộ đệm.
Còn quá sớm để biết liệu khả năng của pandas có giải quyết được vấn đề hiệu năng hay không. Ngay cả khi bị các công cụ khác vượt qua, vẫn có lý do chính đáng để tiếp tục dùng pandas: bạn sẽ không cần viết lại mã hiện có hay học mã mới vì có lẽ bạn đã dùng nó rồi.
pandas 2.0 vs polars: Khung so sánh
Giờ bạn đã nắm lý thuyết, hãy bắt tay vào thực hành. Ở các phần sau, chúng ta sẽ thực hiện loạt thử nghiệm để so sánh hiệu năng của pandas 2.0 và polars. Tiêu chí đánh giá là thời gian cần để hoàn thành một tác vụ. Chúng ta sẽ dùng mô-đun time, cung cấp cách đơn giản để tính thời gian thực thi.
Ngoài các bài kiểm tra hiệu năng, chúng ta sẽ phân tích mức độ tương thích mã của polars với pandas (tức là bạn cần thay đổi bao nhiêu mã nếu muốn chuyển sang polars?).
Tập dữ liệu
Chúng ta sẽ kiểm thử hiệu năng của pandas 2.0 và polars trên một tập dữ liệu giả định về doanh số theo giờ của một công ty có văn phòng ở nhiều quốc gia trong giai đoạn 1980–2022. Để tăng độ phức tạp cho phân tích, chúng ta sẽ thêm một số giá trị thiếu ngẫu nhiên.
Tổng thể, tập dữ liệu gồm hơn 22 triệu dòng và chiếm hơn 1,6 GB bộ nhớ, đủ để “đặt pandas dưới áp lực” đáng kể. Toàn bộ mã cho các bài kiểm thử có tại DataLab workbook này.

Kiểm thử nhập dữ liệu
Hãy bắt đầu so sánh hiệu năng của pandas 2.0 và polars khi nhập dữ liệu. Polars quảng bá rõ rằng nó có hiệu năng mạnh khi nhập từ CSV. Điều đó có đúng không?
Chúng ta sẽ kiểm tra thời gian pandas và polars cần để đọc một tập con cột và hàng. Ví dụ, giả sử bạn chỉ muốn phân tích doanh số của văn phòng Pháp. Với pandas, bạn phải đọc tất cả các hàng rồi lọc để loại các hàng không mong muốn. Chúng ta sẽ dùng phương thức .query() mới để làm vậy. Để thử hành vi của pandas 2.0, chúng ta sẽ nhập CSV bằng cả engine numpy truyền thống và engine pyarrow mới.
Ngược lại, polars cung cấp phương thức filter() có thể kết hợp với các điều kiện nhất định để chỉ đọc các hàng bạn quan tâm.
# pandas query
df_pd = pd.read_csv("./example.csv", engine="pyarrow")
df_pd = df_pd[['id', 'date', 'office', 'sales']]
df_pd = df_pd.query("office=='France'")
# polars filter
df_pl = pl.read_csv('example.csv').filter(
(pl.col('office') == 'France'))
df_pl.select(pl.col(['id', 'date', 'office', 'sales']))
Khi chạy thử nghiệm, polars cho kết quả tốt hơn đáng kể so với pandas, như biểu đồ cho thấy. Đáng ngạc nhiên là engine pyarrow không cho kết quả tốt hơn engine numpy tiêu chuẩn.

Về mức độ tương thích cú pháp, polars rõ ràng có “hương vị” giống pandas, với các hàm như pl.read_csv() phản chiếu hoàn hảo pandas. Tuy nhiên, hai thư viện hoạt động khác nhau ở tầng bên dưới, và điều đó tất yếu dẫn đến khác biệt cú pháp, như minh họa với phương thức query() vs filter().
Kiểm thử group by
Các thao tác groupby rất phù hợp để song song hóa. Hãy xem pandas và polars thể hiện thế nào với một số phép tổng hợp theo nhóm. Cụ thể, chúng ta sẽ tính trung bình và trung vị của doanh số theo văn phòng và theo tháng.
df_pd.groupby([df_pd.office, df_pd.date.dt.month])['sales'].agg('mean') # pandas groupby mean
df_pl.groupby([pl.col('office'), pl.col('date').dt.month()]).agg([pl.mean('sales')]) # polars groupby mean

df_pd.groupby([df_pd.office, df_pd.date.dt.month])['sales'].agg('median') # pandas groupby median
df_pl.groupby([pl.col('office'), pl.col('date').dt.month()]).agg([pl.median('sales')]) pandas groupby median

Kết quả cho thấy ở các phép group by và tổng hợp, polars hoạt động tốt hơn pandas. Một lần nữa, cú pháp của polars giống pandas.
Kiểm thử thống kê trượt
Một thao tác phù hợp khác để kiểm thử hiệu năng của pandas và polars là thống kê trượt. Tối ưu hóa kiểu tính toán này đòi hỏi nhiều thủ thuật thuật toán. Hãy xem chúng thể hiện thế nào khi tính trung bình trượt theo ngày cho doanh số.
# pandas rolling mean
df_pd['sales'].rolling(1440, min_periods=1).mean()
# polars rolling mean
df_pl['sales'].rolling_mean(1440, min_periods=1)
Một lần nữa, polars là bên chiến thắng. Lưu ý khác biệt nhỏ về cú pháp: polars có phương thức rolling_mean(), trong khi pandas dùng rolling() rồi kết hợp với các phương thức khác như mean() để cho cùng kết quả.

Kiểm thử lấy mẫu
Nghiên cứu toàn bộ quần thể quan tâm để phân tích một hiện tượng thường không khả thi. Đó là lý do lấy mẫu là một trong các tác vụ phổ biến nhất trong thống kê và khoa học dữ liệu. Việc này bao gồm lấy mẫu ngẫu nhiên bằng các kỹ thuật Mô phỏng Monte Carlo như bootstrapping và hoán vị. Tuy nhiên, thao tác này cũng cần sao chép biến rất nhiều, có thể thách thức nếu tài nguyên tính toán hạn chế.
Hãy xem pandas và polars làm việc thế nào khi thực hiện bootstrap, tức là lấy mẫu có hoàn lại. Chúng ta sẽ ước tính trung bình doanh số trên 10000 mẫu, mỗi mẫu gồm 1000 điểm dữ liệu.
#pandas bootstrap
simu_weights = []
for i in range(10000):
bootstrap_sample = random.choices(df_pd['sales'], k=1000)
simu_weights.append(np.nanmean(bootstrap_sample))
#polars bootstrap
simu_weights = []
for i in range(10000):
bootstrap_sample = df_pl['sales'].sample(n=1000,with_replacement=True)
simu_weights.append(bootstrap_sample.mean())
Khi thực hiện bootstrap, polars nhanh gần gấp 5 lần so với pandas.
Về cú pháp, trong khi pandas dựa vào numpy để thao tác lấy mẫu, polars có sẵn phương thức sample() tiện dụng.

Chuỗi thao tác phức hợp
Ở bài kiểm thử cuối, chúng ta sẽ thực hiện một chuỗi thao tác dữ liệu liên tiếp. Đây là công việc thường ngày của người làm dữ liệu. Tuy vậy, các thao tác dữ liệu gồm nhiều bước rất dễ được viết theo cách kém tối ưu về tính toán.
Giả sử văn phòng Ý của công ty giả định đang cân nhắc tuyển một số thực tập sinh năm 2022 sau khi kết thúc kỳ thực tập. Mục tiêu là phân tích xem những thực tập sinh nào thực hiện mười giao dịch doanh số lớn nhất năm 2022 để chọn ứng viên phù hợp.
Để làm vậy, chúng ta phải nối tập dữ liệu của mình với một tập dữ liệu thứ hai, gọi là “italy_2022”, chứa thông tin về người thực hiện từng giao dịch trong năm 2022. Cột “id” là cột chung của cả hai tập.

# pandas compound operations
df_pd.merge(italy_2022, on='id').query("responsibility =='Sales Intern'").sort_values('sales', ascending=False).head(10)[['name','surname','sales','sex']]
#polars compound manipulations
df_pl.join(italy_2022_pl, on="id").filter(pl.col('responsability') == 'Sales Intern').sort('sales',descending=True).head(10).select(pl.col(['name','surname','sales','sex']))

Như các bài kiểm thử trước, polars vượt trội pandas về hiệu năng. Trong bài này, phép nối (join) dường như là điểm yếu nhất của pandas. Ngược lại, polars hiệu quả hơn nhiều, chỉ mất chưa đến hai giây để hoàn tất toàn bộ chuỗi tác vụ.
Bảng so sánh pandas 2.0 vs polars
Chúng tôi đã tóm tắt các bài kiểm thử ở trên trong một bảng để bạn có cái nhìn nhanh:
|
Bài kiểm thử |
Pandas 2.0 |
Polars |
Bên thắng |
Mức độ giống cú pháp |
|
Kiểm thử nhập dữ liệu |
Đọc tất cả các hàng rồi lọc hàng không cần bằng phương thức .query() |
Đọc trực tiếp chỉ những hàng quan tâm bằng phương thức filter() |
Polars (hiệu năng tốt hơn) |
Tương tự, nhưng khác biệt do hiện thực bên dưới (.query() vs filter()) |
|
Kiểm thử Group By |
Dùng .groupby() để tổng hợp như trung bình và trung vị doanh số theo văn phòng và tháng |
Tương tự pandas nhưng hiệu năng tốt hơn |
Polars (hiệu năng tốt hơn) |
Tương tự |
|
Kiểm thử thống kê trượt |
Dùng rolling() kết hợp các phương thức khác như mean() để tính trung bình trượt |
Dùng phương thức rolling_mean() để tính trung bình trượt |
Polars (hiệu năng tốt hơn) |
Tương tự, nhưng có khác biệt nhỏ (.rolling().mean() vs rolling_mean()) |
|
Kiểm thử lấy mẫu |
Dùng numpy để thực hiện bootstrap |
Dùng phương thức sample() tích hợp sẵn để bootstrap |
Polars (nhanh gần gấp 5 lần) |
Khác nhau: pandas dựa vào numpy còn polars có sẵn phương thức |
|
Kiểm thử chuỗi thao tác phức hợp |
Thực hiện join với tập dữ liệu khác rồi sắp xếp và chọn dữ liệu |
Thực hiện các tác vụ tương tự nhưng hiệu quả hơn |
Polars (nhanh hơn đáng kể) |
Tương tự, nhưng có khác biệt nhỏ (merge().query().sort_values().head() vs join().filter().sort().head().select()) |
Nâng tầm kỹ năng
Chúc mừng bạn đã đọc đến cuối bài hướng dẫn. Theo các bài kiểm thử, polars hoạt động tốt hơn đáng kể so với pandas 2.0 ở gần như tất cả phép thử. Dù đây là mối đe dọa thực sự với vị thế dẫn đầu của pandas, pandas vẫn có lợi thế là lựa chọn mặc định để thao tác dữ liệu trong Python, nhờ đó hưởng lợi từ cộng đồng người dùng lớn hơn.
Nếu bạn làm việc với tập dữ liệu lớn, tăng hiệu năng là điều bắt buộc. DataCamp có thể hỗ trợ bạn. Dưới đây là danh sách khóa học giúp bạn nâng hiệu năng tính toán lên tầm cao mới:
Tôi là một chuyên viên phân tích dữ liệu tự do, hợp tác với các công ty và tổ chức trên toàn thế giới trong các dự án khoa học dữ liệu. Tôi cũng là giảng viên khoa học dữ liệu với hơn 2 năm kinh nghiệm. Tôi thường xuyên viết bài về khoa học dữ liệu bằng tiếng Anh và tiếng Tây Ban Nha; một số bài đã được đăng trên các trang uy tín như DataCamp, Towards Data Science và Analytics Vidhya. Là một nhà khoa học dữ liệu có nền tảng khoa học chính trị và luật, mục tiêu của tôi là làm việc tại giao điểm giữa chính sách công, pháp luật và công nghệ, tận dụng sức mạnh của ý tưởng để thúc đẩy các giải pháp và cách tiếp cận mới nhằm giúp chúng ta đối mặt với những thách thức cấp bách, đặc biệt là khủng hoảng khí hậu. Tôi xem mình là người tự học, không ngừng trau dồi và là một người ủng hộ vững chắc cho tính đa ngành. Không bao giờ là quá muộn để học điều mới.
