# Python cho Data Science: NumPy, Pandas và Scikit-Learn trong năm 2026 > Hướng dẫn thực hành NumPy 2.1, Pandas 2.2 và Scikit-Learn 1.6 với Python 3.12. Từ làm sạch dữ liệu, Feature Engineering đến xây dựng ML Pipeline sẵn sàng triển khai — kèm đầy đủ ví dụ mã nguồn. - Published: 2026-04-07 - Updated: 2026-04-07 - Author: Anthony Fillion-Maillet - Tags: data-science, python, numpy, pandas, scikit-learn, machine-learning, tutorial - Reading time: 10 min --- Python tiếp tục giữ vị thế là ngôn ngữ chủ đạo trong lĩnh vực Data Science vào năm 2026 — không phải nhờ các xu hướng nhất thời, mà nhờ hệ sinh thái trưởng thành xung quanh ba thư viện cốt lõi: NumPy, Pandas và Scikit-Learn. NumPy cung cấp các phép toán ma trận hiệu năng cao, Pandas hỗ trợ thao tác dữ liệu dạng bảng linh hoạt, và Scikit-Learn mang đến các thuật toán Machine Learning sẵn sàng sử dụng. Thành thạo ba công cụ này cho phép người làm việc với dữ liệu thực hiện toàn bộ quy trình từ xử lý dữ liệu thô đến triển khai mô hình hoàn toàn trong Python. Bài viết này trình bày một ví dụ xuyên suốt — phân tích và dự đoán việc thăng chức nhân viên — để minh họa cách sử dụng hiệu quả cả ba thư viện. > **Phiên bản sử dụng trong bài viết** > > Tất cả các đoạn mã trong bài viết được phát triển và kiểm thử với Python 3.12+, NumPy 2.1, Pandas 2.2 và Scikit-Learn 1.6. Các phiên bản cũ hơn có thể khác biệt ở một số chi tiết API, đặc biệt là hành vi Copy-on-Write của Pandas 2.2 và các tính năng tương thích Array API mới trong NumPy 2.1. ## Các phép toán mảng NumPy: Cơ bản và Boolean Indexing NumPy thay thế danh sách Python và các vòng lặp truyền thống bằng các phép toán vector hóa (vectorized operations), được thực thi bên dưới bằng mã C tối ưu. Thay vì sử dụng vòng lặp `for` để duyệt từng phần tử, các hàm NumPy hoạt động trên toàn bộ mảng cùng lúc — mang lại tốc độ nhanh hơn từ 10 đến 100 lần so với Python thuần trong các tác vụ Data Science điển hình. Ví dụ dưới đây minh họa các thao tác mảng cơ bản: tạo mảng từ danh sách, phép tính số học vector hóa, và kỹ thuật Boolean Indexing quan trọng giúp lọc dữ liệu mà không cần vòng lặp. ```python # numpy_basics.py import numpy as np # Create arrays from different sources prices = np.array([29.99, 49.99, 19.99, 99.99, 39.99]) quantities = np.arange(1, 6) # [1, 2, 3, 4, 5] # Vectorized arithmetic — no loops needed revenue = prices * quantities print(revenue) # [29.99, 99.98, 59.97, 399.96, 199.95] # Statistical aggregations print(f"Total revenue: ${revenue.sum():.2f}") # $789.85 print(f"Mean price: ${prices.mean():.2f}") # $47.99 print(f"Std deviation: ${prices.std():.2f}") # $27.64 # Boolean indexing — filter without loops premium_mask = prices > 40 premium_items = prices[premium_mask] # [49.99, 99.99] ``` Boolean Indexing là một mẫu thiết kế trung tâm trong NumPy: biểu thức `prices > 40` tạo ra một mảng gồm các giá trị `True`/`False`, đóng vai trò trực tiếp như một bộ lọc (mask). Khái niệm này sẽ xuất hiện lại trong Pandas DataFrame, bởi Pandas được xây dựng trên nền tảng NumPy. Các hàm thống kê tổng hợp — `sum()`, `mean()`, `std()` — mặc định hoạt động trên toàn bộ phần tử; thông qua tham số `axis`, chúng có thể được điều chỉnh để tính toán theo chiều cụ thể trên mảng đa chiều. ## Reshape và Broadcasting trong NumPy Mảng đa chiều xuất hiện phổ biến trong thực tế: dữ liệu doanh số theo sản phẩm và tháng, giá trị pixel trong hình ảnh, hoặc ma trận đặc trưng (feature matrix) cho các mô hình Machine Learning. NumPy cho phép biến đổi linh hoạt cấu trúc dữ liệu thông qua `reshape()`, đồng thời hỗ trợ broadcasting tự động khi thực hiện phép tính giữa các mảng có kích thước khác nhau. ```python # numpy_reshape.py import numpy as np # Monthly sales data: 4 products x 3 months sales = np.array([ [120, 150, 130], # Product A [200, 180, 220], # Product B [90, 110, 95], # Product C [300, 280, 310], # Product D ]) # Column-wise mean (average per month) monthly_avg = sales.mean(axis=0) # [177.5, 180.0, 188.75] # Row-wise sum (total per product) product_totals = sales.sum(axis=1) # [400, 600, 295, 890] # Normalize each product relative to its own max normalized = sales / sales.max(axis=1, keepdims=True) # keepdims=True preserves the shape for broadcasting print(normalized[0]) # [0.8, 1.0, 0.867] — Product A relative to its peak # Reshape for Scikit-Learn (requires 2D input) flat_sales = sales.flatten() # 1D array of 12 values reshaped = flat_sales.reshape(-1, 1) # 12x1 column vector ``` Tham số `axis` đóng vai trò quyết định: `axis=0` tổng hợp theo cột (qua các hàng), `axis=1` tổng hợp theo hàng (qua các cột). Khi chuẩn hóa với `keepdims=True`, shape `(4, 1)` được giữ nguyên, đảm bảo broadcasting hoạt động chính xác trong phép chia. Biểu thức `reshape(-1, 1)` ở cuối là một mẫu thường gặp: Scikit-Learn luôn yêu cầu đầu vào ít nhất là mảng 2D. ## Thao tác và làm sạch dữ liệu với Pandas DataFrame Pandas là công cụ chính để xử lý dữ liệu dạng bảng trong Python. Một DataFrame hoạt động tương tự như bảng cơ sở dữ liệu hoặc bảng tính Excel, nhưng cung cấp khả năng biến đổi mạnh mẽ hơn nhiều. Trong thực tế, làm sạch dữ liệu là bước tốn thời gian nhất của mọi dự án Data Science — các giá trị thiếu, ngoại lai, trùng lặp và kiểu dữ liệu không nhất quán đều cần được xử lý có hệ thống. Cách tiếp cận được khuyến nghị là xây dựng một chuỗi biến đổi có thể tái tạo (reproducible), giữ nguyên DataFrame gốc và lưu kết quả vào biến mới. Ví dụ dưới đây làm việc với tập dữ liệu gồm 1.500 hồ sơ ứng viên. ```python # pandas_cleaning.py import pandas as pd import numpy as np # Load and inspect raw data df = pd.read_csv("candidates.csv") print(df.shape) # (1500, 8) print(df.dtypes) # Check column types print(df.isna().sum()) # Count missing values per column # Clean in a reproducible chain df_clean = ( df .dropna(subset=["salary", "experience_years"]) # Drop rows missing critical fields .assign( salary=lambda x: x["salary"].clip(lower=20000, upper=500000), # Cap outliers experience_years=lambda x: x["experience_years"].astype(int), hired_date=lambda x: pd.to_datetime(x["hired_date"], errors="coerce"), ) .drop_duplicates(subset=["email"]) # Remove duplicate candidates .query("experience_years >= 0") # Filter invalid entries .reset_index(drop=True) ) print(f"Cleaned: {len(df)} -> {len(df_clean)} rows") ``` Phương thức `dropna(subset=...)` loại bỏ các hàng thiếu giá trị ở những cột quan trọng, trong khi `clip()` giới hạn các giá trị ngoại lai trong khoảng hợp lý. Chuỗi method chaining (gọi phương thức nối tiếp) giúp mã nguồn dễ đọc và dễ bảo trì — mỗi bước biến đổi được thể hiện rõ ràng trên một dòng riêng. Tham số `errors="coerce"` trong `pd.to_datetime()` chuyển các giá trị ngày không hợp lệ thành `NaT` (Not a Time) thay vì gây lỗi, cho phép quy trình xử lý tiếp tục mà không bị gián đoạn. > **Copy-on-Write trong Pandas 2.2** > > Kể từ Pandas 2.2, chế độ Copy-on-Write (CoW) được bật mặc định. Điều này có nghĩa là các phép gán như `df_clean["salary"] = ...` sẽ tạo bản sao ngầm thay vì sửa đổi trực tiếp dữ liệu gốc. Hành vi này ngăn chặn lỗi `SettingWithCopyWarning` phổ biến và giúp mã nguồn an toàn hơn, nhưng người dùng cần lưu ý rằng một số mẫu gán cũ có thể hoạt động khác so với các phiên bản Pandas trước đó. ## GroupBy, Aggregation và Feature Engineering Sau khi dữ liệu đã được làm sạch, bước tiếp theo là tổng hợp thống kê và tạo các đặc trưng (feature) phù hợp cho mô hình Machine Learning. Phương thức `groupby()` của Pandas cho phép nhóm dữ liệu theo một hoặc nhiều cột và áp dụng các hàm tổng hợp — tương tự câu lệnh `GROUP BY` trong SQL nhưng linh hoạt hơn nhiều. Để hiểu sâu hơn về kỹ thuật tạo đặc trưng, có thể tham khảo bài tập chuyên sâu về [Feature Engineering](/technologies/data-science/interview-questions/feature-engineering). ```python # pandas_groupby.py import pandas as pd # Aggregate candidate stats by department dept_stats = ( df_clean .groupby("department") .agg( avg_salary=("salary", "mean"), median_experience=("experience_years", "median"), headcount=("email", "count"), max_salary=("salary", "max"), ) .sort_values("avg_salary", ascending=False) ) print(dept_stats.head()) # Create features for ML: encode categorical + add aggregated stats df_features = ( df_clean .assign( # Ratio of individual salary to department average salary_ratio=lambda x: x["salary"] / x.groupby("department")["salary"].transform("mean"), # Time since hire in days tenure_days=lambda x: (pd.Timestamp.now() - x["hired_date"]).dt.days, # Binary encoding is_senior=lambda x: (x["experience_years"] >= 5).astype(int), ) ) ``` Phương thức `agg()` với cú pháp tên gọi (named aggregation) cho phép đặt tên rõ ràng cho từng cột kết quả, giúp mã nguồn tự giải thích. Hàm `transform("mean")` là một kỹ thuật quan trọng: thay vì trả về DataFrame đã gộp nhóm, nó phát lại (broadcast) giá trị trung bình của nhóm về từng hàng gốc — cho phép tính tỷ lệ lương cá nhân so với trung bình phòng ban mà không cần thao tác merge. Đặc trưng `tenure_days` và `is_senior` là ví dụ điển hình về Feature Engineering: chuyển đổi dữ liệu thô thành các biến có ý nghĩa thống kê mà mô hình có thể học được. ## Xây dựng Pipeline trong Scikit-Learn Scikit-Learn Pipeline kết hợp các bước tiền xử lý và huấn luyện mô hình thành một đối tượng duy nhất, đảm bảo tính nhất quán giữa quá trình huấn luyện và dự đoán. `ColumnTransformer` cho phép áp dụng các phép biến đổi khác nhau cho từng nhóm cột — ví dụ, chuẩn hóa (scaling) cho cột số và mã hóa one-hot cho cột phân loại. Đây là nền tảng cho bất kỳ dự án [phân loại có giám sát](/technologies/data-science/interview-questions/ml-supervised-classification) nào trong thực tế. ```python # sklearn_pipeline.py from sklearn.model_selection import train_test_split from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import classification_report import pandas as pd # Define column groups numeric_features = ["salary", "experience_years", "salary_ratio", "tenure_days"] categorical_features = ["department", "role_level"] target = "promoted" # Split before any preprocessing X = df_features[numeric_features + categorical_features] y = df_features[target] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # Build the preprocessing + model pipeline preprocessor = ColumnTransformer( transformers=[ ("num", StandardScaler(), numeric_features), # Scale numeric columns ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features), # Encode categories ] ) pipeline = Pipeline([ ("preprocessor", preprocessor), ("classifier", GradientBoostingClassifier( n_estimators=200, learning_rate=0.1, max_depth=4, random_state=42, )), ]) # Train and evaluate pipeline.fit(X_train, y_train) y_pred = pipeline.predict(X_test) print(classification_report(y_test, y_pred)) ``` `ColumnTransformer` cho phép định nghĩa các bước tiền xử lý riêng biệt cho từng loại cột. `StandardScaler` chuẩn hóa các đặc trưng số về trung bình 0 và độ lệch chuẩn 1. `OneHotEncoder(handle_unknown="ignore")` chuyển các giá trị phân loại thành cột nhị phân và xử lý các danh mục chưa biết xuất hiện trong tập kiểm tra một cách an toàn. Mặc dù `GradientBoostingClassifier` vốn đã hoạt động tốt với dữ liệu chưa chuẩn hóa, việc tích hợp `StandardScaler` trong pipeline giúp dễ dàng thay thế mô hình bằng các thuật toán tuyến tính hoặc SVM khi cần thiết. ## Cross-Validation và Hyperparameter Tuning Một lần chia train-test duy nhất chỉ cung cấp ước lượng không ổn định về chất lượng mô hình. Cross-validation (kiểm chứng chéo) chia dữ liệu thành nhiều fold và tính trung bình kết quả — tạo ra đánh giá đáng tin cậy hơn về khả năng tổng quát hóa của mô hình. Sau đó, `GridSearchCV` tối ưu hóa các siêu tham số (hyperparameter) một cách có hệ thống thông qua cùng cơ chế cross-validation. Để tìm hiểu sâu hơn về các thuật toán Machine Learning, có thể tham khảo bài viết [Giải thích các thuật toán Machine Learning](/blog/data-science/machine-learning-algorithms-explained). ```python # sklearn_tuning.py from sklearn.model_selection import cross_val_score, GridSearchCV import numpy as np # 5-fold cross-validation on the full pipeline scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring="f1") print(f"F1 scores: {scores}") print(f"Mean F1: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})") # Grid search over hyperparameters param_grid = { "classifier__n_estimators": [100, 200, 300], "classifier__max_depth": [3, 4, 5], "classifier__learning_rate": [0.05, 0.1, 0.2], } grid_search = GridSearchCV( pipeline, param_grid, cv=5, scoring="f1", n_jobs=-1, # Use all CPU cores verbose=1, ) grid_search.fit(X_train, y_train) print(f"Best params: {grid_search.best_params_}") print(f"Best F1: {grid_search.best_score_:.3f}") # Evaluate the best model on held-out test set best_model = grid_search.best_estimator_ print(classification_report(y_test, best_model.predict(X_test))) ``` Dấu gạch dưới kép trong `"classifier__n_estimators"` là quy ước của Scikit-Learn để truy cập tham số của các bước lồng nhau trong pipeline: `classifier` là tên bước trong pipeline, `n_estimators` là tham số của estimator bên trong. Tham số `n_jobs=-1` sử dụng tất cả các lõi CPU có sẵn, giúp tăng tốc đáng kể quá trình tìm kiếm — với lưới 3x3x3 tổ hợp và 5-fold CV, tổng cộng 135 mô hình sẽ được huấn luyện. > **Tránh Data Leakage trong Cross-Validation** > > Lỗi phổ biến nhất khi cross-validate với tiền xử lý: tính toán tham số chuẩn hóa trên toàn bộ tập huấn luyện trước khi chia fold. Điều này dẫn đến kết quả quá lạc quan vì dữ liệu validation gián tiếp ảnh hưởng đến quá trình chuẩn hóa. Scikit-Learn Pipeline giải quyết vấn đề này tự động: trong mỗi fold, toàn bộ bước tiền xử lý chỉ được fit trên training fold tương ứng, đảm bảo không có thông tin từ validation fold bị rò rỉ ngược. ## Lưu và tải mô hình cho môi trường Production Một mô hình đã huấn luyện chỉ thực sự có giá trị khi được lưu trữ (persist) và có thể tải lại trong các môi trường khác nhau — máy chủ web, quy trình xử lý batch, hoặc microservice. Scikit-Learn Pipeline có thể được tuần tự hóa (serialize) hoàn toàn: các tham số tiền xử lý (giá trị trung bình của Scaler, danh mục của Encoder) và trọng số mô hình đều được lưu trong một tệp duy nhất. Để có cái nhìn tổng quan về [hệ sinh thái Data Science](/technologies/data-science), có thể tham khảo các lộ trình học tập có sẵn trên nền tảng. ```python # sklearn_export.py import joblib from pathlib import Path # Save the complete pipeline (preprocessor + model) model_dir = Path("models") model_dir.mkdir(exist_ok=True) joblib.dump(best_model, model_dir / "promotion_model_v1.joblib") # Load and predict in a different process loaded_model = joblib.load(model_dir / "promotion_model_v1.joblib") new_data = pd.DataFrame({ "salary": [75000], "experience_years": [4], "salary_ratio": [1.05], "tenure_days": [730], "department": ["Engineering"], "role_level": ["Mid"], }) prediction = loaded_model.predict(new_data) probability = loaded_model.predict_proba(new_data)[:, 1] print(f"Promoted: {bool(prediction[0])}, Confidence: {probability[0]:.2%}") ``` Thư viện `joblib` được tối ưu hóa cho mảng số so với `pickle` và nhanh hơn đáng kể khi xử lý mô hình lớn. Điểm quan trọng: tệp `.joblib` chứa tất cả tham số Scaler và từ điển Encoder — khi tải trong tiến trình khác, không cần xử lý lại dữ liệu huấn luyện. Phương thức `predict_proba()` trả về xác suất cho từng lớp; biểu thức `[:, 1]` trích xuất xác suất của lớp dương (được thăng chức), vốn hữu ích hơn trong thực tế so với chỉ dự đoán nhị phân đơn thuần. ## Kết luận NumPy, Pandas và Scikit-Learn bổ trợ cho nhau một cách liền mạch trong quy trình Data Science: - **NumPy** cung cấp nền tảng toán học ma trận và các phép toán mảng hiệu năng cao, là cơ sở cho toàn bộ hệ sinh thái khoa học Python. - **Pandas** xây dựng trên NumPy, bổ sung khả năng thao tác dữ liệu linh hoạt với cột có tên và chỉ mục, lý tưởng cho dữ liệu dạng bảng thực tế. - **Scikit-Learn** sử dụng cả hai làm định dạng đầu vào cho các thuật toán của mình, cung cấp Pipeline nhất quán từ tiền xử lý đến triển khai mô hình. - Quy trình được trình bày trong bài viết — tải và làm sạch dữ liệu, tạo đặc trưng, xây dựng pipeline, cross-validate và lưu mô hình — không phải là lý thuyết hàn lâm mà là tiêu chuẩn thực tế trong các đội ngũ Data Science chuyên nghiệp. - Nắm vững ba thư viện này mở ra cánh cửa tới các framework phức tạp hơn như PyTorch hoặc XGBoost — vì các quy ước API của chúng đều dựa trên cùng các nguyên tắc cơ bản. Những ai muốn tìm hiểu sâu hơn về các thuật toán nền tảng có thể thực hành với các bài tập chuyên sâu về [Feature Engineering](/technologies/data-science/interview-questions/feature-engineering) và [phân loại có giám sát](/technologies/data-science/interview-questions/ml-supervised-classification) trên SharpSkill. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/data-science/python-data-science-numpy-pandas-scikit-learn-2026