Viết mã Pandas như một chuyên gia với Method Chaining Pipelines

12 tháng 4, 2026·10 phút đọc

Bài viết này hướng dẫn cách nâng tầm kỹ năng Pandas của bạn thông qua việc sử dụng method chaining (chuỗi phương thức), hàm assign() và pipe(). Đây là những kỹ thuật giúp biến đổi mã nguồn lộn xộn thành các dòng dữ liệu (pipeline) sạch sẽ, dễ đọc, dễ kiểm thử và sẵn sàng cho môi trường sản xuất.

Viết mã Pandas như một chuyên gia với Method Chaining Pipelines

Mới đây, tôi đã mở lại một cuốn sổ tay cũ (notebook) và ngay lập tức đóng nó lại. Không phải vì nó sai, mã nguồn vẫn chạy tốt và các con số cũng chính xác. Nhưng tôi hoàn toàn không hiểu chuyện gì đang xảy ra.

Khắp nơi là những biến số. df1, df2, final_df, final_final. Mỗi bước có vẻ hợp lý khi đứng riêng lẻ, nhưng nhìn vào tổng thể, tôi cảm thấy như đang lạc trong mê cung. Tôi phải đọc từng dòng một chỉ để hiểu mình đã làm gì trước đó.

Điều thú vị là, hầu hết chúng ta đều bắt đầu với Pandas như vậy. Bạn học vài thao tác, lọc ở đây, tạo cột ở kia, nhóm và tổng hợp. Nó hoàn thành công việc. Nhưng theo thời gian, mã nguồn của bạn trở nên khó tin cậy hơn, khó xem lại hơn và chắc chắn là khó chia sẻ hơn.

Đó là lúc tôi nhận ra một điều. Khoảng cách giữa người mới bắt đầu và người dùng Pandas ở mức trung cấp không nằm ở việc biết nhiều hàm hơn. Nó nằm ở cách bạn cấu trúc các phép biến đổi dữ liệu.

Có một mô hình âm thầm thay đổi mọi thứ khi bạn nhận ra nó. Mã nguồn của bạn trở nên dễ đọc hơn, dễ gỡ lỗi hơn và dễ phát triển hơn. Đó được gọi là method chaining (chuỗi phương thức).

Trong bài viết này, tôi sẽ hướng dẫn cách sử dụng method chaining đúng cách, cùng với assign()pipe(), và cách chúng thay đổi cách tôi viết mã Pandas. Nếu bạn từng cảm thấy notebook của mình trở nên lộn xộn khi phát triển, bài viết này chắc chắn sẽ giúp bạn.

Sự thay đổi: Người dùng Pandas trung cấp làm gì khác biệt?

Lúc đầu, tôi nghĩ việc giỏi Pandas hơn là học nhiều hàm hơn. Nhiều mẹo, nhiều cú pháp, nhiều cách thao tác dữ liệu hơn. Nhưng càng viết nhiều, tôi càng nhận ra một điều. Những người thực sự giỏi Pandas không nhất thiết phải dùng nhiều hàm hơn tôi. Mã của họ chỉ trông... khác biệt. Sạch sẽ hơn, có chủ đích hơn và dễ theo dõi hơn.

Thay vì viết mã từng bước với nhiều biến trung gian, họ viết các phép biến đổi chảy vào nhau. Bạn có thể đọc mã của họ từ trên xuống dưới và hiểu chính xác dữ liệu đang bị biến đổi như thế nào ở mỗi giai đoạn. Nó gần giống như đọc một câu chuyện.

Đó là lúc tôi hiểu ra. Bước nâng cấp thực sự không nằm ở những gì bạn sử dụng, mà ở cách bạn cấu trúc nó.

Thay vì nghĩ: "Mình sẽ làm gì tiếp theo với DataFrame này?"

Bạn bắt đầu nghĩ: "Phép biến đổi nào sẽ diễn ra tiếp theo?"

Sự thay đổi nhỏ đó thay đổi mọi thứ. Và đây chính là nơi method chaining phát huy tác dụng.

Cách viết "trước đây": Đa số chúng ta viết Pandas như thế nào

Để làm rõ vấn đề, hãy giả sử chúng ta muốn trả lời một câu hỏi đơn giản: Nhóm sản phẩm nào đang tạo ra nhiều doanh thu nhất mỗi tháng?

Tôi lấy một tập dữ liệu bán hàng nhỏ với chi tiết đơn hàng, danh mục sản phẩm, giá và ngày tháng. Không có gì quá phức tạp.

import pandas as pd

df = pd.read_csv("sales.csv")
print(df.head())

Và đây là cách tôi thường viết mã này không lâu trước đây:

# Tạo cột mới cho doanh thu
df["revenue"] = df["quantity"] * df["price"]

# Lọc các đơn hàng từ năm 2023 trở đi
df_filtered = df[df["order_date"] >= "2023-01-01"]

# Chuyển đổi order_date sang datetime và trích xuất tháng
df_filtered["month"] = pd.to_datetime(df_filtered["order_date"]).dt.to_period("M")

