Giới thiệu
Trong bài viết này, chúng ta sẽ khám phá cách giải quyết bài toán "Spot the Sale" từ Maven Analytics bằng Python và DuckDB. Đây là một thách thức thú vị giúp chúng ta ứng dụng các kỹ thuật phân tích dữ liệu để xác định các chương trình khuyến mãi mà khách hàng có thể đã bỏ lỡ.
Mục tiêu của bài viết
Chúng ta sẽ xem xét nhiều phương pháp khác nhau để giải quyết bài toán này, từ cách tiếp cận đơn giản nhất đến các phương pháp tối ưu hiệu suất.
Tải dữ liệu
Trước tiên, chúng ta cần tải dữ liệu từ các tệp CSV. Dưới đây là đoạn mã để thực hiện việc này:
python
import pandas as pd
promo_df = pd.read_csv("promotions.csv", parse_dates=["start_date", "end_date"])
orders_df = pd.read_csv("orders.csv", parse_dates=["order_date"])
Giải pháp đầu tiên: Sử dụng Hàm Định nghĩa Người Dùng (UDF)
Một trong những cách tiếp cận đầu tiên của tôi là sử dụng một hàm định nghĩa người dùng (UDF) để kiểm tra xem ngày đặt hàng có nằm trong khoảng thời gian khuyến mãi hay không:
python
def get_promo(dt: pd.Timestamp) -> str:
filtered_promo = promo_df.where(
(promo_df["start_date"] <= dt) & (promo_df["end_date"] >= dt)
).dropna()
if filtered_promo.empty:
return ""
return filtered_promo["promo_id"].values[0]
Hàm này sẽ nhận một đối tượng datetime và kiểm tra xem nó có nằm trong khoảng thời gian của một chương trình khuyến mãi hay không.
Chúng ta có thể áp dụng hàm này cho cột order_date của orders_df như sau:
python
res = orders_df.order_date.apply(lambda x: get_promo(x))
Khi thực hiện đoạn mã này, chúng ta có thể kiểm tra số lượng đơn hàng không có khuyến mãi:
python
len(res[res == ""])
Kết quả trả về là 1916, nhưng phương pháp này rất chậm, mất khoảng 1 giây để thực hiện.
Giải pháp thứ hai: Sử dụng pd.cut
Để cải thiện hiệu suất, chúng ta có thể sử dụng phương pháp pd.cut. Phương pháp này tạo ra một IntervalIndex dựa trên các ngày bắt đầu và kết thúc của từng chương trình khuyến mãi:
python
res = pd.cut(
orders_df["order_date"],
bins=pd.IntervalIndex.from_arrays(
promo_df["start_date"], promo_df["end_date"], closed="both"
),
labels=promo_df["promo_id"],
)
Thời gian thực hiện của phương pháp này đã giảm xuống còn khoảng 1.8 ms.
Giải pháp thứ ba: Sử dụng DuckDB
Nếu chúng ta chuyển đổi câu truy vấn SQL dưới đây thành biến my_q:
sql
select
count(*)
from (
SELECT od.*,
coalesce(pd.promo_id, '') as promo
FROM
orders_df as od
left join
promo_df as pd on od.order_date between pd.start_date and pd.end_date
) t
where 1=1
and promo = ''
Thì khi chạy duckdb.sql(my_q) sẽ mất chỉ 430 μs, điều này cho thấy sự cải thiện đáng kể về hiệu suất.
Giải pháp từ Maven
Cuối cùng, giải pháp Python được cung cấp trong video là:
python
pd.merge_asof(
orders_df.sort_values("order_date", ascending=True),
promo_df.sort_values("start_date", ascending=True),
left_on="order_date",
right_on="start_date",
direction="backward",
).query("order_date <= end_date")
Giải pháp này chạy trong 1.2 ms, nhanh hơn so với phương pháp cut, nhưng chậm hơn so với phương pháp duckdb. Hơn nữa, merge_asof không phổ biến nên có thể gây khó khăn cho việc đọc hiểu mã.
Bảng so sánh các giải pháp
Dưới đây là bảng tóm tắt so sánh các giải pháp:
| Giải pháp | Thời gian | Ưu điểm | Nhược điểm |
|---|---|---|---|
| Naive | 1 s | Dễ đọc / Linh hoạt | Rất chậm |
cut |
~2 ms | Nhanh / Linh hoạt | Khó đọc hơn một chút |
merge_asof |
~1 ms | Còn nhanh hơn | Khó đọc và điều chỉnh hơn |
duckdb |
430 μs | Nhanh nhất / Dựa trên SQL | Cần thêm một phụ thuộc trên pandas |
Thực hành tốt nhất
- Sử dụng phương pháp
duckdbkhi bạn có khối lượng dữ liệu lớn, vì nó cho hiệu suất tốt nhất. - Tối ưu hóa mã bằng cách sử dụng các phương pháp như
pd.cutđể cải thiện tốc độ truy xuất dữ liệu.
Những cạm bẫy thường gặp
- Sử dụng UDF có thể dẫn đến hiệu suất kém, đặc biệt với dữ liệu lớn.
- Bỏ qua kiểm tra dữ liệu có thể dẫn đến sai sót trong kết quả phân tích.
Mẹo hiệu suất
- Sử dụng các thư viện tối ưu hóa như
duckdbcó thể cải thiện đáng kể thời gian thực thi các truy vấn phức tạp. - Giảm thiểu số lượng phép toán trong các vòng lặp để nâng cao hiệu suất tổng thể.
Kết luận
Trong bài viết này, chúng ta đã xem xét nhiều phương pháp khác nhau để giải quyết bài toán "Spot the Sale" với Python và DuckDB. Hy vọng bạn đã tìm thấy thông tin hữu ích và có thể áp dụng vào dự án của mình. Đừng ngần ngại thử nghiệm các phương pháp khác nhau để tìm ra giải pháp tốt nhất cho nhu cầu của bạn!
Câu hỏi thường gặp (FAQ)
1. Tại sao nên sử dụng DuckDB thay vì Pandas?
DuckDB cung cấp hiệu suất cao hơn cho các truy vấn SQL phức tạp, đặc biệt là với dữ liệu lớn.
2. Làm thế nào để tối ưu hóa mã Python của tôi?
Sử dụng các phương pháp như pd.cut và tránh sử dụng UDF cho các phép toán phức tạp để cải thiện tốc độ.
3. Có cần cài đặt thêm thư viện nào không?
Có, nếu bạn sử dụng DuckDB, bạn cần cài đặt nó bổ sung vào môi trường phát triển của mình.