# Nhóm theo category và month, sau đó tổng doanh thu
grouped = df_filtered.groupby(["category", "month"])["revenue"].sum()

# Chuyển Series trở lại DataFrame
result = grouped.reset_index()

# Sắp xếp theo doanh thu giảm dần
result = result.sort_values(by="revenue", ascending=False)

print(result)

Cách này hoạt động. Bạn nhận được câu trả lời. Nhưng có một vài vấn đề bắt đầu xuất hiện khi phân tích của bạn phát triển.

Thứ nhất, luồng dữ liệu khó theo dõi. Bạn phải theo dõi df, df_filtered, groupedresult. Mỗi biến đại diện cho một trạng thái dữ liệu hơi khác nhau.

Thứ hai, logic bị phân tán. Việc biến đổi diễn ra từng bước, nhưng không theo cách kết nối. Bạn phải ghép nối mọi thứ trong đầu khi đọc.

Thứ ba, nó khó tái sử dụng hoặc kiểm tra hơn. Nếu muốn tinh chỉnh một phần logic, bạn phải lần theo xem mọi thứ đang được sửa đổi ở đâu.

Cách viết "sau này": Khi mọi thứ khớp với nhau

Bây giờ, hãy giải quyết cùng một vấn đề một lần nữa. Cùng tập dữ liệu, cùng mục tiêu. Nhưng hãy xem nó trông như thế nào khi bạn bắt đầu tư duy theo các phép biến đổi:

result = (
    pd.read_csv("sales.csv")  # Bắt đầu với dữ liệu thô
    .assign(
        # Tạo cột doanh thu
        revenue=lambda df: df["quantity"] * df["price"],
        # Chuyển đổi order_date sang datetime
        order_date=lambda df: pd.to_datetime(df["order_date"]),
        # Trích xuất tháng từ order_date
        month=lambda df: df["order_date"].dt.to_period("M")
    )
    # Lọc các đơn hàng từ năm 2023 trở đi
    .loc[lambda df: df["order_date"] >= "2023-01-01"]
    # Nhóm theo category và month, sau đó tổng doanh thu
    .groupby(["category", "month"], as_index=False)["revenue"]
    .sum()
    # Sắp xếp theo doanh thu giảm dần
    .sort_values(by="revenue", ascending=False)
)

print(result)

Kết quả đầu ra giống hệt nhau, nhưng cảm giác hoàn toàn khác biệt.

Điều đầu tiên bạn nhận thấy là mọi thứ chảy trôi. Không có nhảy qua lại giữa các biến hay cố gắng nhớ df_filtered hay grouped nghĩa là gì. Mỗi bước xây dựng dựa trên bước trước đó. Bạn bắt đầu với dữ liệu thô, sau đó: tạo doanh thu, chuyển đổi ngày tháng, trích xuất tháng, lọc, nhóm, tổng hợp và sắp xếp. Tất cả trong một dòng dữ liệu (pipeline) liên tục.

Bạn có thể đọc từ trên xuống dưới và hiểu chính xác dữ liệu đang bị biến đổi như thế nào ở mỗi giai đoạn.

Phân tích mô hình

Khi tôi lần đầu thấy phong cách mã Pandas này, nó trông có vẻ đáng sợ một chút. Mọi thứ được xích lại với nhau. Không có biến trung gian. Nhiều việc diễn ra trong một không gian nhỏ. Nhưng khi tôi chậm lại và chia nhỏ nó, mọi thứ bắt đầu hợp lý.

Về cơ bản chỉ có ba ý tưởng chính ở đây:

  1. Method Chaining (Chuỗi phương thức)
  2. assign()
  3. pipe()

Method Chaining (Nền tảng)

Method chaining rất đơn giản. Mỗi bước lấy một DataFrame, áp dụng một phép biến đổi và trả về một DataFrame mới. DataFrame mới đó ngay lập tức được chuyển vào bước tiếp theo.

Thay vì viết:

df = step1(df)
df = step2(df)
df = step3(df)

Bạn làm thế này:

df = step1(df).step2().step3()

Tác động của nó lớn hơn vẻ bề ngoài. Nó buộc bạn phải tư duy theo luồng. Mỗi dòng trở thành một phép biến đổi. Bạn không còn nhảy qua lại hay lưu trữ trạng thái tạm thời. Bạn chỉ đơn giản là tiến về phía trước.

assign() — Giữ mọi thứ trong luồng

Đây là thứ thực sự mở khóa khả năng chaining cho tôi. Trước đây, bất cứ khi nào tôi muốn tạo một cột mới, tôi sẽ làm gián đoạn luồng:

df["revenue"] = df["quantity"] * df["price"]

Cách này hoạt động, nhưng nó làm gián đoạn pipeline. assign() cho phép bạn làm điều tương tự mà không làm đứt gãy chuỗi:

.assign(revenue=lambda df: df["quantity"] * df["price"])

Lúc đầu, phần lambda df: có vẻ hơi lạ. Nhưng ý tưởng thì đơn giản. Bạn đang nói: "Lấy DataFrame hiện tại và sử dụng nó để định nghĩa cột mới này."

Lợi ích chính là mọi thứ nằm ở một nơi. Bạn có thể thấy cột được tạo ở đâu và cách nó được sử dụng, tất cả trong cùng một luồng.

pipe() — Khi mọi thứ bắt đầu trở nên mạnh mẽ

pipe() là thứ tôi đã bỏ qua lúc đầu. Tôi nghĩ: "Mình đã có thể xích chuỗi các phương thức rồi, tại sao cần cái này?"

Sau đó tôi gặp một vấn đề. Một số phép biến đổi quá phức tạp để vừa gọn trong một chuỗi. Bạn sẽ phải viết logic lộn xộn ngay trong dòng hoặc làm đứt chuỗi hoàn toàn. Đó là nơi pipe() phát huy tác dụng. Nó cho phép bạn chuyển DataFrame của mình vào một hàm tùy chỉnh mà không làm gián đoạn luồng.

Ví dụ:

def filter_high_value_orders(df):
    return df[df["revenue"] > 500]

df = (
    pd.read_csv("sales.csv")
    .assign(revenue=lambda df: df["quantity"] * df["price"])
    .pipe(filter_high_value_orders)
)

Bây giờ logic của bạn sạch sẽ, có thể tái sử dụng và dễ kiểm tra hơn.

Tư duy theo Pipeline (Nâng cấp thực sự)

Khi bạn bắt đầu sử dụng method chaining nhất quán, cách bạn nghĩ về việc làm việc với dữ liệu bắt đầu thay đổi. Trước đây, cách tiếp cận của tôi rất từng bước. Tôi sẽ nhìn vào một DataFrame và nghĩ: "Mình sẽ làm gì tiếp theo? Lọc nó? Sửa đổi nó? Lưu nó? Chuyển sang cái khác?"

Nhưng với method chaining, câu hỏi đó thay đổi. Bây giờ nó trở thành: "Phép biến đổi nào sẽ diễn ra tiếp theo?"

Sự thay đổi đó nhỏ, nhưng nó thay đổi cách bạn cấu trúc mọi thứ. Bạn ngừng tư duy theo các bước cô lập và bắt đầu tư duy theo một luồng. Một pipeline. Dữ liệu đi vào, được biến đổi từng giai đoạn và tạo ra kết quả đầu ra.

Các lỗi thường gặp

Khi tôi bắt đầu sử dụng method chaining, tôi đã lạm dụng nó. Mọi thứ trông sạch sẽ hơn, nên tôi cố ép mọi thứ vào một chuỗi. Điều đó dẫn đến một số mã code đáng ngờ.

Dưới đây là một vài lỗi tôi gặp phải để bạn không phải mắc phải:

  1. Over-Chaining (Xích chuỗi quá nhiều): Tôi nghĩ chuỗi càng dài thì mã càng tốt. Không đúng. Khi chuỗi bắt đầu trở nên quá dày đặc, hãy ngắt nó ra. Nhóm các phép biến đổi liên quan lại với nhau và tách các bước logic khác nhau.
  2. Ép logic vào một dòng: Tôi từng nhồi logic phức tạp vào assign() hoặc loc() chỉ để duy trì chuỗi. Thay vào đó, hãy trích xuất logic đó ra một hàm riêng.
  3. Bỏ qua pipe() quá lâu: Tôi tránh dùng pipe() vì thấy nó không cần thiết. Nhưng nếu không có nó, bạn sẽ gặp giới hạn. Hãy sử dụng pipe() ngay khi logic của bạn ngừng trở nên đơn giản.
  4. Đặt tên kém làm giảm khả năng đọc: Khi bạn bắt đầu sử dụng các hàm tùy chỉnh với pipe(), việc đặt tên rất quan trọng. Đặt tên hàm như filter_high_revenue sẽ giúp pipeline của bạn đọc như một câu chuyện.

Kết luận: Nâng tầm trò chơi Pandas của bạn

Nếu bạn đã theo dõi đến đây, bạn đã thấy một sự thay đổi nhỏ mang lại tác động lớn. Bằng cách tư duy theo các phép biến đổi thay vì các bước, sử dụng method chaining, assign()pipe(), mã của bạn ngừng là một tập hợp các dòng code và trở thành một luồng rõ ràng, dễ đọc.

Đây chính là điều phân biệt người mới bắt đầu với người dùng Pandas ở mức trung cấp. Bạn không còn chỉ là "làm cho nó chạy". Bạn đang thiết kế phân tích của mình theo cách có thể mở rộng, dễ bảo trì và trông chuyên nghiệp với bất kỳ ai đọc nó — kể cả bản thân bạn trong tương lai.

Hãy thử lấy một notebook lộn xộn mà bạn đang làm và tái cấu trúc một phần bằng method chaining. Bạn sẽ ngạc nhiên vì notebook của mình trở nên rõ ràng hơn như thế nào ngay lập tức.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