diff --git a/blog/2022-02-15-xai2-thuc-hanh.md b/blog/2022-02-15-xai2-thuc-hanh.md new file mode 100644 index 00000000..7449590e --- /dev/null +++ b/blog/2022-02-15-xai2-thuc-hanh.md @@ -0,0 +1,354 @@ +# 🔥XAI - Thực hành Biểu đồ Phụ thuộc Từng phần (PDP)🔥 + +## 1. Tạo mô hình với bộ dữ liệu về giá nhà trung bình California Housing Dataset + +California Housing Dataset gồm 20640 bản ghi với 8 đặc trưng dự đoán và nhãn `target`, trong đó các đặc trưng mang ý nghĩa như sau: + +|Đặc trưng| Ý nghĩa| +|-|-| +|`MedInc` | Thu nhập trung bình | +|`HouseAge` | Tuổi nhà trung bình | +|`AveRooms` | Số phòng trung bình | +|`AveBedrms` | Số phòng ngủ trung bình | +|`Population`| Số thành viên hộ gia đình trung bình | +|`AveOccup` | Số thành viên hộ gia đình trung bình | +|`Latitude` | Vĩ độ | +|`Longitude` | Kinh độ | + +Đây là tập dữ liệu có sẵn trong thư viện Sklearn nên chúng ta hoàn toàn có thế gọi ra thông qua hàm `fetch_california_housing()`. Tương tự như các bài toán học máy thông thường, ta chia dữ liệu thành tập huấn luyện và tập kiểm thử và sử dụng mô hình Random Forest để huấn luyện và dự đoán trên cả 2 tập dữ liệu đã chia. + + +```python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +from sklearn.datasets import fetch_california_housing +from sklearn.model_selection import train_test_split +from sklearn.ensemble import RandomForestRegressor +from sklearn.inspection import permutation_importance +from sklearn.metrics import mean_squared_error +``` + + +```python +# Khởi tạo dữ liệu +housing = fetch_california_housing() +X = pd.DataFrame(housing.data, columns=housing.feature_names) +Y = pd.Series(housing.target) +X.info() +``` + + + RangeIndex: 20640 entries, 0 to 20639 + Data columns (total 8 columns): + # Column Non-Null Count Dtype + --- ------ -------------- ----- + 0 MedInc 20640 non-null float64 + 1 HouseAge 20640 non-null float64 + 2 AveRooms 20640 non-null float64 + 3 AveBedrms 20640 non-null float64 + 4 Population 20640 non-null float64 + 5 AveOccup 20640 non-null float64 + 6 Latitude 20640 non-null float64 + 7 Longitude 20640 non-null float64 + dtypes: float64(8) + memory usage: 1.3 MB + + + +```python +# Chia dữ liệu thành tập huấn luyện và tập kiểm thử +X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.2, random_state=42) +# Khởi tạo mô hình Random Forest +regr = RandomForestRegressor(max_depth = 5, random_state = 42, n_estimators = 100) +# Fit dữ liệu huấn luyện vào mô hình đã khởi tạo +regr.fit(X_train, Y_train) + +# Dự đoán nhãn trên tập huấn luyện và tập kiểm thử +Y_predict_train = regr.predict(X_train) +Y_predict = regr.predict(X_test) +``` + +## 2. Lựa chọn những đặc trưng quan trọng (feature importance) + +Một trong những lí do chúng mình chọn mô hình Random Forest đó là bởi chúng ta có thể dễ dàng trực quan những đặc trưng quan trọng, được sử dụng nhiều trong mô hình. Để đánh giá đặc trưng quan trọng cũng như trực quan hoá thứ tự quan trọng của các đặc trưng này, chúng ta có thể làm như sau: + + +```python +# Hàm trực quan hoá trung bình mức quan trọng của các đặc trưng +def feature_plot_importance(importance_df): + plt.rcParams.update({'font.size': 16}) + plt.rcParams["figure.figsize"] = (12,5) + fig, ax = plt.subplots() + x = np.arange(len(importance_df)) + width = 0.77 + rects1 = ax.bar(x, + height = importance_df['means'], + width = width, + yerr = importance_df['stds'], + align='center', + ecolor='black', + capsize=5) + ax.set_ylabel('Feature Importance') + ax.set_xticks(x) + ax.set_xticklabels(importance_df['feature_labels']) + ax.set_title('Feature Importance Initial Model') + plt.tight_layout() +``` + + +```python +# Đánh giá đặc trưng quan trọng trên tập huấn luyện +perm_feature_importance = permutation_importance(regr, X_train, Y_train) + +# Tạo DataFrame chứa nhãn và trung bình, độ lệch chuẩn mức quan trọng +importance_df = pd.DataFrame({'feature_labels': X_train.columns, + 'means' : perm_feature_importance['importances_mean'], + 'stds' : perm_feature_importance['importances_std']}) + +# Trực quan hoá trung bình mức quan trọng của các đặc trưng +feature_plot_importance(importance_df) +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_6_0.png) + + + +Dựa vào feature importance từ mô hình Random Forest, có thể thấy `MedInc`, `AveOccup`, `HouseAge` là 3 đặc trưng quan trọng. Ta hoàn toàn có thể xây dựng 1 mô hình Random Forest mới với chỉ những đặc trưng quan trọng. + + +```python +# Chọn lọc những đặc trưng có điểm feature importance cao +X_train_reduced = X_train.loc[:,['MedInc','HouseAge','AveOccup']] + +# Khởi tạo mô hình Random Forest +regr = RandomForestRegressor(max_depth = 5, random_state = 42, n_estimators = 100) +regr.fit(X_train_reduced, Y_train) + +perm_feature_importance = permutation_importance(regr, X_train_reduced, Y_train) + +# Tạo DataFrame chứa nhãn và trung bình, độ lệch chuẩn mức quan trọng +importance_df_reduced = pd.DataFrame({'feature_labels': X_train_reduced.columns, + 'means' : perm_feature_importance['importances_mean'], + 'stds' : perm_feature_importance['importances_std']}) + +# Trực quan hoá trung bình mức quan trọng của các đặc trưng +feature_plot_importance(importance_df_reduced) +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_8_0.png) + + + +Dựa vào feature importance từ mô hình Random Forest, có thể thấy `MedInc`, `AveOccup`, `HouseAge` là 3 đặc trưng quan trọng. Nhưng chỉ dựa vào đặc trưng thì khó có thể thấy được mối liên hệ giữa đặc trưng đó với việc mô hình đưa ra kết quả dự đoán. Để giải quyết vấn đề này, chúng ta sẽ cùng nhau đến với Biểu đồ Phụ thuộc Từng phần (Partial Dependence Plot). + +# 3. Minh họa cách tạo ra Biểu đồ Phụ thuộc Từng phần +### 3.1. Sử dụng Scikit-Learn +Biểu đồ PDP: +- 1D: biểu diễn sự phụ thuộc giữa mục tiêu dự đoán và những đặc trương được quan tâm (features of interest - những đặc trưng được lựa chọn bởi feature importance) +- 2D: biểu diễn sự phụ thuộc giữa 2 đặc trưng được quan tâm và tác động của nó lên mục tiêu dự đoán + +Chúng ta có thể biểu diễn biểu đồ này thông qua `PartialDependenceDisplay.from_estimator()` từ thư viện `sklearn.inspection`. + + +```python +from sklearn.inspection import PartialDependenceDisplay +``` + +Với biểu đồ DPD 1D, ta có: + + +```python +PartialDependenceDisplay.from_estimator(regr, + X_train_reduced, + features = ['MedInc'], + percentiles = [0,1]); +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_13_0.png) + + + + +```python +PartialDependenceDisplay.from_estimator(regr, + X_train_reduced, + features = ['HouseAge'], + percentiles = [0,1]); +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_14_0.png) + + + + +```python +PartialDependenceDisplay.from_estimator(regr, + X_train_reduced, + features = ['AveOccup'], + percentiles = [0,1]); +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_15_0.png) + + + +Với biểu đồ DPD 2D, ta có: + + +```python +MedInc_AvcOccup_disp = PartialDependenceDisplay.from_estimator(regr, + X_train_reduced, + features = [(0,2)], + percentiles = [0,1], + grid_resolution = 20) +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_17_0.png) + + + + +```python +MedInc_HouseAge_disp = PartialDependenceDisplay.from_estimator(regr, + X_train_reduced, + features = [(0,1)], + percentiles = [0,1], + grid_resolution = 20) +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_18_0.png) + + + + +```python +AveOccup_HouseAge_disp = PartialDependenceDisplay.from_estimator(regr, + X_train_reduced, + features = [(2,1)], + percentiles = [0,1], + grid_resolution = 10) +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_19_0.png) + + + +Ta hoàn toàn có thể thêm các điểm dữ liệu vào biểu đồ PDP bằng cách sau: + + +```python +fig, ax = plt.subplots(figsize=(12, 5)) +AveOccup_HouseAge_disp.plot(ax = ax) +plt.scatter(X_train_reduced['AveOccup'], + X_train_reduced['HouseAge'], + alpha = 0.2, + color = 'black'); +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_21_0.png) + + + +## 3.2. Sử dụng thư viện PDPbox +- Cài đặt thư viện: !pip install pdpbox +- Link: https://github.com/SauceCat/PDPbox + + +```python +from pdpbox import pdp +``` + +Có thể thấy tương tự như biểu đồ PDP của Scikit-Learn, với đặc trưng `MedInc`, giá nhà trung bình cũng tăng khi thu nhập trung bình tăng (với `MedInc` lớn hơn 2). Với thu nhập trung bình lớn hơn khoảng 6, giá nhà tăng khá nhiều và ổn định. + + +```python +pdp_MedInc = pdp.pdp_isolate(model=regr, + dataset=X_train_reduced, + model_features=X_train_reduced.columns, + feature='MedInc') +fig, axes = pdp.pdp_plot(pdp_MedInc, 'MedInc', plot_lines=True, frac_to_plot=100, plot_pts_dist=True); +``` + + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. + + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_25_1.png) + + + + +```python +pdp_MedInc = pdp.pdp_isolate(model=regr, + dataset=X_train_reduced, + model_features=X_train_reduced.columns, + feature='HouseAge') +fig, axes = pdp.pdp_plot(pdp_MedInc, 'HouseAge', plot_lines=True, frac_to_plot=100, plot_pts_dist=True); +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_26_0.png) + + + + +```python +pdp_MedInc = pdp.pdp_isolate(model=regr, + dataset=X_train_reduced, + model_features=X_train_reduced.columns, + feature='AveOccup') +fig, axes = pdp.pdp_plot(pdp_MedInc, 'AveOccup', plot_lines=True, frac_to_plot=100, plot_pts_dist=True); +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_27_0.png) + + + +Thư viện PDPbox cũng cung cấp cả 2 biểu đồ dưới dạng 1D và 2D. Biều đồ 2D phía dưới cho thấy mối quan hệ của giá nhà trung bình với 2 đặc trưng là thu nhập bình quân và độ tuổi của nhà. + + +```python +feats = ['MedInc','HouseAge'] +p = pdp.pdp_interact(regr,X_train_reduced, + X_train_reduced.columns, + feats) +pdp.pdp_interact_plot(p,feats); +``` + + + +![png](2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_29_0.png) + + + +Đón xem những trải nghiệm và phân tích cụ thể từ Zootopi tại: + +- 👉Website: https://zootopi.dev/blog +- 👉Youtube: https://youtu.be/jCCbCPVXcpQ +- 👉Facebook: https://www.facebook.com/aizootopi diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_13_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_13_0.png new file mode 100644 index 00000000..563e38b9 Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_13_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_14_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_14_0.png new file mode 100644 index 00000000..a4f0bc0e Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_14_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_15_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_15_0.png new file mode 100644 index 00000000..eac2a419 Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_15_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_17_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_17_0.png new file mode 100644 index 00000000..0f0439b0 Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_17_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_18_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_18_0.png new file mode 100644 index 00000000..e3014723 Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_18_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_19_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_19_0.png new file mode 100644 index 00000000..ad89e96e Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_19_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_21_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_21_0.png new file mode 100644 index 00000000..a0dc8803 Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_21_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_25_1.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_25_1.png new file mode 100644 index 00000000..975d298d Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_25_1.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_26_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_26_0.png new file mode 100644 index 00000000..5d26ceb3 Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_26_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_27_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_27_0.png new file mode 100644 index 00000000..dfc4358f Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_27_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_29_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_29_0.png new file mode 100644 index 00000000..ee3459ca Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_29_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_6_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_6_0.png new file mode 100644 index 00000000..058bcfd7 Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_6_0.png differ diff --git a/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_8_0.png b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_8_0.png new file mode 100644 index 00000000..52b894ab Binary files /dev/null and b/blog/2022-02-15-xai2-thuc-hanh_files/2022-02-15-xai2-thuc-hanh_8_0.png differ diff --git a/docs/certificate/gcp_ace.md b/docs/certificate/gcp_ace.md new file mode 100644 index 00000000..5ca3027f --- /dev/null +++ b/docs/certificate/gcp_ace.md @@ -0,0 +1,272 @@ +--- +id: certificate-gcp-ace +sidebar_position: 2 +slug: /gcp-ace +--- + +# Chứng chỉ Associate Cloud Engineer từ Google Cloud + +:::info 📚 Tham khảo tài liệu học tập +Đang chuẩn bị thi chứng chỉ này? Bạn có thể tham khảo **[tài liệu học tập chi tiết trên GitHub](https://github.com/honghanhh/gcp-ace-cert)**. + +Repo này tổng hợp các tài nguyên, chiến lược học tập và tips để vượt qua kỳ thi Associate Cloud Engineer mà không tốn phí. Bao gồm learning plans, practice questions, và các resources hữu ích khác. +::: + +## 🎯 Ai nên học chứng chỉ này? + +Nếu bạn muốn làm việc với Google Cloud Platform (GCP) và cần chứng minh khả năng triển khai, quản lý và bảo mật ứng dụng và hạ tầng trên Google Cloud, thì **Associate Cloud Engineer (ACE)** là chứng chỉ phù hợp để bắt đầu. + +Đây là chứng chỉ entry-level của Google Cloud, phù hợp cho: +- Developers muốn làm việc với cloud +- System administrators muốn chuyển sang cloud +- Những người muốn có nền tảng vững chắc về GCP trước khi học các chứng chỉ nâng cao hơn + +## 📖 Giới thiệu + +**Associate Cloud Engineer** là chứng chỉ entry-level của Google Cloud, xác nhận khả năng triển khai, quản lý và bảo mật ứng dụng và hạ tầng trên Google Cloud Platform. + +Chứng chỉ này phù hợp cho những ai muốn bắt đầu với GCP và cần nền tảng vững chắc trước khi học các chứng chỉ nâng cao hơn. + +## 🎓 Bạn sẽ học được gì? + +Sau khi hoàn thành và đạt chứng chỉ này, bạn sẽ có thể: + +- Thiết lập môi trường cloud solution (resource hierarchies, IAM, billing) +- Lập kế hoạch và cấu hình giải pháp cloud (compute, storage, networking) +- Triển khai giải pháp cloud (Compute Engine, GKE, Cloud Run) +- Đảm bảo vận hành thành công (monitoring, logging, resource management) +- Cấu hình quyền truy cập và bảo mật (IAM policies, security measures, audit logs) + +## 📋 Yêu cầu tiên quyết + +Trước khi bắt đầu, bạn nên có: + +- Hiểu cơ bản về cloud computing +- Kiến thức cơ bản về Linux command line +- Hiểu cơ bản về networking và hệ thống +- Kinh nghiệm với command line tools (không bắt buộc nhưng hữu ích) + +**Lưu ý:** Đây là chứng chỉ entry-level, nên không yêu cầu kinh nghiệm sâu về GCP. Tuy nhiên, có kiến thức cơ bản về cloud computing sẽ giúp bạn học hiệu quả hơn. + +## 📚 Nội dung kỳ thi + +**Thông tin kỳ thi:** +- **Thời gian:** 120 phút +- **Format:** Multiple choice và multiple select questions +- **Ngôn ngữ:** Tiếng Anh, Nhật, Tây Ban Nha, Indonesia +- **Lệ phí:** USD 125 (cộng thuế nếu có) + +**Các domain chính trong kỳ thi:** + +1. **Setting Up a Cloud Solution Environment (20%)** + - Tạo resource hierarchies và áp dụng organizational policies + - Quản lý IAM roles + - Bật APIs và cấu hình billing accounts + +2. **Planning and Configuring a Cloud Solution (17.5%)** + - Chọn compute options phù hợp (Compute Engine, GKE) + - Chọn data storage solutions (Cloud SQL, BigQuery) + - Thiết kế network resources, load balancing + +3. **Deploying and Implementing a Cloud Solution (25%)** + - Launch và quản lý compute instances + - Deploy applications với Kubernetes và Cloud Run + - Triển khai data solutions và cấu hình networking + +4. **Ensuring Successful Operation of a Cloud Solution (20%)** + - Monitoring và logging với Cloud Operations suite + - Quản lý compute, storage, và networking resources + - Infrastructure as code + +5. **Configuring Access and Security (17.5%)** + - Quản lý IAM policies và service accounts + - Triển khai security measures + - Cấu hình audit logs và monitoring access + +## 🎁 Chương trình Get Certified 2025 (Miễn phí!) + +:::tip 🎉 Cơ hội học miễn phí +Nếu bạn đang chuẩn bị thi chứng chỉ này, hãy tìm hiểu về chương trình **Get Certified 2025** - một chương trình miễn phí từ Google và các đối tác! + +Chương trình này cung cấp nhiều lợi ích để giúp bạn tiếp cận tài nguyên và kỳ thi: +::: + +### Lợi ích của chương trình: + +1. **📅 8 week guided learning program** + - Lộ trình học có hướng dẫn trong 8 tuần + - Giúp bạn có kế hoạch học tập rõ ràng và có cấu trúc + +2. **👥 Technical membership** + - Exam guide review sessions (các buổi review exam guide) + - Hỗ trợ qua Online community + - Kết nối với cộng đồng học viên khác + +3. **🎫 Exam voucher information (FREE 💸💸💸)** + - Thông tin về exam voucher miễn phí + - Giúp bạn tiết kiệm chi phí thi (thay vì phải trả USD 125) + +4. **💻 400 free credits cho hands-on labs** + - 400 credits miễn phí trên Google Cloud Skill Boost + - Đủ để thực hành các labs quan trọng mà không lo về chi phí + +### Lời khuyên: + +**Nên tuân theo lộ trình học 8 tuần được đề xuất bởi Get Certified** để tận dụng tối đa các tài nguyên và hỗ trợ từ chương trình. Để biết cách đăng ký và thông tin cập nhật, hãy tham khảo [repo GitHub này](https://github.com/honghanhh/gcp-ace-cert). + + +## 📈 Lộ trình học đề xuất + +Dựa trên kinh nghiệm từ cộng đồng và chương trình Get Certified, bạn có thể sắp xếp như sau: + +``` +Tuần 1-2: Nền tảng GCP + ↓ +Tuần 3-4: Compute và Storage + ↓ +Tuần 5-6: Networking và Security + ↓ +Tuần 7-8: Monitoring, Logging và Operations + ↓ +Tuần 9: Ôn tập và làm practice exams + ↓ +🎉 Sẵn sàng thi! +``` + +**Tổng thời gian:** Khoảng 8-10 tuần (tùy vào thời gian bạn dành mỗi tuần) + +**Lưu ý:** Thời gian học có thể khác nhau tùy vào tốc độ và kinh nghiệm của mỗi người. Nếu tham gia chương trình Get Certified 2025, hãy tuân theo lộ trình 8 tuần được đề xuất trong chương trình. + +## 📖 Tài nguyên học tập + +### Tài liệu chính thức + +| Loại | Link | Mô tả | +|:-----|:-----|:------| +| **Exam Guide** | [Certification Exam Guide](https://services.google.com/fh/files/misc/associate_cloud_engineer_exam_guide_english.pdf) | ⚠️ **PHẢI ĐỌC** - Hướng dẫn chi tiết về các chủ đề thi | +| **Learning Path** | [Cloud Engineer Learning Path](https://www.cloudskillsboost.google/paths/11) | Khóa học và labs thực hành trên Google Cloud Skill Boost | +| **Practice Labs** | [Skill Boost ARCADE](https://go.cloudskillsboost.google/arcade) | Labs thực hành miễn phí để luyện tập | + +### Tài liệu bổ sung + +- **Sách:** [Associate Cloud Engineer Study Guide](http://repo.darmajaya.ac.id/4482/1/Official%20Google%20Cloud%20Certified%20Associate%20Cloud%20Engineer%20Study%20Guide%20%28%20PDFDrive%20%29.pdf) - Tài liệu tham khảo chi tiết +- **Video:** [Google Cloud Associate Cloud Engineer Course](https://www.youtube.com/watch?v=jpno8FSqpc8) - Khóa học miễn phí trên YouTube + +### Practice Questions + +- [Associate Cloud Engineer Sample Questions](https://cloud.google.com/learn/certification/associate-cloud-engineer) - Câu hỏi mẫu từ Google +- [Real Google Associate Cloud Engineer Study Questions](https://github.com/honghanhh/gcp-ace-cert) - 14 PDFs với câu hỏi và đáp án +- [LearnGood](https://github.com/honghanhh/gcp-ace-cert) - 193 câu hỏi bao phủ tất cả chủ đề thi + +### Tài liệu tham khảo từ cộng đồng + +**[📚 Tài liệu học tập chi tiết trên GitHub](https://github.com/honghanhh/gcp-ace-cert)** + +Repo này tổng hợp đầy đủ: +- Learning plans 8 tuần (theo chương trình Get Certified) +- Danh sách các tài nguyên học tập +- Practice questions và sample exams +- Tips và chiến lược học tập +- Thông tin về chương trình Get Certified 2025 + +## 💡 Một số lưu ý khi học + +### Về độ khó + +Đây là chứng chỉ **entry-level**, nhưng không có nghĩa là dễ. Bạn cần: +- Hiểu rõ các dịch vụ cơ bản của GCP +- Biết cách sử dụng gcloud CLI và Cloud Console +- Hiểu về networking, security, và monitoring trên cloud + +Nếu bạn chưa có nền tảng về cloud computing, nên dành thời gian làm quen với các khái niệm cơ bản trước. + +### Về thời gian + +Thời gian học có thể khác nhau tùy vào: +- Tốc độ học của mỗi người +- Kinh nghiệm trước đó với cloud computing +- Mức độ chi tiết bạn muốn đi sâu + +Thông thường, nếu dành 10-15 giờ/tuần, bạn có thể sẵn sàng sau 8-10 tuần. + +### Tips học hiệu quả + +- **Thực hành:** Làm các labs trên Google Cloud Skill Boost và sử dụng GCP free tier để thực hành +- **Làm practice exams:** Làm nhiều practice questions để quen với format và timing +- **Tham gia cộng đồng:** Join các group học GCP để hỏi đáp và chia sẻ +- **Đọc exam guide:** Đọc kỹ [exam guide chính thức](https://services.google.com/fh/files/misc/associate_cloud_engineer_exam_guide_english.pdf) để biết trọng tâm +- **Tham khảo tài liệu:** Sử dụng [tài liệu học tập trên GitHub](https://github.com/honghanhh/gcp-ace-cert) để có lộ trình rõ ràng + +### Về chi phí + +- **Lệ phí thi:** USD 125 (hoặc miễn phí nếu có exam voucher từ chương trình Get Certified 2025 - xem chi tiết ở section [Chương trình Get Certified 2025](#-chương-trình-get-certified-2025-miễn-phí) phía trên) +- **Free tier:** GCP có $300 free credits cho 90 ngày đầu +- **Labs:** Google Cloud Skill Boost có một số labs miễn phí, hoặc 400 free credits nếu tham gia Get Certified 2025 + +## ❓ Câu hỏi thường gặp + +
+Tôi có cần kinh nghiệm về GCP trước khi thi không? + +Không bắt buộc, nhưng nên có. Đây là chứng chỉ entry-level, nhưng bạn vẫn cần hiểu cách sử dụng các dịch vụ GCP. Nên dành thời gian thực hành trên GCP free tier trước khi thi. +
+ +
+Làm thế nào để thực hành mà không tốn phí? + +Bạn có thể thực hành miễn phí bằng cách: + +- Sử dụng GCP free tier ($300 credits trong 90 ngày) +- Làm các labs miễn phí trên Google Cloud Skill Boost +- Sử dụng các sandbox environments có sẵn +- Tham gia chương trình Get Certified 2025 để nhận 400 free credits cho labs và exam voucher miễn phí + +Xem chi tiết về chương trình Get Certified 2025 ở section [Chương trình Get Certified 2025](#-chương-trình-get-certified-2025-miễn-phí) phía trên. +
+ +
+Kỳ thi có khó không? + +Độ khó phụ thuộc vào mức độ chuẩn bị của bạn. Nếu bạn đã thực hành đầy đủ và làm nhiều practice questions, kỳ thi sẽ không quá khó. Quan trọng là hiểu cách các dịch vụ GCP hoạt động và cách sử dụng chúng trong thực tế. +
+ +
+Tôi nên học trong bao lâu? + +Tùy vào thời gian bạn dành mỗi tuần. Thông thường, nếu dành 10-15 giờ/tuần, bạn có thể sẵn sàng sau 8-10 tuần. Quan trọng là thực hành đều đặn, không chỉ đọc lý thuyết. Xem thêm ở section [Lộ trình học đề xuất](#-lộ-trình-học-đề-xuất) phía trên. +
+ +
+Có cần biết lập trình không? + +Không bắt buộc, nhưng biết một chút scripting (bash, Python) sẽ hữu ích. Quan trọng hơn là hiểu cách sử dụng gcloud CLI và Cloud Console để quản lý resources. +
+ +
+Chương trình Get Certified 2025 là gì và làm sao để đăng ký? + +Xem chi tiết về chương trình Get Certified 2025 ở section [Chương trình Get Certified 2025](#-chương-trình-get-certified-2025-miễn-phí) phía trên. Để biết cách đăng ký và thông tin cập nhật nhất, hãy tham khảo [repo GitHub này](https://github.com/honghanhh/gcp-ace-cert). +
+ +## 📚 Tài liệu tham khảo thêm + +Nếu muốn tìm hiểu sâu hơn về các dịch vụ GCP: + +- [Google Cloud Documentation](https://cloud.google.com/docs) +- [gcloud CLI Reference](https://cloud.google.com/sdk/gcloud/reference) +- [Google Cloud Architecture Center](https://cloud.google.com/architecture) +- [Google Cloud Blog](https://cloud.google.com/blog) + +## 🎬 Bắt đầu học + +Nếu bạn đã sẵn sàng, có thể bắt đầu tại: + +- **[Đăng ký thi trên Google Cloud](https://cloud.google.com/certification/cloud-engineer)** +- **[Tham khảo tài liệu học tập trên GitHub](https://github.com/honghanhh/gcp-ace-cert)** +- **[Bắt đầu Learning Path trên Skill Boost](https://www.cloudskillsboost.google/paths/11)** + +:::tip 💡 Gợi ý +Khi học, hãy kết hợp giữa đọc tài liệu và thực hành. Đừng chỉ đọc, hãy tạo tài khoản GCP và thử nghiệm các dịch vụ. Làm nhiều practice questions để quen với format thi. Nếu gặp khó khăn, có thể tham khảo [tài liệu học tập trên GitHub](https://github.com/honghanhh/gcp-ace-cert) để có hướng dẫn chi tiết. +::: + +Chúc các bạn học tập hiệu quả! diff --git a/docs/certificate/ibm_ads.md b/docs/certificate/ibm_ads.md index fb668d21..6c448052 100644 --- a/docs/certificate/ibm_ads.md +++ b/docs/certificate/ibm_ads.md @@ -6,79 +6,241 @@ slug: / # Khoá học chuyên sâu về Khoa học Dữ liệu từ IBM -## 1. Giới thiệu +:::info 💻 Tham khảo bài giải của ZootoPi +Đang học khoá này và gặp khó khăn? Bạn có thể tham khảo **[bài giải chi tiết của ZootoPi trên GitHub](https://github.com/ZootoPi/advanced_data_science_ibm)**. -Advanced Data Science with IBM Specialization (Khoá học chuyên sâu về Khoa học Dữ liệu nâng cao) là 1 trong những khoá học phù hợp với những bạn đã có nền tảng về khoa học dữ liệu cũng như kỹ năng lập trình cơ bản trong python, SQL cơ bản... và muốn tìm hiểu sâu hơn về xử lý dữ liệu lớn song song, khám phá và trực quan hóa dữ liệu cũng như học máy và học sâu ở mức nâng cao. +Chúng mình đã giải và giải thích các bài tập trong khoá học để bạn có thể tham khảo khi cần. Tuy nhiên, nên tự làm trước, chỉ tham khảo khi thực sự cần thiết để đảm bảo bạn thực sự hiểu và nắm vững kiến thức. +::: + +## 🎯 Ai nên học khoá này? + +Nếu bạn đã có nền tảng về khoa học dữ liệu và kỹ năng lập trình Python, SQL cơ bản, và muốn đi sâu hơn vào xử lý dữ liệu lớn, học máy và học sâu ở mức nâng cao, thì **Advanced Data Science with IBM Specialization** có thể là lựa chọn phù hợp. ![IBM Specialization Certificate](./img/advanced_ds.png) -Về mặt lý thuyết, khoá học sẽ giúp bạn hiểu các cơ sở toán học đằng sau tất cả các thuật toán học máy và học sâu. Từ đó, bạn có thể áp dụng những kiến thức này vào trong thực tế dự án, đánh giá cách chúng tác động đến hiệu suất và khả năng mở rộng của mô hình cũng như giúp bạn đưa ra những quyết định đúng đắn về kiến ​​trúc. +## 📖 Giới thiệu + +Khoá học này tập trung vào hai khía cạnh chính: + +**Về lý thuyết:** Khoá học giúp bạn hiểu các cơ sở toán học đằng sau các thuật toán học máy và học sâu. Kiến thức này giúp bạn đánh giá cách các thuật toán tác động đến hiệu suất và khả năng mở rộng của mô hình, từ đó đưa ra quyết định về kiến trúc phù hợp. + +**Về thực hành:** Bạn sẽ được làm quen với chu trình xử lý dữ liệu, học máy và học sâu từ đầu đến cuối thông qua các công nghệ như Apache Spark, scikit-learn, SparkML, SystemML, TensorFlow, Keras, PyTorch, DeepLearning4J, Apache CouchDB và MQTT. + +## 🎓 Bạn sẽ học được gì? + +Sau khi hoàn thành khoá học này, bạn sẽ có thể: + +- Xử lý và phân tích dữ liệu lớn với Apache Spark +- Xây dựng và tối ưu hóa mô hình Machine Learning +- Áp dụng Deep Learning vào các bài toán thực tế (NLP, Computer Vision, Time Series) +- Hiểu sâu về toán học đằng sau các thuật toán ML/DL +- Sử dụng các framework: TensorFlow, PyTorch, Keras, SparkML +- Triển khai mô hình ở quy mô lớn với Kubernetes và GPU + +## 📋 Yêu cầu tiên quyết + +Trước khi bắt đầu, bạn nên có: + +- Kiến thức Python cơ bản (variables, functions, loops, data structures) +- Hiểu cơ bản về SQL +- Nền tảng về Data Science (EDA, basic ML concepts) +- Kinh nghiệm với Jupyter Notebooks + +**Lưu ý:** Nếu bạn chưa có nền tảng về lập trình cơ bản hay khoa học dữ liệu, nên học trước khoá [IBM Data Science Professional Certificate](https://www.coursera.org/professional-certificates/ibm-data-science) để dễ dàng tiếp thu kiến thức trong khoá học này. + +## 📚 Nội dung khoá học + +**[Đăng ký khoá học trên Coursera](https://www.coursera.org/specializations/advanced-data-science-ibm)** + +Khoá học bao gồm 4 khoá nhỏ, tổng cộng khoảng 80 giờ học: + +1. **Khoá 1:** [Fundamentals of Scalable Data Science](https://www.coursera.org/learn/ds) - 20 giờ +2. **Khoá 2:** [Advanced Machine Learning and Signal Processing](https://www.coursera.org/learn/advanced-machine-learning-signal-processing) - 27 giờ +3. **Khoá 3:** [Applied AI with DeepLearning](https://www.coursera.org/learn/ai) - 24 giờ +4. **Khoá 4:** [Advanced Data Science Capstone](https://www.coursera.org/learn/advanced-data-science-capstone) - 9 giờ + +## 📈 Lộ trình học đề xuất + +Dựa trên thời gian ước tính, bạn có thể sắp xếp như sau: + +``` +Tuần 1-2: Khoá 1 - Fundamentals (20 giờ) + ↓ +Tuần 3-5: Khoá 2 - Advanced ML (27 giờ) + ↓ +Tuần 6-8: Khoá 3 - Deep Learning (24 giờ) + ↓ +Tuần 9: Khoá 4 - Capstone Project (9 giờ) + ↓ +🎉 Hoàn thành chứng chỉ! +``` + +**Lưu ý:** Thời gian học có thể khác nhau tùy vào tốc độ và kinh nghiệm của mỗi người. Coursera cho phép bạn học theo tốc độ của riêng mình, nên bạn có thể điều chỉnh lộ trình phù hợp. + +## 📖 Chi tiết từng khoá học + +### Khoá 1: Fundamentals of Scalable Data Science + +**Thời gian:** 20 giờ (4-6 giờ/tuần) +**Độ khó:** ⭐⭐⭐ Nâng cao + +#### Nội dung chính: + +- **Thống kê và phân tích dữ liệu:** + - Các biện pháp thống kê cơ bản + - Phân tích đặc điểm dữ liệu, xu hướng, độ lệch + - Phát hiện sự không nhất quán và các ngoại lệ tiềm ẩn + +- **Xử lý dữ liệu lớn:** + - Kỹ thuật làm việc với Big Data + - Phân vùng và phân tích song song + - Trực quan hóa dữ liệu ở định dạng 2D và 3D + +#### Công cụ sử dụng: + +- Jupyter Notebooks (IBM Watson Studio - bản miễn phí) +- Apache Spark (IBM Watson Studio - bản miễn phí) +- Python + +[👉 Xem chi tiết khoá 1](https://www.coursera.org/learn/ds) + +### Khoá 2: Advanced Machine Learning and Signal Processing + +**Thời gian:** 27 giờ (4-6 giờ/tuần) +**Độ khó:** ⭐⭐⭐ Nâng cao + +#### Nội dung chính: + +- **Machine Learning:** + - Mô hình Học máy có Giám sát và Không Giám sát + - Đại số tuyến tính cơ bản để hiểu cách hoạt động của các thuật toán + - Tinh chỉnh tham số và siêu tham số để tối ưu hóa mô hình + +- **Frameworks:** + - Scikit-Learn cho Python + - SparkML cho xử lý dữ liệu lớn + +#### Công cụ sử dụng: + +- Tất cả công cụ từ Khoá 1 +- Scikit-Learn +- SparkML + +[👉 Xem chi tiết khoá 2](https://www.coursera.org/learn/advanced-machine-learning-signal-processing) + +### Khoá 3: Applied AI with DeepLearning + +**Thời gian:** 24 giờ (4-6 giờ/tuần) +**Độ khó:** ⭐⭐⭐ Nâng cao + +#### Nội dung chính: + +- **Deep Learning:** + - Các mô hình Học sâu cho Xử lý ngôn ngữ tự nhiên (NLP) + - Thị giác máy tính (Computer Vision) + - Phân tích chuỗi thời gian (Time Series) + - Đại số tuyến tính và Mạng nơron + +- **Frameworks Deep Learning:** + - Keras, TensorFlow, PyTorch + - DeepLearning4J, Apache SystemML + +- **Triển khai ở quy mô lớn:** + - Kubernetes + - Apache Spark + - GPU computing + +#### Công cụ sử dụng: + +- Tất cả công cụ từ Khoá 1 và 2 +- Keras, TensorFlow, PyTorch +- DeepLearning4J, Apache SystemML + +[👉 Xem chi tiết khoá 3](https://www.coursera.org/learn/ai) + +### Khoá 4: Advanced Data Science Capstone + +**Thời gian:** 9 giờ +**Độ khó:** ⭐⭐⭐ Nâng cao + +#### Nội dung chính: + +- **Capstone Project:** + - Tổng hợp và áp dụng tất cả kiến thức từ 3 khoá trước + - Xây dựng một dự án hoàn chỉnh từ đầu đến cuối + - Đánh giá và trình bày kết quả + +Đây là cơ hội để bạn thực hành và củng cố lại những gì đã học trong 3 khoá trước. + +[👉 Xem chi tiết khoá 4](https://www.coursera.org/learn/advanced-data-science-capstone) + +## 💡 Một số lưu ý khi học + +### Về độ khó + +Trên thang Cơ bản - Trung cấp - Nâng cao, đây là khoá học ở mức **Nâng cao**. Nếu bạn chưa có nền tảng vững, có thể sẽ gặp khó khăn. Nên cân nhắc học trước khoá [IBM Data Science Professional Certificate](https://www.coursera.org/professional-certificates/ibm-data-science) nếu cần. + +### Về thời gian + +Thời gian học có thể khác nhau tùy vào: +- Tốc độ học của mỗi người +- Kinh nghiệm trước đó với các công cụ +- Mức độ chi tiết bạn muốn đi sâu -Về mặt thực hành và ứng dụng, khoá học này sẽ giúp các bạn nắm được tổng quan chu trình tổng hợp dữ liệu, học máy và học sâu từ đầu đến cuối thông qua các công nghệ nổi bật và được sử dụng rộng rãi nhất như Apache Spark, scikit-learning, SparkML, SystemML, TensorFlow, Keras, PyTorch, DeepLearning4J, Apache CouchDB và MQTT. +Coursera cho phép bạn học theo tốc độ của riêng mình, nên bạn có thể điều chỉnh lộ trình phù hợp. -## 2. Nội dung khoá học +### Tips học hiệu quả -| Truy cập vào khoá học | Tham khảo bài giải | -| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| [Advanced DS with IBM Specialization](https://www.coursera.org/specializations/advanced-data-science-ibm) | [ZootoPi Solution](https://github.com/ZootoPi/advanced_data_science_ibm) | +- **Ghi chép:** Tạo notebook riêng để ghi lại các khái niệm quan trọng +- **Thực hành:** Không chỉ xem video, hãy code lại từ đầu để hiểu sâu hơn +- **Tham khảo bài giải:** Nếu gặp khó khăn, có thể tham khảo [bài giải của ZootoPi](https://github.com/ZootoPi/advanced_data_science_ibm) +- **Đọc thêm:** Tham khảo documentation chính thức của các framework được dạy +- **Quản lý thời gian:** Dành 4-6 giờ/tuần một cách đều đặn sẽ hiệu quả hơn là học dồn -Khoá học bao gồm 4 khoá nhỏ: +## ❓ Câu hỏi thường gặp -- Khoá 1: [Fundamentals of Scalable Data Science](https://www.coursera.org/learn/ds) -- Khoá 2: [Advanced Machine Learning and Signal Processing](https://www.coursera.org/learn/advanced-machine-learning-signal-processing) -- Khoá 3: [Applied AI with DeepLearning](https://www.coursera.org/learn/ai) -- Khoá 4: [Advanced Data Science Capstone](https://www.coursera.org/learn/advanced-data-science-capstone) +
+Tôi có cần kiến thức nền tảng không? -## 3. Đánh giá về khoá học +Có, bạn nên có kiến thức cơ bản về Python, SQL và Data Science. Nếu chưa có, nên học trước khoá [IBM Data Science Professional Certificate](https://www.coursera.org/professional-certificates/ibm-data-science) để có nền tảng vững chắc. +
-Trên thang Cơ bản - Trung cấp - Nâng cao, ZootoPi đánh giá đây là 1 khoá học Nâng Cao. Những bạn chưa có nền tảng về lập trình cơ bản hay khoa học dữ liệu nên học trước khoá [IBM Data Science Professional Certificate](https://www.coursera.org/professional-certificates/ibm-data-science) để dễ dàng tiếp thu kiến thức trong khoá học này. +
+Khoá học có miễn phí không? -Dưới đây là 1 số tổng hợp về thông tin các khoá con giúp dễ dàng nắm được lộ trình học và tiện sắp xếp thời gian. +Bạn có thể audit khoá học miễn phí để xem nội dung, nhưng để nhận chứng chỉ và làm assignments, bạn cần trả phí. Coursera có chương trình financial aid cho những ai gặp khó khăn về tài chính. +
-### 3.1 Khoá 1 +
+Tôi có thể học theo tốc độ của riêng mình không? -- Kiến thức: - - Giới thiệu các biện pháp thống kê cơ bản, các đặc điểm dữ liệu, cũng như xu hướng, độ lệch hoặc sự không nhất quán và các ngoại lệ tiềm ẩn trong dữ liệu. - - Xác định kỹ thuật nào hữu ích để làm việc với dữ liệu lớn. - - Làm quen với các công cụ và thư viện trực quan hoá để nâng cao hiệu quả phân tích dữ liệu lớn với phân vùng và phân tích song song và trực quan hóa dữ liệu ở một số định dạng 2D và 3D. -- Công cụ: - - Jupyter notebooks (IBM Watson Studio bản miễn phí). - - ApacheSpark (IBM Watson Studio bản miễn phí). - - Python. -- Thời gian: 20 tiếng (4-6 tiếng/tuần). +Có, Coursera cho phép bạn học theo tốc độ của riêng mình. Bạn có thể hoàn thành nhanh hơn hoặc chậm hơn so với thời gian ước tính. +
-### 3.2 Khoá 2 +
+Khoá học có phù hợp cho người mới bắt đầu không? -- Kiến thức: - - Tiếp cận kiến thức về Mô hình Học máy có Giám sát và Không Giám sát. - - Tìm hiểu về các nguyên tắc cơ bản của Đại số tuyến tính để hiểu cách hoạt động của các chế độ học máy. - - Làm quen với các Frameworks phổ biến nhất cho python Scikit-Learn và SparkML. - - Học cách tinh chỉnh tham số/ siêu tham số để tối ưu hoá mô hình. -- Công cụ: - - Các công cụ trong Khoá 1. - - Frameworks: Scikit-Learn, SparkML. -- Thời gian: 27 tiếng (4-6 tiếng/tuần). +Không, đây là khoá học nâng cao. Nếu bạn mới bắt đầu, nên học trước khoá [IBM Data Science Professional Certificate](https://www.coursera.org/professional-certificates/ibm-data-science) hoặc các khoá học cơ bản khác. +
-### 3.3 Khoá 3 +## 📚 Tài liệu tham khảo thêm -- Kiến thức: +Nếu muốn tìm hiểu sâu hơn về các công cụ được dạy trong khoá học: - - Tiếp cận kiến thức về các mô hình Học sâu được sử dụng trong Xử lý ngôn ngữ tự nhiên, Thị giác máy tính, Phân tích chuỗi thời gian và nhiều ngành khác. - - Tìm hiểu về các nguyên tắc cơ bản của Đại số tuyến tính và Mạng nơron. - - Giới thiệu các Framework DeepLearning phổ biến nhất như Keras, TensorFlow, PyTorch, DeepLearning4J và Apache SystemML. - - Học cách mở rộng quy mô của những bộ não nhân tạo đó bằng Kubernetes, Apache Spark và GPU. +- [Apache Spark Documentation](https://spark.apache.org/docs/latest/) +- [TensorFlow Guide](https://www.tensorflow.org/guide) +- [PyTorch Tutorials](https://pytorch.org/tutorials/) +- [Scikit-learn User Guide](https://scikit-learn.org/stable/user_guide.html) +- [Keras Documentation](https://keras.io/) -- Công cụ: - - Các công cụ trong Khoá 1 và 2. - - Frameworks:Keras, TensorFlow, PyTorch, DeepLearning4J và Apache SystemML. -- Thời gian: 24 tiếng (4-6 tiếng/tuần). +## 🎬 Bắt đầu học -### 3.4 Khoá 4 +Nếu bạn đã sẵn sàng, có thể bắt đầu tại: -- Kiến thức: - - Thực hành capstone project để đánh giá kiến thức đã học được trong 3 khoá trên. -- Thời gian: 9 tiếng +- **[Đăng ký khoá học trên Coursera](https://www.coursera.org/specializations/advanced-data-science-ibm)** -Tuỳ vào tốc độ và trải nghiệm học của mỗi người, cùng 1 khoá học thời gian học sẽ có những khác biệt. Chứng chỉ trên Coursera có 1 lợi thế đó là thời gian linh hoạt nên bạn hoàn toàn có thể tuỳ chỉnh lộ trình học phù hợp với bạn nhất. +:::tip 💡 Gợi ý +Khi học, nếu gặp khó khăn với các bài tập, bạn có thể tham khảo **[bài giải của ZootoPi](https://github.com/ZootoPi/advanced_data_science_ibm)**. Tuy nhiên, nên tự làm trước, chỉ tham khảo khi thực sự cần thiết để đảm bảo bạn thực sự hiểu và nắm vững kiến thức. +::: -Chúc các bạn học tập vui vẻ ^^! +Chúc các bạn học tập hiệu quả! diff --git a/docs/tutorial/data_science/01.intro.md b/docs/tutorial/data_science/01.intro.md index 12ffa79c..ebbc5b9f 100644 --- a/docs/tutorial/data_science/01.intro.md +++ b/docs/tutorial/data_science/01.intro.md @@ -4,6 +4,18 @@ sidebar_label: "Bài 1: Giới thiệu" # Bài 1: Khoa học dữ liệu là gì? +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được định nghĩa và lịch sử phát triển của Khoa học dữ liệu +- [ ] Nắm vững 4 phương pháp phân tích chính trong Khoa học dữ liệu +- [ ] Phân biệt được sự khác biệt giữa các phương pháp phân tích +- [ ] Áp dụng kiến thức vào các tình huống thực tế + +## 📖 TL;DR (Tóm tắt nhanh) + +**Khoa học dữ liệu** là lĩnh vực kết hợp thống kê, khoa học máy tính và kiến thức chuyên môn để phân tích dữ liệu lớn. Có 4 phương pháp chính: **Mô tả** (chuyện gì đã xảy ra), **Chẩn đoán** (tại sao xảy ra), **Dự đoán** (chuyện gì sẽ xảy ra), và **Đề xuất** (nên làm gì). + ## 1. Khoa học dữ liệu là gì? Khoa học dữ liệu là lĩnh vực nghiên cứu về dữ liệu kết hợp của nhiều chuyên ngành, bao gồm thống kê, khoa học máy tính, công nghệ thông tin, trí tuệ nhân tạo, và kiến thức chuyên môn trong các lĩnh vực cụ thể để phân tích khối lượng lớn dữ liệu. @@ -18,12 +30,26 @@ Vậy thì Khoa học dữ liệu đóng vai trò gì và giúp ích gì cho ch Khoa học dữ liệu giúp cho các nhà Khoa học dữ liệu đặt ra và trả lời những câu hỏi như sự kiện gì đã xảy ra, tại sao nó xảy ra, sự kiện gì sẽ xảy ra và có thể sử dụng kết quả thu được cho mục đích gì. Với mục tiêu sử dụng để nghiên cứu dữ liệu, Khoa học dữ liệu có thể được đúc kết thành 4 phương pháp chính như sau: -| Phương pháp | Mô tả | Đặc trưng | -| -------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | -| Phân tích mô tả (Descriptive) | Xem xét dữ liệu để thu thập thông tin chuyên sâu về những sự kiện đã hoặc đang xảy ra | Trực quan hoá dữ liệu | -| Phân tích chẩn đoán (Diagnostic) | Phân tích chuyên sâu hoặc chi tiết dữ liệu để nắm được nguyên nhân khiến một sự kiện xảy ra | Khám phá, khai thác, và đối chiếu dữ liệu | -| Phân tích dự đoán (Predictive) | Sử dụng dữ liệu lịch sử để đưa ra các dự báo chính xác về mẫu dữ liệu có thể xảy ra trong tương lai | Xây dựng mô hình dự đoán, học máy | -| Phân tích đề xuất (Prescriptive) | Dự đoán sự kiện gì sẽ xảy ra, phân tích tác động tiềm năng và đề xuất phương án hành động tối ưu | Phân tích đồ thị, mô phỏng, xử lý sự kiện phức tạp, và công cụ đề xuất từ học máy | +| Phương pháp | Câu hỏi trả lời | Mô tả | Đặc trưng | Khi nào sử dụng | +| -------------------------------- | --------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------- | +| Phân tích mô tả (Descriptive) | Chuyện gì đã xảy ra? | Xem xét dữ liệu để thu thập thông tin chuyên sâu về những sự kiện đã hoặc đang xảy ra | Trực quan hoá dữ liệu | Báo cáo, Dashboard, KPI tracking | +| Phân tích chẩn đoán (Diagnostic) | Tại sao xảy ra? | Phân tích chuyên sâu hoặc chi tiết dữ liệu để nắm được nguyên nhân khiến một sự kiện xảy ra | Khám phá, khai thác, và đối chiếu dữ liệu | Root cause analysis, Troubleshooting | +| Phân tích dự đoán (Predictive) | Chuyện gì sẽ xảy ra? | Sử dụng dữ liệu lịch sử để đưa ra các dự báo chính xác về mẫu dữ liệu có thể xảy ra trong tương lai | Xây dựng mô hình dự đoán, học máy | Forecasting, Risk assessment, Demand prediction | +| Phân tích đề xuất (Prescriptive) | Nên làm gì? | Dự đoán sự kiện gì sẽ xảy ra, phân tích tác động tiềm năng và đề xuất phương án hành động tối ưu | Phân tích đồ thị, mô phỏng, xử lý sự kiện phức tạp, và công cụ đề xuất từ học máy | Optimization, Recommendation systems, A/B testing | + +### Mối quan hệ giữa các phương pháp + +``` +Descriptive (Mô tả) + ↓ +Diagnostic (Chẩn đoán) - Tìm hiểu nguyên nhân + ↓ +Predictive (Dự đoán) - Dự đoán tương lai + ↓ +Prescriptive (Đề xuất) - Đưa ra hành động +``` + +**Lưu ý**: Các phương pháp này thường được sử dụng tuần tự, nhưng cũng có thể độc lập tùy vào mục tiêu của dự án. Để giúp các bạn dễ hình dung hơn về 4 phương pháp này, chúng ta sẽ cũng nhau đi qua một ví dụ thực tế cách vận hành dịch vụ đặt vé của một hãng hàng không. @@ -43,9 +69,38 @@ Từ những phân tích từ hai mức độ trên, đội ngũ phòng vé có Cuối cùng và là cấp độ cao nhất của Khoa học dữ liệu, đó là phân tích đề xuất. Áp dụng cho dịch vụ vé máy bay, phân tích đề xuất có thể xem xét các chiến dịch tiếp thị trước đây để tăng tối đa lợi thế của mức tăng đột biến sắp tới về số lượng vé được đặt. Nhà Khoa học dữ liệu có thể dự đoán kết quả đặt vé cho các mức chi tiêu tiếp thị khác nhau trên những kênh tiếp thị đa dạng. Những dự báo này sẽ giúp hãng bay và phòng vé tự tin hơn khi đưa ra các quyết định tiếp thị. -## 3. Tổng kết +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **Định nghĩa**: Khoa học dữ liệu là lĩnh vực kết hợp nhiều chuyên ngành để phân tích dữ liệu lớn +- **4 phương pháp chính**: + - **Descriptive**: Mô tả những gì đã xảy ra (báo cáo, dashboard) + - **Diagnostic**: Tìm hiểu tại sao xảy ra (root cause analysis) + - **Predictive**: Dự đoán những gì sẽ xảy ra (forecasting, ML models) + - **Prescriptive**: Đề xuất hành động tối ưu (optimization, recommendations) +- **Ứng dụng thực tế**: Từ phân tích đơn giản đến xây dựng hệ thống đề xuất phức tạp + +## 💡 Lưu ý quan trọng + +- Không phải mọi bài toán đều cần cả 4 phương pháp +- Bắt đầu từ Descriptive để hiểu dữ liệu trước khi chuyển sang các phương pháp phức tạp hơn +- Prescriptive là cấp độ cao nhất nhưng cũng đòi hỏi nhiều tài nguyên nhất + +## 🧪 Thực hành + +Hãy thử áp dụng 4 phương pháp này vào một tình huống thực tế: + +1. Chọn một vấn đề kinh doanh (ví dụ: giảm tỷ lệ khách hàng rời bỏ) +2. Xác định câu hỏi cho từng phương pháp: + - Descriptive: Tỷ lệ rời bỏ hiện tại là bao nhiêu? + - Diagnostic: Tại sao khách hàng rời bỏ? + - Predictive: Khách hàng nào có nguy cơ rời bỏ? + - Prescriptive: Nên làm gì để giữ chân khách hàng? + +## ➡️ Bước tiếp theo -Vâỵ là trong phần này, chúng ta đã bước đầu làm quen với khái niệm Khoa học dữ liệu cũng như vai trò của Khoa học dữ liệu thông qua 4 phương pháp chính. Trong phần tiếp theo, chúng ta sẽ tìm hiểu về các quy trình hoạt động của Khoa học dữ liệu khi áp dụng vào các bài toán doanh nghiệp. +Trong phần tiếp theo, chúng ta sẽ tìm hiểu về **quy trình OSEMN** - một framework thực tế để giải quyết các bài toán Khoa học dữ liệu từ đầu đến cuối. Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/data_science/02.ds_flow.md b/docs/tutorial/data_science/02.ds_flow.md index bc56ef6b..5cf92091 100644 --- a/docs/tutorial/data_science/02.ds_flow.md +++ b/docs/tutorial/data_science/02.ds_flow.md @@ -4,6 +4,14 @@ sidebar_label: "Bài 2: Quy trình Khoa học dữ liệu" # Bài 2: Quy trình Khoa học dữ liệu +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được quy trình OSEMN trong Khoa học dữ liệu +- [ ] Nắm vững từng bước trong quy trình và công cụ sử dụng +- [ ] Phân biệt được Khoa học dữ liệu với các lĩnh vực liên quan +- [ ] Áp dụng quy trình vào các dự án thực tế + ## 1. Giới thiệu Trong bài trước, chúng ta đã cùng nhau tìm hiểu Khoa học dữ liệu là gì cũng như bốn vai trò của Khoa học dữ liệu được áp dụng trong các bài toán doanh nghiệp với một ví dụ minh hoạ thực tế gần gũi. Vậy thì để có thể giải quyết vấn đề với Khoa học dữ liệu, chúng ta sẽ cần làm những gì? Hãy cùng Zootopi tìm hiểu quy trình tổng quan của Khoa học dữ liệu cũng như phân biệt những lĩnh vực dữ liệu dễ gây nhầm lẫn với nhau như Khoa học dữ liệu, phân tích dữ liệu, phân tích kinh doanh, kỹ thuật dữ liệu trong bài viết này nhé! @@ -22,26 +30,179 @@ Thứ tự các bước trong quy trình này được thể hiện trong hình ![Quy trình Khoa học dữ liệu](./imgs/ds_flow.png) -### 2.1. Thu thập dữ liệu +### 2.1. Thu thập dữ liệu (Obtain) Bước đầu tiên trong quy trình Khoa học dữ liệu là thu thập dữ liệu. Dữ liệu có thể có sẵn từ trước, vừa mới được thu thập hoặc có thể dễ dàng truy cập trên Internet. Và đôi khi dữ liệu chúng ta cần không chỉ đến từ một nguồn duy nhất mà là sự kết hợp, tổng hợp từ nhiều nguồn khác nhau, ví dụ từ cơ sở dữ liệu nội bộ hoặc bên ngoài, phần mềm CRM của công ty, nhật ký máy chủ web, mạng xã hội hoặc mua dữ liệu từ các nguồn bên thứ ba đáng tin cậy. Nhiệm vụ của các nhà khoa học dữ liệu sau khi xác định được vấn đề cần giải quyết là tìm kiếm, thu thập, và trích xuất những dữ liệu phù hợp. -### 2.2. Làm sạch dữ liệu +**Ví dụ thực tế:** +```python +# Thu thập dữ liệu từ API +import requests +import pandas as pd + +# Ví dụ: Thu thập dữ liệu từ API công khai +response = requests.get('https://api.example.com/data') +data = response.json() +df = pd.DataFrame(data) + +# Hoặc đọc từ file CSV +df = pd.read_csv('data.csv') + +# Hoặc từ database +import sqlite3 +conn = sqlite3.connect('database.db') +df = pd.read_sql_query('SELECT * FROM table', conn) +``` + +**Công cụ phổ biến**: Python (requests, pandas), SQL, APIs, Web scraping (BeautifulSoup, Scrapy), Database connectors + +**Common Challenges**: +- Dữ liệu phân tán ở nhiều nguồn khác nhau +- Vấn đề về quyền truy cập và bảo mật +- Dữ liệu có format không đồng nhất + +### 2.2. Làm sạch dữ liệu (Scrub) Những dữ liệu chúng ta thu thập được trong bước đầu tiên thường được gọi là dữ liệu thô. Dữ liệu thô thường không đáp ứng được các yêu cầu của các thuật toán được sử dụng trong phân tích và học máy, ví dụ như dữ liệu bị thiếu, dữ liệu bị lặp lại, dữ liệu không đúng định dạng, dữ liệu không đồng nhất, v.v. Do đó, bước tiếp theo trong quy trình ta cần thức hiện, đó là làm sạch dữ liệu. Đây là bước quan trọng nhất trong quy trình Khoa học dữ liệu giúp chúng ta chuẩn hoá dữ liệu theo một định dạng được định trước để chúng ta dễ dàng khám phá và tìm ra những hành vi ẩn thú vị có thể giúp ích cho doanh nghiệp. Chúng ta sẽ đi sâu vào các bước trong quá trình làm sạch dữ liệu và bắt tay thực hiện làm sạch dữ liệu với Python trong các phần sau. -### 2.3. Khám phá dữ liệu +**Ví dụ thực tế:** +```python +# Xử lý dữ liệu khuyết +df = df.dropna() # Xóa dòng có giá trị khuyết +df = df.fillna(df.mean()) # Điền giá trị trung bình + +# Xóa dữ liệu trùng lặp +df = df.drop_duplicates() + +# Chuẩn hóa định dạng +df['ngay'] = pd.to_datetime(df['ngay']) +df['gia'] = df['gia'].astype(float) + +# Xử lý outliers +Q1 = df['gia'].quantile(0.25) +Q3 = df['gia'].quantile(0.75) +IQR = Q3 - Q1 +df = df[(df['gia'] >= Q1 - 1.5*IQR) & (df['gia'] <= Q3 + 1.5*IQR)] +``` + +**Công cụ phổ biến**: Pandas, NumPy, OpenRefine, Data cleaning libraries + +**Common Challenges**: +- Dữ liệu khuyết với nhiều cơ chế khác nhau (MCAR, MAR, MNAR) +- Outliers và dữ liệu không hợp lệ +- Inconsistency trong format và encoding + +### 2.3. Khám phá dữ liệu (Explore) Bước tiếp theo trong quy trình Khoa học dữ liệu là khám phá dữ liệu. Đây là thao tác phân tích sơ bộ dữ liệu giúp chúng ta hiểu sâu sắc hơn về dữ liệu ta đã thu thập được, những mẫu dữ liệu nào thú vị có thể được nghiên cứu hoặc tận dụng, những thông tin nào có thể giúp ích cho doanh nghiệp, những thông tin nào có thể được sử dụng để xây dựng mô hình học máy, v.v. Từ đó, nhà Khoa học dữ liệu có thể lập kế hoạch kỹ hơn cho các chiến lược mô hình hóa dữ liệu bước tiếp theo. Các công cụ như Pandas, NumPy để phân tích dữ liệu, và Matplotlib, Seaborn, Plotly, v.v. để trực quan hóa và khám phá dữ liệu thường được sử dụng trong giai đoạn này. Tương tự như bước làm sạch dữ liệu, chúng ta sẽ đi sâu vào các phương pháp khám phá dữ liệu hiệu quả thông các công cụ phân tích và trực quan hóa dữ liệu sử dụng Python trong các phần sau. -### 2.4. Xây dựng mô hình +**Ví dụ thực tế:** +```python +# Thống kê mô tả +print(df.describe()) +print(df.info()) + +# Phân tích tương quan +correlation_matrix = df.corr() +print(correlation_matrix) + +# Trực quan hóa +import matplotlib.pyplot as plt +import seaborn as sns + +# Histogram +df['gia'].hist(bins=50) +plt.show() + +# Scatter plot +plt.scatter(df['dien_tich'], df['gia']) +plt.show() + +# Box plot +sns.boxplot(x='quan', y='gia', data=df) +plt.show() + +# Heatmap +sns.heatmap(correlation_matrix, annot=True) +plt.show() +``` + +**Công cụ phổ biến**: Pandas, NumPy, Matplotlib, Seaborn, Plotly, Jupyter Notebook + +**Common Challenges**: +- Dữ liệu quá lớn khó visualize +- Tìm patterns ẩn trong dữ liệu +- Hiểu được mối quan hệ giữa các biến + +### 2.4. Xây dựng mô hình (Model) Xây dựng mô hình là bước áp chót trong quy trình Khoa học dữ liệu. Đây là giai đoạn chúng ta sử dụng các thuật toán học máy và học sâu để xây dựng mô hình dự đoán, phân loại, phân cụm, v.v. từ dữ liệu. Các thuật toán này có thể được chia thành hai mảng chính dựa theo đặc điểm dữ liệu huấn luyện, đó là học có giám sát (dữ liệu có nhãn) và học phi giám sát (dữ liệu không có nhãn). Nếu học có giám sát thường được sử dụng để xây dựng mô hình dự đoán, phân loại, v.v. thì học phi giám sát lại thường được dùng để xây dựng mô hình phân cụm, giảm chiều dữ liệu, v.v. Các thuật toán từ các hai nhóm này dễ dàng được triển khai trong các thư viện như Scikit-learn, Tensorflow, Keras, Pytorch v.v. Trong các phần sau, chúng ta sẽ đi sâu vào các thuật toán học máy thông qua các ví dụ thực tế sử dụng Python kết hợp các thư viện trên. -### 2.5. Diễn giải kết quả +**Ví dụ thực tế:** +```python +from sklearn.model_selection import train_test_split +from sklearn.ensemble import RandomForestClassifier +from sklearn.metrics import accuracy_score, classification_report + +# Chia dữ liệu +X = df[['dien_tich', 'nam_xay']] +y = df['loai_nha'] +X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) + +# Xây dựng mô hình +model = RandomForestClassifier(n_estimators=100) +model.fit(X_train, y_train) + +# Đánh giá mô hình +y_pred = model.predict(X_test) +print(f"Accuracy: {accuracy_score(y_test, y_pred)}") +print(classification_report(y_test, y_pred)) +``` + +**Công cụ phổ biến**: Scikit-learn, TensorFlow, Keras, PyTorch, XGBoost, LightGBM + +**Common Challenges**: +- Overfitting/Underfitting +- Chọn thuật toán phù hợp +- Tuning hyperparameters +- Xử lý class imbalance + +### 2.5. Diễn giải kết quả (Interpret) Kết quả được thu được từ mô hình dữ liệu được diễn giải bởi các nhà khoa học dữ liệu (có thể hợp tác cùng các chuyên gia phân tích và doanh nghiệp) để đưa ra các khuyến nghị hành động. Các kết quả này thường được diễn giải bằng cách sử dụng các đồ thị và biểu đồ để thể hiện những xu hướng và dự đoán, từ đó nhà Khoa học dữ liệu có thể ra các khuyến nghị hành động hợp lý và giúp doanh nghiệp triển khai một cách hữu hiệu. +**Ví dụ thực tế:** +```python +# Feature importance +feature_importance = pd.DataFrame({ + 'feature': X.columns, + 'importance': model.feature_importances_ +}).sort_values('importance', ascending=False) + +print(feature_importance) + +# Visualization +import matplotlib.pyplot as plt +plt.barh(feature_importance['feature'], feature_importance['importance']) +plt.title('Feature Importance') +plt.show() + +# Confusion matrix +from sklearn.metrics import confusion_matrix +import seaborn as sns + +cm = confusion_matrix(y_test, y_pred) +sns.heatmap(cm, annot=True, fmt='d') +plt.show() +``` + +**Công cụ phổ biến**: Matplotlib, Seaborn, Plotly, SHAP, LIME, Dashboard tools (Tableau, Power BI) + +**Common Challenges**: +- Diễn giải kết quả cho người không chuyên +- Tạo actionable insights +- Trình bày kết quả một cách thuyết phục + ## 3. Khoa học dữ liệu khác gì những lĩnh vực dữ liệu khác Nhắc tới dữ liệu, không quá ngạc nhiên khi các bạn có thể bị choáng ngợp bởi rất nhiều thuật ngữ xoay quanh các tác vụ và phương pháp xoay quanh dữ liệu như Khoa học dữ liệu, phân tích dữ liệu, phân tích kinh doanh, kỹ thuật dữ liệu, v.v. Liệu rằng, các thuật ngữ này có cùng ám chỉ một lĩnh vực? Các bạn có thể phân biệt được các thuật ngữ này và hiểu được sự khác nhau giữa chúng? Hãy cùng tìm hiểu nhé! @@ -78,9 +239,41 @@ Các kỹ sư dữ liệu xây dựng và duy trì các hệ thống cho phép n Các nhà khoa học dữ liệu sử dụng dữ liệu mà kỹ sư dữ liệu đã xử lý để xây dựng và huấn luyện các mô hình dự đoán. Sau đó, họ có thể giao kết quả cho các nhà phân tích để đưa ra quyết định tiếp theo. -## 4. Tổng kết +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **Quy trình OSEMN**: 5 bước chính trong Khoa học dữ liệu + - **Obtain**: Thu thập dữ liệu từ nhiều nguồn + - **Scrub**: Làm sạch và chuẩn hóa dữ liệu + - **Explore**: Khám phá và hiểu dữ liệu + - **Model**: Xây dựng mô hình học máy + - **Interpret**: Diễn giải kết quả và đưa ra khuyến nghị + +- **Sự khác biệt** giữa Khoa học dữ liệu và các lĩnh vực liên quan: + - **Phân tích dữ liệu**: Tập trung vào phân tích, sử dụng công cụ có sẵn + - **Phân tích kinh doanh**: Tập trung vào trường hợp kinh doanh và xác thực giải pháp + - **Kỹ thuật dữ liệu**: Tập trung vào xây dựng hệ thống và pipeline dữ liệu + +## 💡 Lưu ý quan trọng + +- Quy trình OSEMN không phải là tuyến tính hoàn toàn - có thể quay lại các bước trước +- Bước Scrub thường chiếm 60-80% thời gian của dự án +- Communication skills rất quan trọng ở bước Interpret +- Documentation ở mỗi bước giúp dự án dễ maintain hơn + +## 🧪 Checklist: Bạn đã sẵn sàng? + +Trước khi bắt đầu một dự án Khoa học dữ liệu, hãy đảm bảo: +- [ ] Đã xác định rõ vấn đề kinh doanh cần giải quyết +- [ ] Có quyền truy cập vào dữ liệu cần thiết +- [ ] Hiểu được yêu cầu và kỳ vọng của stakeholders +- [ ] Có đủ tài nguyên (thời gian, công cụ, nhân lực) +- [ ] Đã lập kế hoạch cho từng bước OSEMN + +## ➡️ Bước tiếp theo -Vâỵ là trong phần này, chúng ta đã nắm được quy trình tổng quát từ đầu đến cuối với một bài toán Khoa học dữ liệu cũng như hiểu được sự khác biệt giữa mảng này so với các lĩnh vực dữ liệu khác như phân tích dữ liệu, phân tích kinh doanh, kỹ thuật dữ liệu. Trong phần tiếp theo, chúng ta sẽ cũng nhau tiếp cận tới những khái niệm liên quan tới thành phân quan trọng nhất trong lĩnh vực này, đó chính là dữ liệu. +Trong phần tiếp theo, chúng ta sẽ tìm hiểu về **dữ liệu khuyết** - một trong những thách thức phổ biến nhất trong bước Scrub và cách xử lý chúng một cách hiệu quả. Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/data_science/03.missing.md b/docs/tutorial/data_science/03.missing.md index e142ef7f..9616a150 100644 --- a/docs/tutorial/data_science/03.missing.md +++ b/docs/tutorial/data_science/03.missing.md @@ -1,8 +1,16 @@ --- -sidebar_label: "Khuyết dữ liệu" +sidebar_label: "Bài 3: Khuyết dữ liệu" --- -# Câu chuyện khuyết dữ liệu +# Bài 3: Câu chuyện khuyết dữ liệu + +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được các loại dữ liệu khuyết và nguyên nhân gây ra chúng +- [ ] Phân biệt được MCAR, MAR, và MNAR +- [ ] Áp dụng các phương pháp xử lý phù hợp cho từng loại dữ liệu khuyết +- [ ] Sử dụng Python để phát hiện và xử lý dữ liệu khuyết trong thực tế ## 1. Khuyết dữ liệu (Missing values) là gì? @@ -53,3 +61,296 @@ Trong thực tế, khi làm việc với 1 tập dữ liệu và đối mặt v Thông thường, ta khó có thể phân biệt tường minh giữa các loại khuyết dữ liệu này, đặc biệt là MAR so với MNAR. Tuy nhiên, ta có thể kiểm tra xem dữ liệu khuyết theo MCAR hay MAR thông qua thử nghiệm tạo các biến giả rồi thử nghiệm `t-test` và kiểm định `chi-squared test` giữa biến này và các biến khác trong tập dữ liệu để xem liệu sự thiếu hụt trên biến này có liên quan đến giá trị của các biến khác hay không. Ví dụ: nếu nữ giới thực sự ít có khả năng cho bạn biết cân nặng của họ hơn nam giới, thì `chi-squared test` sẽ cho bạn biết rằng tỷ lệ khuyết dữ liệu trên biến cân nặng ở phụ nữ cao hơn nam giới. Từ đó, ta có thể kết luận rằng trường cân nặng là MAR. + +## 3. Phát hiện dữ liệu khuyết với Python + +Trước khi xử lý dữ liệu khuyết, chúng ta cần phát hiện và hiểu được mức độ khuyết dữ liệu trong dataset. Dưới đây là các cách phổ biến: + +```python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns + +# Tạo dataset mẫu để minh họa +data = { + 'tuoi': [25, 30, np.nan, 35, 40, np.nan, 28], + 'luong': [5000, 6000, 7000, np.nan, 8000, 5500, np.nan], + 'gioi_tinh': ['Nam', 'Nữ', 'Nam', 'Nữ', np.nan, 'Nam', 'Nữ'], + 'kinh_nghiem': [2, 5, np.nan, 8, 10, 3, 4] +} +df = pd.DataFrame(data) + +# 1. Kiểm tra số lượng giá trị khuyết +print("Số lượng giá trị khuyết:") +print(df.isnull().sum()) + +# 2. Tỷ lệ phần trăm giá trị khuyết +print("\nTỷ lệ phần trăm giá trị khuyết:") +print(df.isnull().sum() / len(df) * 100) + +# 3. Tổng số giá trị khuyết +print(f"\nTổng số giá trị khuyết: {df.isnull().sum().sum()}") + +# 4. Trực quan hóa dữ liệu khuyết +plt.figure(figsize=(10, 6)) +sns.heatmap(df.isnull(), cbar=True, yticklabels=False, cmap='viridis') +plt.title('Bản đồ dữ liệu khuyết') +plt.show() + +# 5. Thống kê chi tiết về dữ liệu khuyết +print("\nThống kê chi tiết:") +print(df.info()) +``` + +## 4. Xử lý dữ liệu khuyết + +### 4.1. Xử lý dữ liệu khuyết về cấu trúc + +**Phương pháp:** +- Loại bỏ các bản ghi có dữ liệu khuyết về cấu trúc +- Hoặc điền giá trị 0 hoặc giá trị mặc định phù hợp + +```python +# Ví dụ: Loại bỏ các bản ghi có dữ liệu khuyết về cấu trúc +df_cleaned = df.dropna(subset=['tuoi']) # Loại bỏ nếu thiếu tuổi + +# Hoặc điền giá trị mặc định +df['tuoi'].fillna(0, inplace=True) # Điền 0 cho tuổi khuyết +``` + +### 4.2. Xử lý dữ liệu MCAR (Missing Completely At Random) + +**Phương pháp:** +- **Xóa dữ liệu (Deletion)**: Nếu tỷ lệ khuyết < 5%, có thể xóa an toàn +- **Điền giá trị (Imputation)**: Mean/Median/Mode cho dữ liệu số, Mode cho dữ liệu phân loại + +```python +# Phương pháp 1: Xóa dữ liệu (chỉ khi tỷ lệ khuyết rất thấp) +df_dropped = df.dropna() # Xóa tất cả dòng có giá trị khuyết +df_dropped_cols = df.dropna(axis=1) # Xóa cột có giá trị khuyết + +# Phương pháp 2: Điền giá trị trung bình (cho dữ liệu số) +df['luong'].fillna(df['luong'].mean(), inplace=True) + +# Phương pháp 3: Điền giá trị trung vị (ít bị ảnh hưởng bởi outliers) +df['luong'].fillna(df['luong'].median(), inplace=True) + +# Phương pháp 4: Điền giá trị mode (cho dữ liệu phân loại) +df['gioi_tinh'].fillna(df['gioi_tinh'].mode()[0], inplace=True) + +# Phương pháp 5: Điền giá trị forward fill hoặc backward fill (cho dữ liệu thời gian) +df['kinh_nghiem'].fillna(method='ffill', inplace=True) # Forward fill +df['kinh_nghiem'].fillna(method='bfill', inplace=True) # Backward fill +``` + +### 4.3. Xử lý dữ liệu MAR (Missing At Random) + +**Phương pháp:** +- **Imputation có điều kiện**: Điền giá trị dựa trên các biến liên quan +- **Mô hình dự đoán**: Sử dụng regression hoặc classification để dự đoán giá trị khuyết + +```python +# Ví dụ: Điền lương dựa trên kinh nghiệm +# Tạo nhóm dựa trên kinh nghiệm +df['nhom_kinh_nghiem'] = pd.cut(df['kinh_nghiem'], + bins=[0, 3, 6, 10, np.inf], + labels=['Mới', 'Trung bình', 'Cao', 'Rất cao']) + +# Điền lương trung bình theo nhóm kinh nghiệm +df['luong'] = df.groupby('nhom_kinh_nghiem')['luong'].transform( + lambda x: x.fillna(x.mean()) +) + +# Hoặc sử dụng mô hình dự đoán (ví dụ với Linear Regression) +from sklearn.linear_model import LinearRegression +from sklearn.impute import SimpleImputer + +# Tách dữ liệu có và không có giá trị khuyết +df_with_salary = df[df['luong'].notna()] +df_missing_salary = df[df['luong'].isna()] + +# Huấn luyện mô hình dự đoán lương dựa trên tuổi và kinh nghiệm +X_train = df_with_salary[['tuoi', 'kinh_nghiem']].fillna(df_with_salary[['tuoi', 'kinh_nghiem']].mean()) +y_train = df_with_salary['luong'] +X_test = df_missing_salary[['tuoi', 'kinh_nghiem']].fillna(df_missing_salary[['tuoi', 'kinh_nghiem']].mean()) + +model = LinearRegression() +model.fit(X_train, y_train) +predicted_salary = model.predict(X_test) + +df.loc[df['luong'].isna(), 'luong'] = predicted_salary +``` + +### 4.4. Xử lý dữ liệu MNAR (Missing Not At Random) + +**Phương pháp:** +- **Phân tích độ nhạy**: So sánh kết quả với và không có dữ liệu khuyết +- **Mô hình đặc biệt**: Sử dụng các mô hình có thể xử lý dữ liệu khuyết (ví dụ: XGBoost) +- **Thu thập thêm dữ liệu**: Nếu có thể, thu thập lại dữ liệu để hiểu rõ nguyên nhân + +```python +# Ví dụ: Sử dụng XGBoost có thể xử lý dữ liệu khuyết tự động +from xgboost import XGBRegressor + +# XGBoost có thể xử lý NaN tự động +model = XGBRegressor() +# model.fit(X, y) # Có thể train trực tiếp với NaN + +# Hoặc sử dụng Multiple Imputation +from sklearn.experimental import enable_iterative_imputer +from sklearn.impute import IterativeImputer + +imputer = IterativeImputer(random_state=42) +df_imputed = pd.DataFrame(imputer.fit_transform(df), columns=df.columns) +``` + +## 5. Decision Tree: Chọn phương pháp xử lý + +``` +Bắt đầu + | + ├─ Dữ liệu khuyết về cấu trúc? + | └─> Loại bỏ hoặc điền giá trị mặc định (0, "N/A") + | + ├─ Tỷ lệ khuyết < 5%? + | └─> Có thể xóa an toàn + | + ├─ Dữ liệu số? + | ├─> Mean/Median imputation + | └─> Hoặc sử dụng mô hình dự đoán + | + ├─ Dữ liệu phân loại? + | └─> Mode imputation + | + ├─ Dữ liệu thời gian? + | └─> Forward fill / Backward fill + | + └─ Dữ liệu phức tạp (MNAR)? + └─> Sử dụng mô hình đặc biệt hoặc Multiple Imputation +``` + +## 6. Best Practices và Common Mistakes + +### ✅ Best Practices + +1. **Luôn kiểm tra tỷ lệ khuyết trước khi quyết định phương pháp** + ```python + missing_percentage = df.isnull().sum() / len(df) * 100 + if missing_percentage > 50: + print("Cảnh báo: Tỷ lệ khuyết quá cao, cần xem xét lại") + ``` + +2. **So sánh kết quả trước và sau khi xử lý** + ```python + # Thống kê trước khi xử lý + print("Trước khi xử lý:") + print(df.describe()) + + # Xử lý + df['luong'].fillna(df['luong'].median(), inplace=True) + + # Thống kê sau khi xử lý + print("\nSau khi xử lý:") + print(df.describe()) + ``` + +3. **Lưu lại thông tin về dữ liệu khuyết để phân tích sau** + ```python + # Tạo cột flag để đánh dấu giá trị đã được điền + df['luong_was_missing'] = df['luong'].isnull() + df['luong'].fillna(df['luong'].median(), inplace=True) + ``` + +4. **Sử dụng cross-validation khi dùng mô hình dự đoán để điền giá trị** + +### ❌ Common Mistakes + +1. **Xóa dữ liệu quá nhiều**: Không nên xóa nếu tỷ lệ khuyết > 20% +2. **Điền giá trị không phù hợp**: Không nên điền mean cho dữ liệu phân loại +3. **Bỏ qua phân tích nguyên nhân**: Luôn cố gắng hiểu tại sao dữ liệu bị khuyết +4. **Không kiểm tra phân phối sau khi điền**: Cần đảm bảo phân phối không bị thay đổi quá nhiều + +## 7. Ví dụ thực tế: Xử lý dữ liệu khuyết trong dataset bất động sản + +```python +import pandas as pd +import numpy as np + +# Giả sử có dataset bất động sản +data = { + 'dien_tich': [50, 70, np.nan, 100, 120, np.nan, 80], + 'gia': [2e9, 3e9, 4e9, np.nan, 6e9, 2.5e9, np.nan], + 'quan': ['Q1', 'Q2', 'Q1', 'Q3', np.nan, 'Q2', 'Q1'], + 'nam_xay': [2010, 2015, np.nan, 2020, 2018, 2012, 2016] +} +df = pd.DataFrame(data) + +# Bước 1: Phân tích dữ liệu khuyết +print("Phân tích ban đầu:") +print(df.isnull().sum()) +print(f"\nTỷ lệ khuyết: {df.isnull().sum() / len(df) * 100}") + +# Bước 2: Xử lý từng cột +# Diện tích: Điền trung bình theo quận +df['dien_tich'] = df.groupby('quan')['dien_tich'].transform( + lambda x: x.fillna(x.mean()) +) + +# Giá: Sử dụng mô hình dự đoán dựa trên diện tích +from sklearn.linear_model import LinearRegression + +df_with_price = df[df['gia'].notna()] +df_missing_price = df[df['gia'].isna()] + +if len(df_with_price) > 0 and len(df_missing_price) > 0: + X_train = df_with_price[['dien_tich']].fillna(df_with_price['dien_tich'].mean()) + y_train = df_with_price['gia'] + X_test = df_missing_price[['dien_tich']].fillna(df_missing_price['dien_tich'].mean()) + + model = LinearRegression() + model.fit(X_train, y_train) + df.loc[df['gia'].isna(), 'gia'] = model.predict(X_test) + +# Quận: Điền mode +df['quan'].fillna(df['quan'].mode()[0], inplace=True) + +# Năm xây: Điền trung vị +df['nam_xay'].fillna(df['nam_xay'].median(), inplace=True) + +print("\nSau khi xử lý:") +print(df.isnull().sum()) +``` + +## ✅ Tóm tắt + +- **4 loại dữ liệu khuyết**: Cấu trúc, MCAR, MAR, MNAR +- **Phát hiện**: Sử dụng `isnull()`, `info()`, và visualization +- **Xử lý MCAR**: Xóa hoặc điền mean/median/mode +- **Xử lý MAR**: Imputation có điều kiện hoặc mô hình dự đoán +- **Xử lý MNAR**: Mô hình đặc biệt hoặc Multiple Imputation +- **Best Practice**: Luôn phân tích nguyên nhân và so sánh kết quả + +## 🧪 Thực hành + +Hãy thử áp dụng các phương pháp trên với dataset của riêng bạn: + +1. Tải một dataset có dữ liệu khuyết (ví dụ: từ Kaggle) +2. Phân tích và phân loại loại dữ liệu khuyết +3. Áp dụng phương pháp xử lý phù hợp +4. So sánh kết quả trước và sau khi xử lý + +## 📚 Đọc thêm + +- [Pandas Documentation - Working with missing data](https://pandas.pydata.org/docs/user_guide/missing_data.html) +- [Scikit-learn - Imputation](https://scikit-learn.org/stable/modules/impute.html) +- [Handling Missing Data in Python](https://realpython.com/python-data-cleaning-numpy-pandas/) + +## ➡️ Bước tiếp theo + +Sau khi nắm vững xử lý dữ liệu khuyết, bạn có thể tiếp tục với: +- Feature Engineering +- Data Transformation +- Exploratory Data Analysis (EDA) + +Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/finance_ml/01.intro.md b/docs/tutorial/finance_ml/01.intro.md index 35151bb8..211d7b6f 100644 --- a/docs/tutorial/finance_ml/01.intro.md +++ b/docs/tutorial/finance_ml/01.intro.md @@ -4,14 +4,36 @@ sidebar_label: Lời mở đầu # Lời mở đầu +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành chương này, bạn sẽ có thể: +- [ ] Hiểu được các loại dữ liệu cơ bản trong tài chính +- [ ] Áp dụng phương pháp KYD (Know Your Data) để thấu hiểu dữ liệu tài chính +- [ ] Đánh giá chất lượng dữ liệu theo 4 nguyên tắc cơ bản +- [ ] Xử lý và phân tích dữ liệu tài chính với Python +- [ ] Xây dựng mô hình học máy cho bài toán tài chính thực tế + +## 📖 Giới thiệu + Ở những chương trước, chúng ta đã làm quen với các công cụ phục vụ phân tích dữ liệu, như Pandas, Numpy, Matplotlib, Scikit-learn, và các thư viện khác. Đồng thời, chúng ta đã có cái nhìn tổng quan về những thuật toán cơ bản trong học máy như Logistic Regression, Decision Tree, Random Forest, SVM, Neural Network và thực hành các phương pháp này trên một số tập dữ liệu mẫu. Tuy nhiên, chúng ta chưa thực sự áp dụng các kiến thức này vào một mảng hay bài toán thực tế cần kết hợp với kiến thức chuyên môn. Chương này được xây dựng nhằm mục tiêu giúp cho các bạn có thể tiếp cận với một mảng thực tế, đó là phân tích dữ liệu trong ngành tài chính. Chúng ta sẽ thực hành các bài toán phân tích dữ liệu trong ngành tài chính, như phân tích tương quan, phân tích cổ phiếu hay phân tích chuỗi thời gian. Đồng thời, chúng ta sẽ áp dụng các kiến thức về học máy để xây dựng một mô hình dự đoán giá cổ phiếu. +## 📚 Cấu trúc chương + +Chương này được chia thành các phần chính: + +1. **Dữ liệu tài chính**: Tìm hiểu các loại dữ liệu trong tài chính và cách phân loại chúng +2. **Thấu hiểu dữ liệu (KYD)**: 9 câu hỏi cơ bản để hiểu rõ dữ liệu của bạn +3. **Đánh giá chất lượng dữ liệu**: 4 nguyên tắc cơ bản (Accuracy, Completeness, Consistency, Timeliness) +4. **Case study thực tế**: Xác suất vỡ nợ (Probability of Default) + +## 🔑 Kiến thức cần có + Để làm được điều này, chúng ta cần nắm được hai mảng kiến thức chính: -- Kiến thức chuyên môn về ngành tài chính -- Kiến thức về học máy +- **Kiến thức chuyên môn về ngành tài chính**: Hiểu các khái niệm cơ bản như cổ phiếu, trái phiếu, lợi nhuận, rủi ro, v.v. +- **Kiến thức về học máy**: Các thuật toán cơ bản và cách áp dụng chúng Tài chính là một mảng kiến thức rất rộng lớn mà ta không thể tìm hiểu hết toàn bộ chỉ trong trong vỏn vẹn một chương học. Do đó, chúng ta sẽ tập trung vào một số kiến thức cơ bản nhất, nhưng đủ để có thể thực hành được các bài toán phân tích dữ liệu trong ngành tài chính, bao gồm: @@ -19,6 +41,28 @@ Tài chính là một mảng kiến thức rất rộng lớn mà ta không th - Một số vấn đề khi làm việc với dữ liệu tài chính - Phương pháp đánh giá dữ liệu từ góc nhìn tài chính tới học máy -Bên cạnh đó, chúng ta sẽ cùng nhau tiếp cận các kỹ thuật cơ bản trong Python bao gồm nhập, lọc, trực quan hóa, tóm tắt, phân tích dữ liệu giao dịch và chuẩn bị dữ liệu để sử dụng trong các mô hình, từ đó có thể đưa ra những quyết định đúng đắn cho doanh nghiệp. +## 🛠️ Công cụ và kỹ thuật + +Bên cạnh đó, chúng ta sẽ cùng nhau tiếp cận các kỹ thuật cơ bản trong Python bao gồm: + +- **Thu thập dữ liệu**: Nhập dữ liệu từ các nguồn khác nhau (CSV, API, Database) +- **Xử lý dữ liệu**: Lọc, làm sạch, và chuẩn hóa dữ liệu +- **Phân tích dữ liệu**: Trực quan hóa, tóm tắt, và phân tích dữ liệu giao dịch +- **Xây dựng mô hình**: Chuẩn bị dữ liệu và xây dựng mô hình học máy + +Từ đó có thể đưa ra những quyết định đúng đắn cho doanh nghiệp. + +## 💡 Ví dụ ứng dụng thực tế + +Trong chương này, chúng ta sẽ làm việc với các bài toán thực tế như: + +- **Phân tích cổ phiếu**: Dự đoán giá cổ phiếu dựa trên dữ liệu lịch sử +- **Xác suất vỡ nợ**: Tính toán xác suất một công ty không thể trả nợ +- **Phân tích rủi ro**: Đánh giá rủi ro tín dụng của khách hàng +- **Phân tích chuỗi thời gian**: Dự đoán xu hướng tài chính + +## ➡️ Bước tiếp theo + +Hãy bắt đầu với [Bài 1: Dữ liệu tài chính](./02.data_type.md) để tìm hiểu về các loại dữ liệu trong ngành tài chính! Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/finance_ml/02.data_type.md b/docs/tutorial/finance_ml/02.data_type.md index edbdaec7..20a5cd49 100644 --- a/docs/tutorial/finance_ml/02.data_type.md +++ b/docs/tutorial/finance_ml/02.data_type.md @@ -4,6 +4,14 @@ sidebar_label: "Bài 1: Dữ liệu tài chính" # Bài 1: Dữ liệu tài chính +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Phân loại được các loại dữ liệu tài chính theo góc nhìn tài chính +- [ ] Hiểu được sự khác biệt giữa dữ liệu có cấu trúc, phi cấu trúc và bán cấu trúc +- [ ] Áp dụng Python để xử lý từng loại dữ liệu +- [ ] Chọn phương pháp xử lý phù hợp cho từng loại dữ liệu + ## 1. Các kiểu dữ liệu dưới góc nhìn tài chính Trong phần đầu tiên này, chúng ta sẽ cùng nhau tổng hợp các loại dữ liệu liên quan đến thị trường tài chính và chứng khoán. @@ -58,6 +66,26 @@ Dễ giúp các bạn dễ hình dung hơn về dữ liệu có cấu trúc tron Trong Python, dữ liệu có cấu trúc có thể dễ dàng được thu thập, phân tích, và xử lý với các thư viện quen thuộc như: Pandas, Numpy, Scipy, v.v. Các thư viện này cung cấp cho bạn các công cụ để xử lý dữ liệu có cấu trúc, như: đọc, ghi, lọc, sắp xếp, thống kê, v.v. +**Ví dụ code:** +```python +import pandas as pd +import numpy as np + +# Đọc dữ liệu giá cổ phiếu từ CSV +df = pd.read_csv('stock_prices.csv') +print(df.head()) + +# Tạo ma trận giá cổ phiếu (1000 ngày x 100 cổ phiếu) +dates = pd.date_range('2020-01-01', periods=1000, freq='D') +stocks = [f'STOCK_{i:03d}' for i in range(100)] +prices = np.random.randn(1000, 100).cumsum(axis=0) + 100 +df_prices = pd.DataFrame(prices, index=dates, columns=stocks) + +# Tính lợi nhuận (returns) +returns = df_prices.pct_change().dropna() +print(returns.describe()) +``` + ### 2.2. Dữ liệu phi cấu trúc Trong thực tế, hầu hết dữ liệu tài chính là dạng số, nhưng tất nhiên cũng có dữ liệu phi số, có thể kể đến như các bài đăng trên mạng xã hội, các trang web đánh giá, hình ảnh, âm thanh và tệp video. @@ -66,15 +94,90 @@ Ví dụ, nếu bạn đang quan tâm đến cổ phiếu một công ty cụ th Không như dữ liệu có cấu trúc, dữ liệu phi cấu trúc không dễ lưu trữ do định dạng độc đáo, không tuân theo bất kì định dạng, quy tắc cụ thể nào.Trong Python, dữ liệu phi cấu trúc có thể được xử lý dưới dạng từ điển và danh sách, có thể cho phép xử lý linh hoạt các kiểu dữ liệu và độ dài dữ liệu khác nhau. Tuy nhiên, việc xử lý dữ liệu phi cấu trúc vẫn còn khó khăn, đặc biệt là khi dữ liệu có nhiều chiều và có nhiều dạng dữ liệu khác nhau. +**Ví dụ code:** +```python +# Xử lý dữ liệu văn bản từ mạng xã hội +tweets = [ + "Stock $AAPL is going up! Great earnings report!", + "Worried about $TSLA volatility...", + "Bullish on $MSFT after new product launch" +] + +# Phân tích sentiment (cần thư viện như TextBlob hoặc VADER) +from textblob import TextBlob + +sentiments = [] +for tweet in tweets: + blob = TextBlob(tweet) + sentiments.append({ + 'text': tweet, + 'polarity': blob.sentiment.polarity, # -1 (tiêu cực) đến 1 (tích cực) + 'subjectivity': blob.sentiment.subjectivity + }) + +df_sentiment = pd.DataFrame(sentiments) +print(df_sentiment) +``` + ### 2.3. Dữ liệu bán cấu trúc Dữ liệu bán cấu trúc là sự kết hợp của dữ liệu có cấu trúc và phi cấu trúc. Ví dụ, bạn có thể có một bảng dữ liệu về các cổ phiếu, trong đó mỗi cột là một thuộc tính của cổ phiếu, và mỗi hàng là một cổ phiếu. Tuy nhiên, một số cột có thể là dữ liệu phi cấu trúc, ví dụ như các bài đánh giá về cổ phiếu. Các thuộc tính này có thể được lưu trữ dưới dạng văn bản, hình ảnh, âm thanh, tệp video, v.v. Trong Python, dữ liệu bán cấu trúc có thể được xử lý bằng sự kết hợp của các cấu trúc dữ liệu. Rất khó để đưa ra một quy tắc cụ thể, nhưng Python chắc chắn có nhiều cấu trúc dữ liệu khác nhau để xử lý điều này. -## 3. Tổng kết +**Ví dụ code:** +```python +# Dữ liệu bán cấu trúc: Bảng cổ phiếu với cột đánh giá (văn bản) +stocks_data = { + 'symbol': ['AAPL', 'MSFT', 'GOOGL'], + 'price': [150.25, 300.50, 2500.75], + 'rating': [ + 'Strong buy - Excellent fundamentals and growth potential', + 'Buy - Solid company with good prospects', + 'Hold - Wait for better entry point' + ] +} + +df_stocks = pd.DataFrame(stocks_data) + +# Xử lý cột văn bản để trích xuất thông tin +df_stocks['rating_type'] = df_stocks['rating'].str.split(' - ').str[0] +df_stocks['rating_desc'] = df_stocks['rating'].str.split(' - ').str[1] +print(df_stocks) +``` + +### So sánh các loại dữ liệu + +| Đặc điểm | Có cấu trúc | Phi cấu trúc | Bán cấu trúc | +|----------|------------|--------------|-------------| +| **Định dạng** | Bảng (CSV, Excel) | Văn bản, hình ảnh, video | JSON, XML | +| **Dễ xử lý** | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | +| **Công cụ** | Pandas, NumPy | NLP, Computer Vision | JSON parser, XML parser | +| **Ví dụ** | Giá cổ phiếu, giao dịch | Tweet, báo cáo PDF | Báo cáo tài chính JSON | + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **Dữ liệu tài chính** được phân loại theo: + - **Góc nhìn tài chính**: Vốn chủ sở hữu, Trái phiếu, Dữ liệu khác + - **Góc nhìn học máy**: Có cấu trúc, Phi cấu trúc, Bán cấu trúc + +- **Dữ liệu có cấu trúc**: Dễ xử lý nhất, dạng bảng (CSV, Excel) +- **Dữ liệu phi cấu trúc**: Khó xử lý, cần NLP/CV (văn bản, hình ảnh) +- **Dữ liệu bán cấu trúc**: Kết hợp cả hai (JSON, XML) + +## 💡 Lưu ý quan trọng + +- Luôn bắt đầu với dữ liệu có cấu trúc để hiểu rõ vấn đề +- Dữ liệu phi cấu trúc cần tiền xử lý trước khi phân tích +- Chọn công cụ phù hợp với từng loại dữ liệu + +## ➡️ Bước tiếp theo + +Nếu trong lĩnh vực tài chính thuần, ta đã quen thuộc với khái niệm "thấu hiểu khách hàng" (KYC, viết tắt của Know Your Customer), thì dưới góc nhìn kĩ thuật, khi xử lý dữ liệu tài chính, ta có một thuật ngữ tương tự, đó là "thấu hiểu dữ liệu" (KYD, viết tắt của Know Your Data). -Vậy là trong phần này, chúng ta đã cùng nhau tìm hiểu về các kiểu dữ liệu dưới góc nhìn tài chính thuần và học máy thuần. Nếu trong lĩnh vực tài chính thuần, ta đã quen thuộc với khái niệm "thấu hiểu khách hàng" (KYC, viết tắt của Know Your Customer), thì dưới góc nhìn kĩ thuật, khi xử lý dữ liệu tài chính, ta có một thuật ngữ tương tự, đó là "thấu hiểu dữ liệu" (KYD, viết tắt của Know Your Data). Vậy làm thế nào để chúng ta có thể thấu hiểu dữ liệu? Hẹn các bạn trong bài viết tiếp theo. +Hãy tiếp tục với [Bài 2: Thấu hiểu dữ liệu](./03.kyd.md) để tìm hiểu 9 câu hỏi cơ bản giúp bạn hiểu rõ dữ liệu của mình! Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/finance_ml/03.kyd.md b/docs/tutorial/finance_ml/03.kyd.md index 4a45600b..2c840de4 100644 --- a/docs/tutorial/finance_ml/03.kyd.md +++ b/docs/tutorial/finance_ml/03.kyd.md @@ -4,12 +4,26 @@ sidebar_label: "Bài 2: Thấu hiểu dữ liệu" # Bài 2: Thấu hiểu dữ liệu +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được khái niệm KYD (Know Your Data) và tầm quan trọng của nó +- [ ] Áp dụng 9 câu hỏi cơ bản để đánh giá dữ liệu +- [ ] Sử dụng công cụ Know Your Data của Google +- [ ] Thực hiện KYD với Python + +## 📖 Giới thiệu + Nếu trong lĩnh vực tài chính thuần, ta đã quen thuộc với khái niệm "thấu hiểu khách hàng" (KYC, viết tắt của Know Your Customer), thì dưới góc nhìn kĩ thuật, khi xử lý dữ liệu tài chính, ta có một thuật ngữ tương tự, đó là "thấu hiểu dữ liệu" (KYD, viết tắt của Know Your Data). Đây là một bước quan trọng trong quá trình xử lý dữ liệu, đặc biệt là khi ta xử lý dữ liệu phi và bán cấu trúc. Vậy làm thế nào để chúng ta có thể thấu hiểu dữ liệu? ## 1. Thấu hiểu dữ liệu Để thực hiện bước KYD này, ta cần phải hiểu được kiểu dữ liệu mà ta đang xử lý là gì, các đặc điểm cũng như những vấn đề cần phải được xử lý với kiểu dữ liệu đó. Dưới đây là 9 câu hỏi cơ bản về dữ liệu bạn thu thập và dự định sử dụng để có thể thấu hiểu dữ liệu của mình. +### Nhóm 1: Cấu trúc và Định dạng (Câu hỏi 1-3) +### Nhóm 2: Tính chất và Ràng buộc (Câu hỏi 4-6) +### Nhóm 3: Nguồn gốc và Chất lượng (Câu hỏi 7-9) + | # | Câu hỏi | Giải thích | | --- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | Dữ liệu có các trường thông tin định nghĩa trước không? | Khi dữ liệu có các cột được định nghĩa trước, cấu trúc dữ liệu dễ xác định và duy trì hơn. | @@ -32,9 +46,87 @@ Một trong số những công cụ thú vị các bạn có thể sử dụng Bên cạnh đó, chúng ta cũng có thể thực hiện các bước Thấu hiểu dữ liệu với Python. Đây cũng sẽ là nội dung mà chúng ta sẽ cùng nhau tiếp cận trong những phần tiếp theo. -## 3. Tổng kết +## 3. Checklist KYD với Python + +Dưới đây là một số code Python để thực hiện KYD: + +```python +import pandas as pd +import numpy as np + +def know_your_data(df): + """Thực hiện KYD cơ bản trên DataFrame""" + print("=" * 50) + print("KNOW YOUR DATA (KYD) REPORT") + print("=" * 50) + + # 1. Kiểm tra cấu trúc + print("\n1. CẤU TRÚC DỮ LIỆU:") + print(f" - Số dòng: {len(df)}") + print(f" - Số cột: {len(df.columns)}") + print(f" - Các cột: {list(df.columns)}") + + # 2. Kiểm tra dữ liệu khuyết + print("\n2. DỮ LIỆU KHUYẾT:") + missing = df.isnull().sum() + missing_pct = (missing / len(df)) * 100 + for col in df.columns: + if missing[col] > 0: + print(f" - {col}: {missing[col]} ({missing_pct[col]:.2f}%)") + + # 3. Kiểm tra kiểu dữ liệu + print("\n3. KIỂU DỮ LIỆU:") + print(df.dtypes) + + # 4. Thống kê mô tả + print("\n4. THỐNG KÊ MÔ TẢ:") + print(df.describe()) + + # 5. Kiểm tra ràng buộc + print("\n5. KIỂM TRA RÀNG BUỘC:") + for col in df.select_dtypes(include=[np.number]).columns: + if df[col].min() < 0: + print(f" - {col}: Có giá trị âm (min: {df[col].min()})") + if col in ['probability', 'rate'] and df[col].max() > 1: + print(f" - {col}: Có giá trị > 1 (max: {df[col].max()})") + + # 6. Kiểm tra duplicates + print(f"\n6. DỮ LIỆU TRÙNG LẶP: {df.duplicated().sum()} dòng") + + return { + 'shape': df.shape, + 'missing': missing.to_dict(), + 'dtypes': df.dtypes.to_dict() + } + +# Sử dụng +# kyd_report = know_your_data(your_dataframe) +``` + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **KYD (Know Your Data)**: Tương tự như KYC trong tài chính +- **9 câu hỏi cơ bản** được chia thành 3 nhóm: + - **Nhóm 1**: Cấu trúc và định dạng + - **Nhóm 2**: Tính chất và ràng buộc + - **Nhóm 3**: Nguồn gốc và chất lượng +- **Công cụ**: Know Your Data của Google và Python + +## 💡 Lưu ý quan trọng + +- Luôn thực hiện KYD trước khi bắt đầu phân tích +- 9 câu hỏi này giúp bạn hiểu rõ dữ liệu và tránh các lỗi phổ biến +- Sử dụng checklist trên để tự động hóa quá trình KYD + +## 🧪 Thực hành + +Hãy thử áp dụng 9 câu hỏi KYD vào một dataset tài chính của riêng bạn và sử dụng hàm `know_your_data()` ở trên để tạo báo cáo! + +## ➡️ Bước tiếp theo -Vậy là trong phần này, chúng ta đã cùng nhau tìm hiểu về Thấu hiểu dữ liệu thông qua 9 câu hỏi cơ bản giúp bạn tiếp cận với dữ liệu một cách dễ dàng hơn cũng như làm quen với một công cụ mới: Know Your Data của Google. Hẹn các bạn trong bài viết tiếp theo. +Sau khi đã thấu hiểu dữ liệu, bước tiếp theo là đánh giá chất lượng dữ liệu. Hãy tiếp tục với [Bài 3: Đánh giá chất lượng dữ liệu](./04.metrics.md)! Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/finance_ml/04.metrics.md b/docs/tutorial/finance_ml/04.metrics.md index 072ea78d..02f293f3 100644 --- a/docs/tutorial/finance_ml/04.metrics.md +++ b/docs/tutorial/finance_ml/04.metrics.md @@ -4,18 +4,28 @@ sidebar_label: "Bài 3: Đánh giá dữ liệu" # Bài 3: Đánh giá chất lượng dữ liệu +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được 4 nguyên tắc cơ bản đánh giá chất lượng dữ liệu +- [ ] Áp dụng các nguyên tắc vào bài toán tín dụng thực tế +- [ ] Sử dụng Python để kiểm tra chất lượng dữ liệu +- [ ] Tránh các lỗi phổ biến khi làm việc với dữ liệu tài chính + +## 📖 Giới thiệu + Trong học phần trước, chúng ta đã làm quen với một số câu hỏi và công cụ cơ bản giúp các bạn làm quen và thấu hiểu dữ liệu mình sẽ sử dụng trong lĩnh vực tài chính. Trong phần này, chúng ta sẽ tìm hiểu một bước quan trọng để đảm bảo rằng dữ liệu mình sử dụng chất lượng và phù hợp với mục đích sử dụng, đó là đánh giá chất lượng dữ liệu. ## 1. Chất lượng dữ liệu Dưới góc nhìn của một kỹ sư tài chính, dữ liệu là thông tin được cung cấp bởi thị trường (ví dụ, sàn giao dịch chứng khoán, thị trường không kê đơn, cơ quan tín dụng, ngân hàng trung ương, tổ chức kinh tế, v.v.). Dữ liệu cũng có thể đến từ các sàn giao dịch, các đại lý môi giới ta giao dịch, thậm chí từ các ngân hàng và các tổ chức kinh tế thông qua cổng API của họ. Để đảm bảo chất lượng dữ liệu bất kể dữ liệu đến từ nguồn nào, chúng ta cũng cần quan tâm đến các nguyên tắc được đúc kết dưới bảng sau với 2 cột đầu tiên tương ứng từ khoá bằng tiếng Anh và tiếng Việt giúp các bạn dễ dàng tìm tài liệu tham khảo, và cột thứ ba là mô tả của các nguyên tắc đó. -| Từ khoá | Nguyên tắc | Mô tả | -| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------ | -| **Accuracy** | **Tính chính xác** | Mức độ chính xác của thông tin mà dữ liệu cung cấp? | -| **Completeness** | **Tính đầy đủ** | Dữ liệu phải thu thập theo nhu cầu đã đầy đủ chưa? | -| **Consistency** | **Tính nhất quán** | Có sự mâu thuẫn, xung đột giữa cùng một đối tượng dữ liệu trong các tập dữ liệu khác nhau không? | -| **Timeliness** | **Tính kịp thời** | Dữ liệu có cung cấp thông tin là những sự kiện xảy ra gần đây hoặc theo thời gian thực không? | +| Từ khoá | Nguyên tắc | Mô tả | Cách kiểm tra với Python | +| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------ | ------------------------ | +| **Accuracy** | **Tính chính xác** | Mức độ chính xác của thông tin mà dữ liệu cung cấp? | So sánh với nguồn khác, kiểm tra outliers | +| **Completeness** | **Tính đầy đủ** | Dữ liệu phải thu thập theo nhu cầu đã đầy đủ chưa? | `df.isnull().sum()`, tỷ lệ khuyết | +| **Consistency** | **Tính nhất quán** | Có sự mâu thuẫn, xung đột giữa cùng một đối tượng dữ liệu trong các tập dữ liệu khác nhau không? | So sánh giữa các nguồn, kiểm tra duplicates | +| **Timeliness** | **Tính kịp thời** | Dữ liệu có cung cấp thông tin là những sự kiện xảy ra gần đây hoặc theo thời gian thực không? | Kiểm tra timestamp, độ trễ dữ liệu | Bên cạnh 4 nguyên tắc cơ bản này, các bạn có thể tham khảo thêm các nguyên tắc khác như Integrity (tính toàn vẹn), Relevance (tính liên quan), Validity (tính hợp lệ), v.v. phục vụ cho việc đánh giá chất lượng dữ liệu nói chung, không chỉ dành cho dữ liệu tài chính tại chương `Data Quality` trong quyển sách [DMBoK - Data Management Body of Knowledge](https://www.amazon.com/DAMA-DMBOK-Data-Management-Body-Knowledge/dp/1634622340). @@ -51,9 +61,142 @@ Tính kịp thời của dữ liệu ở đây nghĩa là chúng ta cần đảm Ví dụ, khi một khách hàng đăng ký 10 thẻ tín dụng khác nhau trong vòng một buổi sáng, chúng ta cần đảm bảo rằng bước kiểm tra tín dụng của mình phản ánh việc cho vay theo thời gian thực. Điều này nhằm đảm bảo giảm thiểu gian lận từ những người đi vay tín dụng với mục đích xấu. Bằng cách gian lận đi vay tất cả các khoản vay nhỏ trong thời gian ngắn, họ có thể để thu được tổng số tiền vượt xa mức có được nếu chỉ đăng ký một khoản vay to trong khoảng thời gian dài hơn. Đây cũng là một thách thức lớn đối với các công ty cung cấp phân tích dữ liệu không chỉ bởi nó đòi hỏi khả năng xử lý các yêu cầu theo thời gian thực mà còn cả khả năng chạy các phân tích trong thời gian ngắn để có thể đưa ra quyết định nhanh chóng. -## 3. Tổng kết - -Vậy là trong phần này, chúng ta đã tìm hiểu một số những nguyên tắc để có thể đánh giá chất lượng của dữ liệu. Hi vọng rằng, bài viết này sẽ giúp bạn hiểu được các nguyên tắc cơ bản để đánh giá chất lượng dữ liệu. Trong phần tiếp theo, chúng ta sẽ tiếp cận với dữ liệu thực tế và bước đầu phân tích dữ liệu trước khi đi vào ứng dụng các mô hình học máy giải quyết các bài toán thực tế với dữ liệu tài chính. +## 3. Kiểm tra chất lượng dữ liệu với Python + +Dưới đây là code Python để kiểm tra 4 nguyên tắc chất lượng dữ liệu: + +```python +import pandas as pd +import numpy as np +from datetime import datetime + +def check_data_quality(df, customer_id_col='customer_id'): + """Kiểm tra chất lượng dữ liệu theo 4 nguyên tắc""" + + print("=" * 60) + print("ĐÁNH GIÁ CHẤT LƯỢNG DỮ LIỆU") + print("=" * 60) + + # 1. COMPLETENESS - Tính đầy đủ + print("\n1. TÍNH ĐẦY ĐỦ (Completeness):") + missing = df.isnull().sum() + missing_pct = (missing / len(df)) * 100 + for col in df.columns: + if missing[col] > 0: + print(f" ⚠️ {col}: {missing[col]} giá trị khuyết ({missing_pct[col]:.2f}%)") + else: + print(f" ✅ {col}: Đầy đủ") + + # 2. ACCURACY - Tính chính xác + print("\n2. TÍNH CHÍNH XÁC (Accuracy):") + # Kiểm tra outliers + numeric_cols = df.select_dtypes(include=[np.number]).columns + for col in numeric_cols: + Q1 = df[col].quantile(0.25) + Q3 = df[col].quantile(0.75) + IQR = Q3 - Q1 + outliers = df[(df[col] < Q1 - 1.5*IQR) | (df[col] > Q3 + 1.5*IQR)] + if len(outliers) > 0: + print(f" ⚠️ {col}: {len(outliers)} giá trị bất thường (outliers)") + else: + print(f" ✅ {col}: Không có outliers") + + # 3. CONSISTENCY - Tính nhất quán + print("\n3. TÍNH NHẤT QUÁN (Consistency):") + # Kiểm tra duplicates + duplicates = df.duplicated().sum() + if duplicates > 0: + print(f" ⚠️ Có {duplicates} bản ghi trùng lặp") + else: + print(f" ✅ Không có bản ghi trùng lặp") + + # Kiểm tra định danh duy nhất + if customer_id_col in df.columns: + unique_customers = df[customer_id_col].nunique() + total_records = len(df) + if unique_customers < total_records: + print(f" ⚠️ Có {total_records - unique_customers} bản ghi trùng ID khách hàng") + else: + print(f" ✅ Mỗi khách hàng có ID duy nhất") + + # 4. TIMELINESS - Tính kịp thời + print("\n4. TÍNH KỊP THỜI (Timeliness):") + date_cols = df.select_dtypes(include=['datetime64']).columns + if len(date_cols) > 0: + for col in date_cols: + latest_date = df[col].max() + days_old = (datetime.now() - latest_date).days + print(f" 📅 {col}: Dữ liệu mới nhất từ {days_old} ngày trước") + if days_old > 30: + print(f" ⚠️ Dữ liệu có thể đã lỗi thời (>30 ngày)") + else: + print(" ℹ️ Không có cột ngày tháng để kiểm tra") + + return { + 'completeness': (missing == 0).all(), + 'accuracy': len(outliers) == 0 if len(numeric_cols) > 0 else True, + 'consistency': duplicates == 0, + 'timeliness': days_old <= 30 if len(date_cols) > 0 else None + } + +# Sử dụng +# quality_report = check_data_quality(your_dataframe) +``` + +## 4. Best Practices + +### ✅ Do's (Nên làm) + +1. **Luôn kiểm tra 4 nguyên tắc trước khi phân tích** + ```python + quality_report = check_data_quality(df) + ``` + +2. **So sánh dữ liệu từ nhiều nguồn** + ```python + # So sánh giá cổ phiếu từ 2 nguồn + source1 = pd.read_csv('source1.csv') + source2 = pd.read_csv('source2.csv') + comparison = source1.merge(source2, on='symbol', suffixes=('_s1', '_s2')) + differences = comparison[comparison['price_s1'] != comparison['price_s2']] + ``` + +3. **Thiết lập data quality checks tự động** + ```python + def validate_data(df): + assert df.isnull().sum().sum() == 0, "Có dữ liệu khuyết!" + assert df.duplicated().sum() == 0, "Có dữ liệu trùng lặp!" + return True + ``` + +### ❌ Don'ts (Không nên làm) + +1. **Không tin tưởng hoàn toàn vào dữ liệu từ nhà cung cấp** +2. **Không bỏ qua việc kiểm tra tính nhất quán** +3. **Không sử dụng dữ liệu quá cũ mà không cập nhật** + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **4 nguyên tắc đánh giá chất lượng dữ liệu**: + - **Accuracy**: Tính chính xác + - **Completeness**: Tính đầy đủ + - **Consistency**: Tính nhất quán + - **Timeliness**: Tính kịp thời + +- **Áp dụng vào bài toán tín dụng**: Ví dụ cụ thể cho từng nguyên tắc +- **Công cụ Python**: Code để tự động kiểm tra chất lượng dữ liệu + +## 💡 Lưu ý quan trọng + +- "Garbage in, garbage out" - Dữ liệu chất lượng kém sẽ dẫn đến kết quả sai +- Luôn kiểm tra dữ liệu từ nhiều nguồn để đảm bảo tính nhất quán +- Thiết lập quy trình kiểm tra chất lượng dữ liệu tự động + +## ➡️ Bước tiếp theo + +Sau khi đã đánh giá chất lượng dữ liệu, chúng ta sẽ tiếp cận với dữ liệu thực tế và bước đầu phân tích dữ liệu trước khi đi vào ứng dụng các mô hình học máy. Hãy tiếp tục với [Case Study: Xác suất vỡ nợ](./05.case_study.ipynb)! Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/finance_ml/05.case_study.md b/docs/tutorial/finance_ml/05.case_study.md new file mode 100644 index 00000000..cec4d429 --- /dev/null +++ b/docs/tutorial/finance_ml/05.case_study.md @@ -0,0 +1,639 @@ +# Bài 4: Xác suất vỡ nợ + +Trước khi tiếp cận với ứng dụng các thuật toán Học máy đối với dữ liệu tài chính, chúng ta sẽ cùng nhau tiếp cận bài toán xác suất vỡ nợ (tiếng anh là Probability of Default hay viết tắt là PD). Trong thị trường tài chính, xác suất vỡ nợ là xác suất mà một công ty phát hành trái phiếu không đáp ứng được các nghĩa vụ hợp đồng của mình theo đúng tiến độ. Bên cạnh trường hợp phổ biến nhất là doanh nghiệp không thanh toán dẫn đến phá sản, ta có thể xây bản cáo bạch trái phiếu xác định các trường hợp vỡ nợ khác, chẳng hạn như việc không đáp ứng một nghĩa vụ nào đó trong hợp đồng hoặc vi phạm giao ước tài chính. + +## 1. Giới thiệu + +Trong bài viết này, chúng ta sẽ tập trung vào xác suất vỡ nợ có thời hạn một năm của giá trái phiếu doanh nghiệp theo thị trường. Thời hạn một năm cho đến khi đáo hạn được chọn để tính toán xác suất vỡ nợ theo thị trường bởi lí do sau: +* Trong khoảng thời gian ngắn hơn một hoặc hai năm, các công ty phải chịu tác động của chu kỳ kinh doanh. +* Trong khoảng thời gian dài hơn, hiệu ứng của chu kỳ kinh doanh có xu hướng có tác động ít hơn và cấu trúc vốn của công ty trở nên quan trọng hơn, từ đó làm cho mức độ rủi ro dài hạn ít theo chu kỳ hơn và ổn định hơn. + +Chúng ta sẽ tận dụng bảng "Tỷ lệ chuyển đổi trung bình trong một năm cho các doanh nghiệp toàn cầu" của Standard & Poor với dữ liệu lịch sử từ năm 1981-2019 để đánh giá các xác suất quan sát được của một xếp hạng cụ thể chuyển đổi sang xếp hạng khác trong suốt một năm. + +## 2. Thu thập và chuyển đổi dữ liệu + +Để tính toán xác suất vỡ nợ theo thị trường, trước tiên chúng ta phải thu được giá trái phiếu hiện tại của công ty. Nếu bạn chưa biết làm thế nào để thu thập được thông tin này thì đừng lo, đã có [Selenium](https://pypi.org/project/selenium/) giúp bạn. Selenium là công cụ chúng ta có thể sử dụng để tự động hóa hoạt động trình duyệt do người dùng thực hiện, chẳng hạn như tải một trang web và điền vào biểu mẫu trên trang web đó. Nó yêu cầu một WebDriver cụ thể cho trình duyệt web của một người. + +Trong trường hợp bạn chưa cài đặt [Selenium](https://pypi.org/project/selenium/), bạn có thể truy cập các liên kết tương ứng của chúng và tải chúng xuống bằng cách sử dụng `pip` (tham khảo cách sử dụng `pip` tại đây). Ngoài ra, ta cũng sẽ cần một chromedriver, có thể tải xuống bằng Python với gói [WebDriver-manager](https://pypi.org/project/webdriver-manager/) trong PyPi. + +Chúng ta sẽ sử dụng tập các câu lệnh Selenium để mô phỏng các thao tác nhấn phím và nhấp chuột của người dùng trong trình duyệt như một phương tiện để trỏ đến dữ liệu trái phiếu Trade Reporting and Compliance Engine (TRACE) do Cơ quan quản lý ngành tài chính (FINRA) cung cấp, từ đó truy cập dữ liệu cần thiết để tính toán xác suất vỡ nợ của thị trường. + +Trước hết, chúng ta sẽ cài đặt những thư viện cần thiết sử dụng `pip` và import những thư viện sẽ dùng trong bài viết này. + + +```python +# Mô phỏng thao tác người dùng để đào dữ liệu +!pip install selenium +# Đơn giản hóa việc quản lý trình điều khiển nhị phân cho các trình duyệt khác nhau. +!pip install webdriver_manager +# Truy cập dữ liệu tài chính công khai từ Internet và nhập dữ liệu đó vào Python dưới dạng DataFrame. +!pip install pandas_datareader +# Tính toán đại số +!pip install sympy +``` + + +```python +import time +import numpy as np +import pandas as pd +import pandas_datareader as dr +from datetime import date +from datetime import datetime as dt +from datetime import timedelta +from sympy import solve, symbols +import matplotlib.pyplot as plt + +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.chrome.service import Service +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import Select, WebDriverWait +from webdriver_manager.chrome import ChromeDriverManager +``` + +Chúng ta sẽ tải xuống thông tin về trái phiếu của công ty từ cơ sở dữ liệu TRACE, được duy trì bởi FINRA bằng đoạn mã dưới đây. Một số công ty bạn có thể thử, ví dụ: + +| Mã công ty | Tên công ty | +|:----------:|:-----------:| +| HES | Hess | +| F | Ford Motor | +| KHC | Kraft Heinz Co | +| DVN | Devon Energy | +| ... | ... | + +Một số tần suất coupon có thể sử dụng: ALL, Annual, Anytime, Bi-Monthly, Monthly, N/A, None, Pays At Maturity, Quarterly, Semi-Annual, Variable, ... + + +```python +# Bắt buộc +company_ticker = "HES" + +# Tuỳ chọn +company_name = "Hess" + +# Tuỳ chọn đầu vào: +coupon_frequency = "Semi-Annual" +``` + + +```python +# Đoạn mã Selenium để thu thập dữ liệu +options = Options() +options.add_argument("--headless") +options.add_argument("--no-sandbox") +options.add_argument("--disable-dev-shm-usage") +driver = webdriver.Chrome( + service=Service(ChromeDriverManager().install()), options=options +) + +# Lưu thời gian bắt đầu thực thi vào biến begin +begin = time.time() + +# Truy cập vào FINRA's TRACE Bond Center +driver.get("http://finra-markets.morningstar.com/BondCenter/Results.jsp") + +# Chọn đồng ý +WebDriverWait(driver, 10).until( + EC.element_to_be_clickable((By.CSS_SELECTOR, ".button_blue.agree")) +).click() + +# Chọn chỉnh sửa tìm kiếm +WebDriverWait(driver, 10).until( + EC.element_to_be_clickable((By.CSS_SELECTOR, "a.qs-ui-btn.blue")) +).click() + +# Chọn Tên tổ chức phát hành +WebDriverWait(driver, 10).until( + EC.presence_of_element_located((By.CSS_SELECTOR, "input[id=firscreener-issuer]")) +) +inputElement = driver.find_element(By.ID, "firscreener-issuer") +inputElement.send_keys(company_name) + +# Chọn biểu tượng +WebDriverWait(driver, 10).until( + EC.presence_of_element_located((By.CSS_SELECTOR, "input[id=firscreener-cusip]")) +) +inputElement = driver.find_element(By.ID,"firscreener-cusip") +inputElement.send_keys(company_ticker) + +# Chọn tìm kiếm nâng cao +WebDriverWait(driver, 10).until( + EC.element_to_be_clickable((By.CSS_SELECTOR, "a.ms-display-switcher.hide")) +).click() + +# Chọn Coupon Frequency +WebDriverWait(driver, 10).until( + EC.presence_of_element_located((By.CSS_SELECTOR, "select[name=interestFrequency]")) +) +Select( + (driver.find_elements(By.CSS_SELECTOR,"select[name=interestFrequency]"))[0] +).select_by_visible_text(coupon_frequency) + +# Chọn hiển thị kết quả +WebDriverWait(driver, 10).until( + EC.element_to_be_clickable((By.CSS_SELECTOR, "input.button_blue[type=submit]")) +).click() + +# Chờ kết quả +WebDriverWait(driver, 10).until( + EC.presence_of_element_located( + (By.CSS_SELECTOR, ".rtq-grid-row.rtq-grid-rzrow .rtq-grid-cell-ctn") + ) +) + +# Tạo DataFrame lưu trữ thông tin +frames = [] +for page in range(1, 11): + bonds = [] + WebDriverWait(driver, 10).until( + EC.presence_of_element_located( + (By.CSS_SELECTOR, (f"a.qs-pageutil-btn[value='{str(page)}']")) + ) + ) + time.sleep(2) + + headers = [ + title.text + for title in driver.find_elements(By.CSS_SELECTOR, + ".rtq-grid-row.rtq-grid-rzrow .rtq-grid-cell-ctn" + )[1:] + ] + + tablerows = driver.find_elements(By.CSS_SELECTOR, + "div.rtq-grid-bd > div.rtq-grid-row" + ) + for tablerow in tablerows: + tablerowdata = tablerow.find_elements(By.CSS_SELECTOR,"div.rtq-grid-cell") + bond = [item.text for item in tablerowdata[1:]] + bonds.append(bond) + + # Chuyển về DataFrame + df = pd.DataFrame(bonds, columns=headers) + + frames.append(df) + + try: + driver.find_element(By.CSS_SELECTOR,"a.qs-pageutil-next").click() + except: + break + +bond_prices_df = pd.concat(frames) + +# Lưu thời gian kết thúc +end = time.time() + +# Tính tổng thời gian chương trình chạy +print(f"Total runtime of the program is {end - begin} seconds") + +bond_prices_df +``` + +Chúng ta cũng đã tải xuống thông tin về giá và xếp hạng cho trái phiếu do một công ty phát hành cụ thể phát hành từ cơ sở dữ liệu trái phiếu được đánh giá cao, TRACE từ FINRA. Sau khi tải xuống, chúng ta tiếp tục sạch dữ liệu, chuyển đổi một số dữ liệu liên quan đến kỳ hạn trái phiếu và lọc dữ liệu để có thể tập trung phân tích vào các trái phiếu có kỳ hạn ngắn hơn thông qua hàm dưới đây. + + +```python +def bond_dataframe_filter(df): + # Loại bỏ những bản ghi về thông tin trái phiếu bị thiếu lợi suất và xếp hạng tín dụng + df["Yield"].replace("", np.nan, inplace=True) + df["Moody's®"].replace({"WR": np.nan, "": np.nan}, inplace=True) + df["S&P"].replace({"NR": np.nan, "": np.nan}, inplace=True) + for col in ["Yield", "Moody's®", "S&P"]: + df = df.dropna(subset=[col]) + + # Tạo cột Số năm đáo hạn Maturity Years phù hợp với + # Khoản thanh toán nửa năm một lần Semi-Annual Payments từ trái phiếu doanh nghiệp + now = dt.strptime(date.today().strftime("%m/%d/%Y"), "%m/%d/%Y") + df["Raw_Maturity"] = pd.to_datetime(df["Maturity"]).dt.strftime("%m/%d/%Y") + df["Maturity"] = [(dt.strptime(x, "%m/%d/%Y") - now).days for x in df["Raw_Maturity"]] + df["Maturity Years"] = [round((x/360)/0.5)*0.5 for x in df["Maturity"]] + + # Ép kiểu dữ liệu về dạng số thực + for col in ["Maturity", "Yield", "Coupon", "Price"]: + df[col] = df[col].astype(float) + + return df[(df["Maturity Years"] > 0) & (df["Maturity Years"] <= 5)] +``` + + +```python +bond_df_result = bond_dataframe_filter(bond_prices_df) +bond_df_result +``` + +## 3. Tính dòng tiền chiết khấu + +Chúng ta sẽ sử dụng các kỹ thuật định giá trái phiếu (Bond Prices) để tính xác suất vỡ nợ bằng cách sử dụng giá trái phiếu doanh nghiệp hiện tại. Việc định giá trái phiếu doanh nghiệp tương tự như bất kỳ tài sản rủi ro nào; nó phụ thuộc vào giá trị hiện tại của các dòng tiền kỳ vọng (Expected Cash Flow hay $ECF$) trong tương lai theo lãi suất suất chiết khấu $d$ (Discount Rate). + +$$ +BOND\ PRICE = \frac{ECF_1}{1+d} + \frac{ECF_2}{(1+d)^2} + \frac{ECF_3}{(1+d)^3} +$$ + +Việc định giá trái phiếu doanh nghiệp cũng cần tính đến khả năng trái phiếu không trả được nợ và không trả lại đầy đủ tiền gốc. +Khi đó, ta cần ước tính dòng tiền kỳ vọng và lãi suất chiết khấu được điều chỉnh theo rủi ro. + +Để định giá trái phiếu, trước hết ta tìm ECF ở mỗi thời kỳ bằng cách cộng tích của khoản thanh toán mặc định (Default Payout) +và xác suất vỡ nợ (P) với tích của khoản thanh toán đã hứa (thanh toán phiếu lãi và trả nợ gốc) và xác suất không vỡ nợ (1-P), hay còn gọi là như xác suất sống sót. + +$$ +ECF_1 = P \times (Default\ Payout) + (1-P) \times (Coupon\ Payment) +$$ + +$$ +ECF_2 = (1-P) \times \{P \times (Default\ Payout) + (1-P) \times (Coupon\ Payment)\} +$$ + +$$ +ECF_3 = (1-P)^2 \times \{P \times (Default\ Payout) + (1-P) \times (Coupon\ Payment + Principal)\} +$$ + +$$ +Default\ Payout = Principal \times Recovery\ Rate +$$ + +Nếu trái phiếu vỡ nợ, khoản thanh toán vỡ nợ là tích của tỷ lệ thu hồi nợ và vốn gốc. Trong trường hợp vốn gốc bằng mệnh giá trái phiếu (ví dụ: \$100), tỷ lệ thu hồi tương ứng tỷ lệ phần trăm của khoản lỗ được thu hồi từ một trái phiếu bị vỡ nợ. Tỷ lệ thu hồi khác nhau tùy theo ngành, mức độ thâm niên trong cấu trúc vốn, mức độ đòn bẩy trong cấu trúc vốn nói chung và liệu một chứng khoán cụ thể có được bảo đảm hay được thế chấp bằng cách khác hay không. Trong bài viết này, ta sẽ giả định tỷ lệ thu hồi đối với trái phiếu doanh nghiệp là 40%, một giả định phổ biến trong thực tế. + +Sau khi các dòng tiền kỳ vọng được tính toán, chúng được chiết khấu trở lại giai đoạn 0 với tỷ lệ chiết khấu được điều chỉnh theo rủi ro (d) để tính giá trái phiếu. Tỷ lệ chiết khấu được điều chỉnh theo rủi ro là tỷ lệ thu được bằng cách kết hợp phần bù rủi ro dự kiến với tỷ lệ phi rủi ro trong quá trình tính toán giá trị hiện tại của một khoản đầu tư rủi ro. + +$$ +Risk-adjusted\ Discount\ Rate = Risk-free\ Interest\ Rate + Expected\ Risk\ Premium +$$ + +với $Risk-adjusted\ Discount\ Rate$ là Tỷ lệ chiết khấu được điều chỉnh theo rủi ro, $Risk-free\ Interest\ Rate$ là Lãi suất phi rủi ro, và $Expected\ Risk\ Premium$ là Phần bù rủi ro dự kiến. + +Ta sử dụng tỷ lệ chiết khấu (đã điều chỉnh theo rủi ro) để tính đến các cân nhắc về tính thanh khoản, thời gian đáo hạn và thuế khiến trái phiếu doanh nghiệp có mức chênh lệch quan sát được so với lợi tức của trái phiếu phi rủi ro như trái phiếu do chính phủ phát hành trong một nền kinh tế ổn định. Lợi nhuận yêu cầu tối thiểu dự kiến đối với một nhà đầu tư trái phiếu bằng tổng của những yếu tố sau: + +| Yếu tố | Mô tả | +|:-------|:------| +| **Default Risk Premium** | Phần bù rủi ro mặc định bồi thường cho các nhà đầu tư có khả năng vỡ nợ doanh nghiệp. | +| **Liquidity Premium** | Phí thanh khoản bồi thường cho các nhà đầu tư khi đầu tư vào các chứng khoán kém thanh khoản hơn như trái phiếu. | +| **Maturity Premium** | Phí bảo hiểm đáo hạn bồi thường cho các nhà đầu tư về rủi ro liên quan đến trái phiếu đáo hạn trong nhiều năm tới trong tương lai vốn tiềm ẩn nhiều rủi ro hơn. | +| **Taxation Premium** | Phí bảo hiểm thuế bồi thường cho các nhà đầu tư về thu nhập chịu thuế mà trái phiếu tạo ra. | +| **Projected Inflation** | Lạm phát dự kiến tính đến sự mất giá của tiền tệ theo thời gian. | +| **Risk-free Rate** | Tỷ lệ phi rủi ro đề cập đến tỷ suất lợi nhuận mà nhà đầu tư có thể mong đợi đối với chứng khoán phi rủi ro. | + +Chúng ta bắt đầu tính toán tỷ lệ chiết khấu được điều chỉnh theo rủi ro thông qua ước tính phần bù rủi ro dự kiến (Expected Risk Premium), được tính bởi công thức sau: + +$$ +Expected\ Risk\ Premium = (Market\ Rate\ of\ Return - Risk-free\ Rate\ of\ Return) \times Beta +$$ + +trong đó $Market\ Rate\ of\ Return$ là tỷ suất lợi nhuận thị trường, $Risk-free\ Rate\ of\ Return$ là tỷ lệ hoàn vốn phi rủi ro, hệ số beta là trọng số được điều chỉnh dựa trên mức độ rủi ro đầu tư liên quan. Bằng cách lựa chọn cẩn thận hệ số beta của trái phiếu doanh nghiệp ngắn hạn đại diện cho thị trường tổng thể, chúng ta có thể tính toán phần bù rủi ro dự kiến sẽ dẫn đến tỷ lệ chiết khấu được điều chỉnh theo rủi ro kết hợp với các cân nhắc về tính thanh khoản, kỳ hạn và thuế để tạo ra xác suất vỡ nợ chính xác hơn khi sử dụng kỹ thuật định giá trái phiếu. + +Để tính phần bù rủi ro kỳ vọng, trước hết chúng ta phải tính tỷ suất sinh lợi thị trường. Chúng ta có thể sử dụng mô hình định giá tài sản vốn (CAPM) để xác định tỷ lệ hoàn vốn thị trường. + +$$ +r_m = r_f + (\beta \times MRP) +$$ + +với $r_m$ là tỷ lệ hoàn vốn thị trường, $r_f$ là lãi suất phi rủi ro, và $MRP$ tương ưng phần bù rủi ro thị trường. + +Chứng khoán chính phủ được giả định là không có rủi ro, ít nhất là từ quan điểm tín dụng. Với giả định này, tỷ lệ thích hợp để sử dụng trong tính toán tỷ lệ hoàn vốn thị trường là chứng khoán của chính phủ có thời hạn xấp xỉ bằng tài sản được định giá và đủ thanh khoản để lợi suất không có phí bảo hiểm rủi ro thanh khoản. Cổ phiếu được giả định là có thời hạn dài, do đó, lợi suất trái phiếu chính phủ dài hạn là đại diện thích hợp cho lãi suất phi rủi ro. + +Trong bước này, lợi suất trái phiếu kho bạc Hoa Kỳ kỳ hạn 10 năm sẽ được sử dụng làm lãi suất phi rủi ro và có thể tính từ dữ liệu Yahoo Finance thông qua đoạn mã dưới đây. + + +```python +# Ten-Year Risk-free Rate +timespan = 100 +current_date = date.today() +past_date = current_date - timedelta(days=timespan) +ten_year_risk_free_rate_df = dr.DataReader("^TNX", "yahoo", past_date, current_date) +ten_year_risk_free_rate = ten_year_risk_free_rate_df.iloc[len(ten_year_risk_free_rate_df) - 1, 5] / 100 +ten_year_risk_free_rate +``` + +Ta tận dụng [phần bù rủi ro thị trường](http://pages.stern.nyu.edu/~adamodar/New_Home_Page/datafile/ctryprem.html) hàng năm do Aswath Damodaran, giáo sư tại Trường Kinh doanh Stern thuộc Đại học New York cung cấp. Theo lý thuyết định giá tài sản, $beta$ đại diện cho loại rủi ro. Bản thân thị trường có hệ số beta là 1. Do đó, hệ số beta sẽ bằng 1 khi tính toán tỷ suất sinh lợi của thị trường. + + +```python +market_risk_premium = 0.0472 +stock_market_beta = 1 +``` + +Từ những dữ kiện trên, ta tính được tỷ lệ hoàn vốn thị trường. + + +```python +market_rate_of_return = ten_year_risk_free_rate + (stock_market_beta * market_risk_premium) +market_rate_of_return +``` + +Sau khi tính được Tỷ lệ hoàn vốn thị trường, ta tiếp tục xác định Phần bù rủi ro kỳ vọng bằng phần chênh lệch giữa Tỷ lệ hoàn vốn thị trường và lãi suất phi rủi ro nhân với hệ số beta của trái phiếu. Trong bước này, chúng ta sẽ sử dụng Lãi suất phi rủi ro một năm để Phần bù rủi ro dự kiến phù hợp với khoảng thời gian đối với tỷ lệ chiết khấu được điều chỉnh theo rủi ro. Ta thực hiện điều này bằng cách lấy Lợi tức của trái phiếu kho bạc Hoa Kỳ kỳ hạn 10 năm có tính thanh khoản rất cao và tăng nó lên lũy thừa 1/10 để chuyển đổi lợi tức thành tương đương một năm. + + +```python +# Lãi suất phi rủi ro một năm +one_year_risk_free_rate = (1 + ten_year_risk_free_rate) ** (1 / 10) - 1 +one_year_risk_free_rate +``` + +Cuối cùng, ta sẽ tính hệ số beta của trái phiếu doanh nghiệp. Hệ số beta của trái phiếu là độ nhạy của tỷ suất sinh lợi của trái phiếu đó đối với tỷ suất sinh lợi của chỉ số thị trường. Nó là thước đo rủi ro hệ thống, không thể đa dạng hóa. Ta có thể tính hệ số beta theo (ít nhất) một trong hai cách dưới đây. + + +```python +# Quỹ Chỉ số Trái phiếu Doanh nghiệp Ngắn hạn Vanguard Cổ phiếu ETF +bond_fund_ticker = "VCSH" +``` + +Do sự khác biệt về tính thanh khoản trong trái phiếu doanh nghiệp là rất lớn, chúng ta sử dụng Cổ phiếu ETF của Quỹ chỉ số trái phiếu doanh nghiệp ngắn hạn Vanguard (VCSH) làm đại diện cho trái phiếu đáo hạn ngắn hạn. Phiên bản beta từ chỉ số này sẽ cho phép ta nhúng một số rủi ro thanh khoản và kỳ hạn vào tỷ lệ chiết khấu được điều chỉnh theo rủi ro sẽ được sử dụng để tính xác suất vỡ nợ cho trái phiếu doanh nghiệp mà ta sẽ phân tích. Điều này sẽ cho phép cô lập tốt hơn rủi ro tín dụng liên quan đến trái phiếu được chọn. + +Tiếp theo, ta tính hệ số beta của quỹ trái phiếu (đối với S&P): + + +```python +# Tải dữ liệu cho quỹ trái phiếu và thị trường +market_data = dr.get_data_yahoo("SPY", past_date, current_date) # thị trường +fund_data = dr.get_data_yahoo("VCSH", past_date, current_date) # quỹ trái phiếu +``` + + +```python +# Cách #1 - Phương pháp hiệp phương sai/phương sai + +# Tính hiệp phương sai giữa quỹ và thị trường -- đây là tử số trong phép tính Beta +fund_market_cov = fund_data["Adj Close"].cov(market_data["Adj Close"]) +print("covariance between fund and market: ", fund_market_cov) + +# Tính phương sai thị trường (S&P) -- đây là mẫu số trong phép tính Beta +market_var = market_data["Adj Close"].var() +print("market variance: ", market_var) + +# Tính Beta +bond_fund_beta_cv = fund_market_cov / market_var +print("bond fund beta (using covariance/variance): ", bond_fund_beta_cv) +``` + + +```python +# Cách #2 - Phương pháp tương quan + +# Tính độ lệch chuẩn của thị trường bằng cách lấy căn bậc hai của phương sai, để sử dụng trong mẫu số +market_stdev = market_var**0.5 +print("market standard deviation: ", market_stdev) + +# Tính độ lệch chuẩn của quỹ trái phiếu, để sử dụng trong tử số + +fund_stdev = fund_data["Adj Close"].std() +print("fund standard deviation: ", fund_stdev) + +# Tính tương quan Pearson giữa quỹ trái phiếu và thị trường (S&P), để sử dụng trong tử số +fund_market_Pearson_corr = fund_data["Adj Close"].corr( + market_data["Adj Close"], method="pearson" +) +print("Pearson correlation between fund and market: ", fund_market_Pearson_corr) + +# Tính Beta +fund_beta_corr = fund_stdev * fund_market_Pearson_corr / market_stdev +print("bond fund beta (using correlation): ", fund_beta_corr) +``` + +Lưu ý rằng `.corr()` ở trên có thể được sử dụng để tính bất kỳ chỉ số nào trong số ba chỉ số tương quan mà chúng ta đã thảo luận sử dụng các tham số như `pearson`, `kendall` hoặc `spearman`. + + +```python +# Beta của trái phiếu có thể được tính bằng một trong 2 hàm trên (bond_fund_beta_cv hoặc fund_beta_corr) +bond_beta = fund_beta_corr +bond_beta +``` + +Bây giờ chúng ta có tất cả các thành phần cần thiết để tính Phần bù rủi ro dự kiến. + + +```python +# Phần bù rủi ro dự kiến +expected_risk_premium = (market_rate_of_return - one_year_risk_free_rate) * bond_beta +expected_risk_premium +``` + +Chúng ta sẽ sử dụng lãi suất phi rủi ro một năm để nó phù hợp với khoảng thời gian mà chúng ta muốn đối với Tỷ lệ chiết khấu được điều chỉnh theo rủi ro và sử dụng nó để chiết khấu các dòng tiền kỳ vọng nhằm xác định xác suất vỡ nợ. + + +```python +# Lãi suất phi rủi ro một năm +one_year_risk_free_rate = (1 + ten_year_risk_free_rate) ** (1 / 10) - 1 +one_year_risk_free_rate +``` + +We can now combine the risk-free interest rate and the expected risk premium to obtain the risk-adjusted discount rate. + + +```python +# Tỷ lệ chiết khấu được điều chỉnh theo rủi ro +risk_adjusted_discount_rate = one_year_risk_free_rate + expected_risk_premium +risk_adjusted_discount_rate +``` + +Ta đã xem xét mô hình CAPM và tìm thấy Tỷ lệ chiết khấu được điều chỉnh theo rủi ro, thông tin đầu vào cho ước tính xác suất vỡ nợ dựa trên thị trường. Để tìm được Lãi suất chiết khấu đã điều chỉnh theo rủi ro, chúng ta phải tìm Lãi suất phi rủi ro kỳ hạn một năm và Phần bù rủi ro kỳ vọng. Lãi suất phi rủi ro một năm bằng cách lấy căn bậc mười của Lãi suất phi rủi ro 10 năm trong khi Phần bù rủi ro kỳ vọng được tính bằng cách tìm Phần bù rủi ro thị trường và Hệ số beta, trong đó beta có thể được tính bằng cách sử dụng mối tương quan và độ lệch chuẩn, hoặc hiệp phương sai và phương sai thị trường, do mối quan hệ toán học giữa các biến này. Tiếp theo, chúng ta sẽ tính toán xác suất vỡ nợ do thị trường. + +## 4. Tính xác suất vỡ nợ với SymPy + +Bước cuối cùng trước khi chạy hàm xác minh tài khoản vỡ nợ của trái phiếu là xác định Tài khoản thanh toán gốc, Tỷ lệ thu hồi nợ, và xác suất vỡ nợ (kí hiệu P) và sử dụng hàm trong thư viện Python `SymPy` để tính. + + +```python +def bonds_probability_of_default(coupon, maturity_years, bond_price, principal_payment, risk_adjusted_discount_rate): + # Công thức dưới đây dành cho Dòng tiền nửa năm + price = bond_price + prob_default_exp = 0 + times = np.arange(0.5, (maturity_years - 0.5) + 1, 0.5) + semi_annual_coupon = coupon / 2 + + # Tính ECF + cashflows = np.array([]) + for i in times[:-1]: + cashflows = np.append(cashflows, semi_annual_coupon) + cashflows = np.append(cashflows, semi_annual_coupon + principal_payment) + + for i in range(len(times)): + # Đoạn mã này được sử dụng nếu chỉ còn một khoản thanh toán + if len(times) == 1: + prob_default_exp += (cashflows[i] * (1 - P) + cashflows[i] * recovery_rate * P) / np.power((1 + risk_adjusted_discount_rate), times[i]) + # Đoãn mã này được sử dụng nếu còn nhiều khoản thanh toán + else: + if times[i] == 0.5: + prob_default_exp += (cashflows[i] * (1 - P) + principal_payment * recovery_rate * P) / np.power((1 + risk_adjusted_discount_rate), times[i]) + else: + prob_default_exp += (np.power((1 - P), times[i - 1]) * (cashflows[i] * (1 - P) + principal_payment * recovery_rate * P)) / np.power((1 + risk_adjusted_discount_rate), times[i]) + + prob_default_exp = prob_default_exp - price + implied_prob_default = solve(prob_default_exp, P) + implied_prob_default = round(float(implied_prob_default[0]) * 100, 2) + + if implied_prob_default < 0: + return 0.0 + else: + return implied_prob_default +``` + + +```python +# Khởi tạo các biến đầu vào của hàm bonds_probability_of_default +principal_payment = 100 +recovery_rate = 0.40 +P = symbols("P") +``` + +Bây giờ chúng ta đã sẵn sàng để chạy hàm `bond_probability_of_default` để tính xác suất vỡ nợ theo thị trường cho các trái phiếu công ty đã chọn. + + +```python +bond_df_result.head(1) +``` + + +```python +# Việc tính toán này có thể mất một chút thời gian nếu có nhiều khoản thanh toán bằng coupon +bond_df_result["Probability of Default %"] = bond_df_result.head(1).apply( + lambda row: bonds_probability_of_default( + row["Coupon"], + row["Maturity Years"], + row["Price"], + principal_payment, + risk_adjusted_discount_rate, + ), + axis=1, +) + +bond_df_result.head(1) +``` + +## 5. Trực quan hoá Ma trận chuyển dịch + +Xếp hạng tín dụng được sử dụng cho trái phiếu do các tập đoàn và tổ chức chính phủ phát hành cũng như chứng khoán đảm bảo bằng tài sản (ABS) và chứng khoán đảm bảo bằng thế chấp (MBS). Ba cơ quan xếp hạng tín dụng lớn trên toàn cầu là Moody’s Investors Service, Standard & Poor’s, và Fitch Ratings. Mỗi bên đều cung cấp xếp hạng chất lượng tín dụng cho các tổ chức phát hành và đây là những xếp hạng theo thứ tự tập trung vào xác suất vỡ nợ. Những cơ quan này sẽ xem xét tổn thất dự kiến do vỡ nợ (LGD) bằng phương pháp ghi chú, đây là một sự điều chỉnh đối với xếp hạng của tổ chức phát hành để phản ánh mức độ ưu tiên của yêu cầu đối với các vấn đề nợ cụ thể của tổ chức phát hành đó. Xếp hạng này thường dành cho khoản nợ không có bảo đảm cấp cao. Xếp hạng nợ thứ cấp sau đó được điều chỉnh, hay còn gọi là "được ghi chú", bằng cách hạ thấp nó xuống một hoặc hai cấp—ví dụ: từ A+ xuống A hoặc xa hơn nữa xuống A–. Việc bao gồm tổn thất do vỡ nợ bên cạnh xác suất vỡ nợ giải thích tại sao chúng được gọi là "xếp hạng tín dụng" chứ không chỉ là "xếp hạng vỡ nợ". Các cơ quan xếp hạng báo cáo ma trận chuyển đổi dựa trên dữ liệu lịch sử của họ và sử dụng ma trận chuyển đổi để cho thấy khả năng xếp hạng thay đổi (hoặc giữ nguyên) trong thời gian một năm. + +Chúng ta có thể xác minh tính chính xác của xác suất vỡ nợ do thị trường bằng ma trận chuyển đổi của các cơ quan xếp hạng. Cụ thể, bài viết này sử dụng Tỷ lệ chuyển đổi trung bình trong một năm của Standard & Poor cho các tập đoàn toàn cầu sử dụng dữ liệu lịch sử từ năm 1981-2019 để xác minh xác suất vỡ nợ do thị trường đã tính toán trước đó thông qua đoạn mã bên dưới đây. + + +```python +def prob_default_term_structure(df): + + fig, (ax1, ax2) = plt.subplots(1, 2, clear=True) + fig.subplots_adjust(wspace=0.5) + Mgroups = df.groupby("Moody's®") + ax1.clear() + ax1.margins(0.5) + ax1.set_xlabel("Days Until Maturity") + ax1.set_ylabel("Probability of Default %") + ax1.set_title("Moody's® Ratings") + for name, group in Mgroups: + ax1.plot( + group["Maturity"], + group["Probability of Default %"], + marker="o", + linestyle="", + ms=12, + label=name, + ) + ax1.legend(numpoints=1, loc="upper left") + + SPgroups = df.groupby("S&P") + ax2.clear() + ax2.margins(0.5) + ax2.set_xlabel("Days Until Maturity") + ax2.set_ylabel("Probability of Default %") + ax2.set_title("S&P Ratings") + + for name, group in SPgroups: + ax2.plot( + group["Maturity"], + group["Probability of Default %"], + marker="o", + linestyle="", + ms=12, + label=name, + ) + ax2.legend(numpoints=1, loc="upper left") + plt +``` + + +```python +prob_default_term_structure(bond_df_result) +``` + + +```python +tgt_website = r"https://www.spglobal.com/ratings/en/research/articles/200429-default-transition-and-recovery-2019-annual-global-corporate-default-and-rating-transition-study-11444862" +``` + + +```python +def get_transition_matrix(tgt_website): + df_list = pd.read_html(tgt_website) + matrix_result_df = df_list[22] + return matrix_result_df + +transition_matrix_df = get_transition_matrix(tgt_website) +``` + + +```python +sp_clean_result_df = pd.DataFrame(transition_matrix_df.iloc[:34, :19].dropna(axis=0)) +sp_clean_result_df +``` + +Ma trận chuyển đổi năm 2019 của Standard & Poor ở trên cho thấy xác suất của một xếp hạng cụ thể chuyển sang xếp hạng khác trong suốt năm tiếp theo. Một tổ chức phát hành được xếp hạng A có 78.88% xác suất duy trì ở mức đó, 0.03% xác suất chuyển lên hạng AAA; xác suất tăng lên AA là 0.22%; xác suất chuyển xuống BBB là 0.86%; 0.10% xuống BB; 0.02% đối với B, 0.01% đối với CCC, CC hoặc C; và 0.05% cho D, ở mức mặc định. + +Selenium có thể được tận dụng để truy xuất xếp hạng tín dụng của Standard & Poor từ đó ta có thể xếp hạng tín dụng của trái phiếu doanh nghiệp và xác định xác suất chuyển đổi xếp hạng cụ thể sang D (mặc định) trong năm tới theo ma trận chuyển đổi của Standard & Poor's 2019. + + +```python +# Lấy xác suất vỡ nợ mỗi xếp hạng + +sp_rating_list = [ "AAA", "AA+", "AA", "AA-", "A+", "A", "A-", "BBB+", "BBB", "BBB-", "BB+", "BB", "BB-", "B+", "B", "B-"] + +ccc_list = ["CCC+", "CCC", "CCC-", "CC+", "CC", "CC-", "C+", "C", "C-"] + +sp_rating = None + +for i in sp_rating_list: + if bond_df_result["S&P"].iloc[0] == i: + sp_rating = bond_df_result["S&P"].iloc[0] + +if sp_rating is None: + for i in ccc_list: + if bond_df_result["S&P"].iloc[0] == i: + sp_rating = "CCC/C" + +sp_transition_dp = 0 + +for i in range(33): + if transition_matrix_df.loc[i][0] == sp_rating: + sp_transition_dp += float(sp_clean_result_df.loc[i][18]) + +sp_transition_dp +``` + +Có vẻ như xác suất vỡ nợ do thị trường ta tính toán cho trái phiếu doanh nghiệp có kỳ hạn gần nhất gần bằng với xác suất vỡ nợ được xác định từ dữ liệu lịch sử trong ma trận chuyển đổi năm 2019 của Standard & Poor. + + +```python +# So sánh kỳ hạn gần nhất Xác suất vỡ nợ theo thị trường với xác suất vỡ nợ lịch sử trong ma trận chuyển đổi năm 2019 của Standard & Poor +print("Market-implied probability of default = %s" % (bond_df_result["Probability of Default %"].iloc[0])) +print("Standard & Poor’s probability of default = %s" % (sp_transition_dp)) +``` + +Từ ví dụ trên, các kỹ thuật định giá trái phiếu sử dụng Tỷ lệ chiết khấu được điều chỉnh theo rủi ro thực hiện khá tốt việc xác định rủi ro vỡ nợ của công ty và có thể đủ để xếp hạng các công ty theo mức độ tín nhiệm. Tuy nhiên, cần lưu ý một số giả định ta đặt ra và thực tế ứng dụng tuỳ trong doanh nghiệp. + +| Yếu tố | Giả định | Thực tế | +|:-------|:---------|:--------| +| Đường cong lợi suất trái phiếu chính phủ | Bằng phẳng | Có thể dốc lên hoặc dốc xuống | +| Tỷ lệ khôi phục | 40% | Có thể thử nghiệm với 30% hoặc 60% | +| Quá trình hồi vốn | Tức thời | Thời gian trì hoãn kéo dài có thể xảy ra giữa sự kiện vỡ nợ và việc thu hồi tiền mặt cuối cùng | +| Xác suất vỡ nợ hàng năm | Như nhau | Không nhất thiết phải đúng như vậy | + +Hi vọng bài viết này sẽ giúp các bạn nắm được những kiến thức tài chính cơ bản cũng như cơ chế được sử dụng để dự đoán xác suất vỡ nợ trước khi có Học máy. Từ đó, chúng ta có thể so sánh cùng một bài toán nhưng khi tiếp cần với phương pháp Học máy thì có những ưu và nhược điểm gì ở các phần sau. + +Chúc các bạn học tập vui vẻ! + +**Tham khảo** + +* Donnelly, Hugh. "[Calculating a Company's Probability of Default with Python.](https://github.com/AlphaWaveData/Jupyter-Notebooks/blob/master/AlphaWave%20Market-Implied%20Probability%20of%20Default%20Example.ipynb.)", AlphaWave Data. +* Mã nguồn và tài liệu tham khảo một phần từ **Hugh Donnelly, CFA**, *AlphaWave Data*, **Tháng Ba 2021** theo Giấy phép MIT sau: + +> Copyright (c) 2020 HDVI. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**Lưu ý**: Thông báo giấy phép MIT ở trên được sao chép ở đây để tuân thủ các yêu cầu của nó, nhưng nó không áp dụng cho nội dung trong các ghi chú bài học này. +* WoldQuant University. “MscFe 600 Financial Data.” WoldQuant University, https://learn.wqu.edu/my-path/courses/financial-data/. Truy cập ngày 22/11/2022. diff --git a/docs/tutorial/python/01.anaconda.md b/docs/tutorial/python/01.anaconda.md index 520ede92..024b3edf 100644 --- a/docs/tutorial/python/01.anaconda.md +++ b/docs/tutorial/python/01.anaconda.md @@ -4,10 +4,20 @@ sidebar_label: "Bài 0: Anaconda" # Bài 0: Làm quen với Anaconda -## I. Giới thiệu +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được Anaconda là gì và tại sao nên sử dụng +- [ ] Phân biệt được Conda, Miniconda và Anaconda +- [ ] Tạo và quản lý môi trường ảo với Conda +- [ ] Cài đặt Anaconda trên hệ điều hành của bạn + +## 📖 Giới thiệu Anaconda là 1 nền tảng mã nguồn mở về Khoa học Dữ liệu sử dụng ngôn ngữ lập trình Python thông dụng nhất hiện nay. Với hơn 25 triệu người dùng (theo thống kê tại trang chủ), Anaconda là giải pháp nhanh nhất và dễ nhất để các bạn có thể tìm hiểu Khoa học Dữ liệu với Python hoặc R trên Windows, Linux và Mac OS X. Và ngày hôm nay, **ZootoPi** xin phép được khai bút chuỗi series **Khoa học Dữ liệu cho người mới bắt đầu** bằng những công cụ thiết yếu dành cho nhà Khoa học Dữ liệu, bắt đầu với Anaconda. +## I. Giới thiệu + Các bạn có thể tải Anaconda tương ứng với hệ điều hành các bạn sử dụng tại [đây](https://www.anaconda.com/products/individual) ![anaconda](img/anaconda.png) @@ -49,7 +59,7 @@ Sự khác biệt giữa Anaconda và Miniconda nằm ở `gói meta anaconda` . conda create -n zootopi python=3.8 ``` -Ở đây, **_-n_** là viết tắt của **_-name_**, **_zootopi_** là tên của môi trường. Các bạn có thể đổi tên môi trường tuỳ vào mục đích của môi trường, tạo cho dự án nào, lưu ý là tên môi trường phải viết liền không dấu. Để chọn phiên bản Python, ta sử dụng **_python=_**. Hiện nay, Python đã có hỗ trợ đến phiên bản 3.9. Tuy nhiên, để đảm bảo tính ổn định của phiên bản, mình khuyến khích các bạn dùng lùi xuống 1 đến 2 version (e.g Python 3.8 hoặc Python 3.7). +Ở đây, **_-n_** là viết tắt của **_-name_**, **_zootopi_** là tên của môi trường. Các bạn có thể đổi tên môi trường tuỳ vào mục đích của môi trường, tạo cho dự án nào, lưu ý là tên môi trường phải viết liền không dấu. Để chọn phiên bản Python, ta sử dụng **_python=_**. Hiện nay, Python đã có hỗ trợ đến phiên bản 3.12+. Tuy nhiên, để đảm bảo tính ổn định và tương thích với các thư viện phổ biến, mình khuyến khích các bạn sử dụng Python 3.10 hoặc Python 3.11 (Python 3.7 và 3.8 đã không còn được hỗ trợ chính thức). ```console (base) ➜ ~ conda create -n zootopi python=3.8 @@ -94,4 +104,46 @@ Type "help", "copyright", "credits" or "license" for more information. Vậy là chúng ta đã tạo ra 1 môi trường ảo để bắt đầu dự án rồi. Các bạn có thể tham khảo thêm 1 số câu lệnh và cú pháp phổ biến tại [đây](https://docs.conda.io/projects/conda/en/4.6.0/_downloads/52a95608c49671267e40c689e0bc00ca/conda-cheatsheet.pdf). -Trong bài viết tiếp theo, ZootoPi sẽ tiếp tục giới thiệu đến các bạn về `JupyterLab` và `Jupyter Notebook`? Vậy 2 khái niệm này là gì? Sử dụng `JupyterLab` và `Jupyter Notebook` như thế nào để có thể làm việc hiệu quả? ZootoPi xin phép hẹn bạn trong bài viết tiếp theo! +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **Anaconda**: Nền tảng mã nguồn mở phổ biến cho Khoa học Dữ liệu +- **Conda, Miniconda, Anaconda**: + - `Conda`: Công cụ quản lý môi trường và gói + - `Miniconda = Python + conda` + - `Anaconda = Python + conda + gói meta anaconda` (160+ gói Python) +- **Tạo môi trường ảo**: `conda create -n tên_môi_trường python=3.x` +- **Kích hoạt môi trường**: `conda activate tên_môi_trường` + +## 💡 Lưu ý quan trọng + +- **Tên môi trường**: Phải viết liền, không dấu +- **Phiên bản Python**: Khuyến khích sử dụng Python 3.10 hoặc 3.11 (3.7 và 3.8 đã không còn được hỗ trợ) +- **Cài đặt**: Tải Anaconda phù hợp với hệ điều hành của bạn + +## 🧪 Thực hành + +Hãy thử tạo một môi trường ảo mới cho dự án của bạn: + +```bash +# Tạo môi trường với Python 3.11 +conda create -n my_project python=3.11 + +# Kích hoạt môi trường +conda activate my_project + +# Kiểm tra phiên bản Python +python --version +``` + +## 📚 Tài liệu tham khảo + +- [Conda Cheat Sheet](https://docs.conda.io/projects/conda/en/4.6.0/_downloads/52a95608c49671267e40c689e0bc00ca/conda-cheatsheet.pdf) +- [Anaconda Documentation](https://docs.anaconda.com/) + +## ➡️ Bước tiếp theo + +Trong bài viết tiếp theo, ZootoPi sẽ tiếp tục giới thiệu đến các bạn về `JupyterLab` và `Jupyter Notebook`. Vậy 2 khái niệm này là gì? Sử dụng `JupyterLab` và `Jupyter Notebook` như thế nào để có thể làm việc hiệu quả? Hãy tiếp tục với [Bài 0: Jupyter](./02.jupyter.md)! + +Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/python/02.jupyter.md b/docs/tutorial/python/02.jupyter.md index 45fd88d5..7873c466 100644 --- a/docs/tutorial/python/02.jupyter.md +++ b/docs/tutorial/python/02.jupyter.md @@ -4,10 +4,20 @@ sidebar_label: "Bài 0: Jupyter" # Bài 0: Làm quen với Jupyter -## 1. Giới thiệu +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được Jupyter là gì và các thành phần của nó +- [ ] Phân biệt được Jupyter Notebook và Jupyter Lab +- [ ] Cài đặt và khởi chạy Jupyter Notebook/Jupyter Lab +- [ ] Sử dụng Jupyter để viết và chạy code Python tương tác + +## 📖 Giới thiệu Trong bài viết trước, ZootoPi đã giới thiệu về Anaconda, một mã nguồn mở giúp ta tạo ra những môi trường ảo với trình quản lí gói conda tiện lợi cho việc truy xuất và cài đặt các thư viện bên trong. Sau khi có môi trường rồi, ta sẽ cần có công cụ để viết, chạy mã nguồn cũng như gỡ lỗi chương trình. Và trong bài viết này, ZootoPi xin giới thiệu họ nhà Jupyter với Jupyter Notebook và Jupyter Lab. +## 1. Giới thiệu + ### 1.1. Jupyter Jupyter là một nền tảng tính toán khoa học mã nguồn mở, với khả năng nổi bật cho phép tương tác trực tiếp với từng dòng code, hỗ trợ hơn 40 ngôn ngữ lập trình, trong đó tập trung vào 3 ngôn ngữ là Julia, Python và R. @@ -35,7 +45,7 @@ Jupyter Notebook (trước 2014 được biết với cái tên IPython Notebook ### 1.3. Jupyter Lab -JupyterLab là môi trường phát triển tương tác dựa trên web dành cho notebook, mã và dữ liệu của Jupyter. Nó có cấu trúc mô-đun giúp ta có thể viết các plugin bổ sung các thành phần mới, tích hợp với các thành phần hiện có, và mở một số notebook hoặc tệp (ví dụ: HTML, Markdowns, v.v.) dưới dạng các tab trong cùng một cửa sổ cũng như cung cấp nhiều trải nghiệm giống như khi làm việc với các IDE. Điểm cộng của JupyterLab là sử linh hoạt, cho phép cấu hình và sắp xếp giao diện người dùng để hỗ trợ các quy trình trong khoa học dữ liệu, máy tính khoa học và máy học. +JupyterLab là môi trường phát triển tương tác dựa trên web dành cho notebook, mã và dữ liệu của Jupyter. Nó có cấu trúc mô-đun giúp ta có thể viết các plugin bổ sung các thành phần mới, tích hợp với các thành phần hiện có, và mở một số notebook hoặc tệp (ví dụ: HTML, Markdowns, v.v.) dưới dạng các tab trong cùng một cửa sổ cũng như cung cấp nhiều trải nghiệm giống như khi làm việc với các IDE. Điểm cộng của JupyterLab là sử dụng linh hoạt, cho phép cấu hình và sắp xếp giao diện người dùng để hỗ trợ các quy trình trong khoa học dữ liệu, máy tính khoa học và máy học. ![alt](./img/jupyterlab.png) @@ -71,7 +81,7 @@ Sau khi cài đặt xong, ta kích hoạt công cụ bằng câu lệnh sau: - Với Jupyter Lab: ```console -(zootopi) ➜ ~ jupyter notebook +(zootopi) ➜ ~ jupyter lab ``` Khi đó giao diện của công cụ sẽ hiện lên trên web browser của trình duyệt web bạn sử dụng (Chrome, Firefox, v.v) với đường dẫn tới: @@ -79,4 +89,60 @@ Khi đó giao diện của công cụ sẽ hiện lên trên web browser của t - Jupyter Notebook: `http://localhost:8888/tree` - Jupyter Lab: `http://localhost:8888/lab` -Trong bài viết tiếp theo, ZootoPi sẽ đi sâu hơn vào cách sử dụng các bạn về `JupyterLab` và `Jupyter Notebook` cũng như đưa ra những so sánh trực quan về tính năng khi làm việc với 2 công cụ này. ZootoPi xin phép hẹn bạn trong bài viết tiếp theo! +### So sánh Jupyter Notebook vs Jupyter Lab + +| Tính năng | Jupyter Notebook | Jupyter Lab | +|-----------|------------------|-------------| +| **Giao diện** | Đơn giản, tập trung vào notebook | Mô-đun, giống IDE | +| **Nhiều tab** | ❌ | ✅ | +| **File browser** | Cơ bản | Nâng cao | +| **Terminal tích hợp** | ❌ | ✅ | +| **Plugin/Extension** | Hạn chế | Nhiều | +| **Phù hợp cho** | Người mới bắt đầu | Người dùng nâng cao | + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **Jupyter**: Nền tảng tính toán khoa học mã nguồn mở, hỗ trợ 40+ ngôn ngữ +- **Jupyter Notebook**: Ứng dụng cho phép kết hợp code, văn bản, hình ảnh trong một file `.ipynb` +- **Jupyter Lab**: Môi trường phát triển tương tác dựa trên web, mạnh mẽ hơn Notebook +- **Cài đặt**: + - Khi tạo môi trường: `conda create -n tên_môi_trường python=3.x jupyter jupyterlab` + - Sau khi có môi trường: `pip install jupyter jupyterlab` +- **Khởi chạy**: + - Jupyter Notebook: `jupyter notebook` → `http://localhost:8888/tree` + - Jupyter Lab: `jupyter lab` → `http://localhost:8888/lab` + +## 💡 Lưu ý quan trọng + +- **File extension**: Jupyter sử dụng file `.ipynb` (IPython Notebook) +- **Tương tác**: Có thể chạy từng cell code riêng lẻ, rất hữu ích cho phân tích dữ liệu +- **Markdown**: Hỗ trợ Markdown để viết tài liệu cùng với code +- **Visualization**: Tích hợp hiển thị kết quả và biểu đồ trực tiếp trong notebook + +## 🧪 Thực hành + +Hãy thử tạo một notebook mới và chạy code Python: + +1. Khởi chạy Jupyter Lab: `jupyter lab` +2. Tạo notebook mới: Click "New" → "Python 3" +3. Thử chạy code: + ```python + print("Hello, Jupyter!") + import pandas as pd + df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + df + ``` + +## 📚 Tài liệu tham khảo + +- [Jupyter Documentation](https://jupyter.org/documentation) +- [Jupyter Notebook Tutorial](https://jupyter-notebook.readthedocs.io/) +- [Jupyter Lab Documentation](https://jupyterlab.readthedocs.io/) + +## ➡️ Bước tiếp theo + +Trong bài viết tiếp theo, ZootoPi sẽ đi sâu hơn vào cách sử dụng `JupyterLab` và `Jupyter Notebook` cũng như đưa ra những so sánh trực quan về tính năng khi làm việc với 2 công cụ này. Sau đó, chúng ta sẽ bắt đầu với [Bài 1: Cài đặt Python](./03.install.ipynb)! + +Chúc các bạn học tập vui vẻ! diff --git a/docs/tutorial/python/03.install.ipynb b/docs/tutorial/python/03.install.ipynb index 343ca4fc..c59e40af 100644 --- a/docs/tutorial/python/03.install.ipynb +++ b/docs/tutorial/python/03.install.ipynb @@ -1,178 +1,238 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "f8602d8d", - "metadata": {}, - "source": [ - "# Bài 1: Hướng dẫn cài đặt" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "f8602d8d", + "metadata": {}, + "source": [ + "# Bài 1: Hướng dẫn cài đặt\n", + "\n", + "## 🎯 Mục tiêu học tập\n", + "\n", + "Sau khi hoàn thành bài này, bạn sẽ có thể:\n", + "- [ ] Hiểu được GitHub là gì và cách sử dụng GitHub Desktop\n", + "- [ ] Cài đặt và sử dụng Anaconda để quản lý môi trường ảo\n", + "- [ ] Phân biệt được Jupyter Notebook, Jupyter Lab và Google Colab\n", + "- [ ] Sử dụng pip để cài đặt các thư viện Python\n", + "- [ ] Thiết lập môi trường làm việc cho Khoa học dữ liệu" + ] + }, + { + "cell_type": "markdown", + "id": "057de08f", + "metadata": {}, + "source": [ + "## 1. Github & Github Desktop" + ] + }, + { + "cell_type": "markdown", + "id": "5021a0e5", + "metadata": {}, + "source": [ + "**GitHub** là sự kết hợp giữa 2 từ:\n", + "\n", + "> **Git** – hệ thống quản lý dự án và phiên bản code.\n", + "\n", + "> **Hub** – nơi biến những dòng lệnh trên Git thành mạng xã hội cho lập trình viên.\n", + "\n", + "Khi chúng ta tham gia các dự án công nghệ, ta thường làm việc theo nhóm. Github được sinh ra như một nơi lưu trữ các tài nguyên chung của nhóm, nơi quản lý các phiên bản giúp các thành viên trong nhóm có thể giám sát toàn bộ thay đổi của dự án, từ đó tăng sự hiệu quả làm việc. \n", + "\n", + "Một thuật ngữ cần nhớ:\n", + "> **Repository**: kho lưu trữ, chứa toàn bộ dữ liệu thông tin, hình ảnh, video, bảng biểu… và các thay đổi trong quá trình thực hiện dự án. \n", + "\n", + "> **Push**: lệnh đưa nội dung mà bạn commit từ kho lưu trữ ở local lên kho lưu trữ server.\n", + "\n", + "> **Fetch**: lệnh lấy dữ liệu trên kho lưu trữ server để tích hợp vào branch.\n", + "\n", + "> **Pull/rebase**: lệnh sử dụng trên kho lưu trữ server, giúp bạn di chuyển toàn bộ dữ liệu trên kho này về máy tính để tích hợp dữ liệu vào branch.\n", + "\n", + "> **Commit**: thao tác ghi lại việc thêm/thay đổi file hay thư mục vào kho lưu trữ. \n", + "\n", + "![](https://user-images.githubusercontent.com/359239/44031406-4c5ebaf0-9eda-11e8-826a-b0d4ab6fd98f.png)\n", + "\n", + "Với những bạn mới bắt đầu tìm hiểu và chưa quen sử dụng những câu lệnh trên Terminal/Prompt máy tính, Github Desktop là một công cụ trực quan quản lý kho lưu trữ local, cũng như giải pháp giúp cho các bạn làm quen với Github dễ dàng hơn khi thực hiện các thao tác, dòng lệch Git cơ bản thông qua những button có thể dễ dàng click chuột trên giao diện.\n", + "\n", + "Tham khảo:\n", + "- Tải Github Desktop tại [đây](https://desktop.github.com/).\n", + "- Sổ tay Git tại [đây](https://rogerdudler.github.io/git-guide/index.vi.html?fbclid=IwAR3obi7MWsbgXSiTUSg9QCzP7a8-vX7k9ASu8u5A-WuUVvP_eigo4E0hCbo)." + ] + }, + { + "cell_type": "markdown", + "id": "a36ba9d0", + "metadata": {}, + "source": [ + "## 2. Anaconda" + ] + }, + { + "cell_type": "markdown", + "id": "9c66fc41", + "metadata": {}, + "source": [ + "Anaconda là 1 nền tảng phân phối các thư viện Python với hơn 25 triệu người dùng, giúp bạn có thể dễ dàng quản lý các môi trường ảo. Mình khuyến khích các bạn mới bắt đầu học làm quen và dùng Anaconda bởi những lí do sau: \n", + "\n", + "- Anaconda đơn giản, dễ sử dụng với 1 hệ sinh thái nguồn mở, đáp ứng nhu cầu về khoa học dữ liệu với Python.\n", + "- Mình khuyến khích mọi người dùng môi trường ảo và cài những thư viện cần thiết cho 1 dự án của bạn trên 1 môi trường ảo. \n", + " - Khi ta làm nhiều dự án và cài hết các thư viện cần thiết từ các dự án khác nhau về 1 nơi thì các thư viện, tài nguyên dễ bị xung đột phiên bản, ảnh hưởng đến việc thực thi mã nguồn của bạn. Với Anaconda, mỗi dự án ta có thể dễ dàng tạo 1 môi trường ảo riêng, sau khi kết thúc dự án ta chỉ cần xoá cái môi trường ảo là xong, đơn giản và nhanh gọn.\n", + " - Khi làm theo nhóm, việc sử dụng môi trường ảo cũng giúp các bạn chia sẻ các thư viện đúng phiên bản được cài đặt với người khác, đảm bảo sự đồng nhất trong phiên bản giữa các bạn trong nhóm. \n", + "\n", + "Thông thường, khi bạn cài Anaconda thì nó sẽ tự động cài thêm cho bạn Python và 1 số thư viện Python hữu ích như Matplotlib, NumPy và SciPy. Ta có thể làm quen với Terminal/Anaconda Prompt bằng 1 số câu lệnh cơ bản:\n", + "> `python --version`: kiểm tra phiên bản của Python, phiên bản mới nhất hiện tại là 3.12+ (khuyến nghị sử dụng Python 3.10 hoặc 3.11)\n", + "\n", + "> `where conda`: tìm xem vị trí conda được lưu tại đâu trong máy \n", + "\n", + "> `pwd` (print working directory): vị trí hiện tại đang trỏ đến, đang làm việc\n", + "\n", + "> `open folder-name`: mở thư mục\n", + "\n", + "Một số câu lệnh cơ bản với Anaconda:\n", + "> `conda create -n zootopi python=3.8`: tạo môi trường ảo với python phiên bản 3.8\n", + "\n", + "> `conda install packagename`: cài đặt thư viện với `packagename` là tên của thư viện, ví dụ `pandas`, `seaborn`, ...\n", + "\n", + "> `conda env remove -n zootopi`: xoá môi trường ảo\n", + "\n", + "> `conda env list`: liệt kê các môi trường ảo đã tạo" + ] + }, + { + "cell_type": "markdown", + "id": "a6b2f4e2", + "metadata": {}, + "source": [ + "Tham khảo:\n", + "- Tải Anaconda tại [đây](https://www.anaconda.com/products/individual)." + ] + }, + { + "cell_type": "markdown", + "id": "4d59ce86", + "metadata": {}, + "source": [ + "## 3. Jupyter vs Colab\n", + "\n", + "### 3.1. Jupyter, Jupyter Notebook, Jupyter Lab\n", + "\n", + "Jupyter là một nền tảng tính toán khoa học mã nguồn mở cho phép tương tác trực tiếp với từng dòng code, hỗ trợ hơn 40 ngôn ngữ lập trình, trong đó tập trung vào 3 ngôn ngữ là Julia, Python và R và cái tên Jupyter bắt nguồn từ cách chơi chữ Jupyter = Julia + Python + R. Trước đây là nó có tên là IPython, đến năm 2014 lại đổi tên thành Jupyter.\n", + "\n", + "- Là môi trường làm việc phổ biến nhất cho phân tích **Khoa học dữ liệu** bằng Python\n", + " > `Jupyter Notebook >> Jupyter Lab`\n", + "\n", + "- Các file Python gốc sẽ có đuôi `.py`. và file jupyter sẽ có đuôi là `.ipynb`.\n", + "\n", + "- **Jupyter** cung cấp môi trường làm việc:\n", + " 1. Đa ngôn ngữ \n", + " > `Jupyter = Julia + Python + R`\n", + " 2. Đa nền tảng: `Windows`,`Ubuntu`,`MacOS`\n", + " 3. Nền web\n", + " 4. Tích hợp hiển thị kết quả lập trình và trực quan hoá dữ liệu\n", + "\n", + "Để chạy notebook, ta sử dụng câu lệnh `jupyter lab` hoặc `jupyter notebook`.\n", + "\n", + "### 3.2. Trình quản lý thử viện PIP\n", + "\n", + "- PIP `pip` viết tắt của `Package Installer for Python`, là trình quản lý giúp người dùng cài đặt, gỡ bỏ và cập nhật các gói thư viện trên Python. \n", + "\n", + "- Để cài đặt, ta sử dụng câu lệnh `pip install từ Anaconda Prompt` và `pip` sẽ tự động tải xuống và cài đặt theo yêu cầu của người dùng, ví dụ:\n", + "\n", + "```python\n", + "!pip install scikit-learn\n", + "```\n", + "\n", + "### 3.3. Google Colab (optional)\n", + "- Môi trường `colab` là 1 nền tảng được cung cấp miễn phí bởi Google và chạy trên nền Jupyter, giúp người dùng có thể dễ dàng mở và chạy `Jupyter notebook` từ bất kỳ đâu mà không cần cài đặt bất kỳ thứ gì.\n", + "\n", + "- Truy cập `colab` tại địa chỉ https://colab.research.google.com/ và bắt đầu viết code.\n", + "\n", + "- Giao diện `colab` gần như giống y hệt với `Jupyter notebook`. Tuy nhiên tính năng thì vô vàn! Ví dụ khả năng kết nối tới Google Drive hay khả năng chia sẽ notebook ..." + ] + }, + { + "cell_type": "markdown", + "id": "eac56327", + "metadata": {}, + "source": [ + "## ✅ Tóm tắt\n", + "\n", + "Trong bài này, chúng ta đã tìm hiểu:\n", + "\n", + "- **GitHub & GitHub Desktop**: Công cụ quản lý phiên bản code và dự án\n", + " - Repository, Push, Pull, Commit, Fetch\n", + "- **Anaconda**: Nền tảng quản lý môi trường ảo và thư viện Python\n", + " - Tạo môi trường: `conda create -n tên_môi_trường python=3.x`\n", + " - Cài đặt thư viện: `conda install packagename`\n", + "- **Jupyter**: Môi trường làm việc tương tác cho Khoa học dữ liệu\n", + " - Jupyter Notebook vs Jupyter Lab\n", + " - File extension: `.ipynb`\n", + "- **PIP**: Trình quản lý thư viện Python\n", + " - Cài đặt: `pip install tên_thư_viện`\n", + "- **Google Colab**: Nền tảng miễn phí chạy Jupyter trên cloud\n", + "\n", + "## 💡 Lưu ý quan trọng\n", + "\n", + "- **Môi trường ảo**: Luôn sử dụng môi trường ảo riêng cho mỗi dự án để tránh xung đột phiên bản\n", + "- **Python version**: Khuyến nghị sử dụng Python 3.10 hoặc 3.11\n", + "- **GitHub Desktop**: Công cụ hữu ích cho người mới bắt đầu với Git\n", + "\n", + "## 🧪 Thực hành\n", + "\n", + "Hãy thử thực hiện các bước sau:\n", + "\n", + "1. **Cài đặt Anaconda** (nếu chưa có)\n", + "2. **Tạo môi trường ảo mới**:\n", + " ```bash\n", + " conda create -n my_project python=3.11\n", + " conda activate my_project\n", + " ```\n", + "3. **Cài đặt Jupyter Lab**:\n", + " ```bash\n", + " pip install jupyterlab\n", + " jupyter lab\n", + " ```\n", + "4. **Cài đặt các thư viện cơ bản**:\n", + " ```bash\n", + " pip install pandas numpy matplotlib seaborn\n", + " ```\n", + "\n", + "## 📚 Tài liệu tham khảo\n", + "\n", + "- [GitHub Desktop](https://desktop.github.com/)\n", + "- [Sổ tay Git](https://rogerdudler.github.io/git-guide/index.vi.html)\n", + "- [Anaconda](https://www.anaconda.com/products/individual)\n", + "- [Google Colab](https://colab.research.google.com/)\n", + "\n", + "## ➡️ Bước tiếp theo\n", + "\n", + "Ở trong [bài tiếp theo](./04.foundation.ipynb), chúng ta sẽ làm quen với những thành phần cơ bản của Python." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.2 64-bit", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.2" + }, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } }, - { - "cell_type": "markdown", - "id": "057de08f", - "metadata": {}, - "source": [ - "## 1. Github & Github Desktop" - ] - }, - { - "cell_type": "markdown", - "id": "5021a0e5", - "metadata": {}, - "source": [ - "**GitHub** là sự kết hợp giữa 2 từ:\n", - "\n", - "> **Git** – hệ thống quản lý dự án và phiên bản code.\n", - "\n", - "> **Hub** – nơi biến những dòng lệnh trên Git thành mạng xã hội cho lập trình viên.\n", - "\n", - "Khi chúng ta tham gia các dự án công nghệ, ta thường làm việc theo nhóm. Github được sinh ra như một nơi lưu trữ các tài nguyên chung của nhóm, nơi quản lý các phiên bản giúp các thành viên trong nhóm có thể giám sát toàn bộ thay đổi của dự án, từ đó tăng sự hiệu quả làm việc. \n", - "\n", - "Một thuật ngữ cần nhớ:\n", - "> **Repository**: kho lưu trữ, chứa toàn bộ dữ liệu thông tin, hình ảnh, video, bảng biểu… và các thay đổi trong quá trình thực hiện dự án. \n", - "\n", - "> **Push**: lệnh đưa nội dung mà bạn commit từ kho lưu trữ ở local lên kho lưu trữ server.\n", - "\n", - "> **Fetch**: lệnh lấy dữ liệu trên kho lưu trữ server để tích hợp vào branch.\n", - "\n", - "> **Pull/rebase**: lệnh sử dụng trên kho lưu trữ server, giúp bạn di chuyển toàn bộ dữ liệu trên kho này về máy tính để tích hợp dữ liệu vào branch.\n", - "\n", - "> **Commit**: thao tác ghi lại việc thêm/thay đổi file hay thư mục vào kho lưu trữ. \n", - "\n", - "![](https://user-images.githubusercontent.com/359239/44031406-4c5ebaf0-9eda-11e8-826a-b0d4ab6fd98f.png)\n", - "\n", - "Với những bạn mới bắt đầu tìm hiểu và chưa quen sử dụng những câu lệnh trên Terminal/Prompt máy tính, Github Desktop là một công cụ trực quan quản lý kho lưu trữ local, cũng như giải pháp giúp cho các bạn làm quen với Github dễ dàng hơn khi thực hiện các thao tác, dòng lệch Git cơ bản thông qua những button có thể dễ dàng click chuột trên giao diện.\n", - "\n", - "Tham khảo:\n", - "- Tải Github Desktop tại [đây](https://desktop.github.com/).\n", - "- Sổ tay Git tại [đây](https://rogerdudler.github.io/git-guide/index.vi.html?fbclid=IwAR3obi7MWsbgXSiTUSg9QCzP7a8-vX7k9ASu8u5A-WuUVvP_eigo4E0hCbo)." - ] - }, - { - "cell_type": "markdown", - "id": "a36ba9d0", - "metadata": {}, - "source": [ - "## 2. Anaconda" - ] - }, - { - "cell_type": "markdown", - "id": "9c66fc41", - "metadata": {}, - "source": [ - "Anaconda là 1 nền tảng phân phối các thư viện Python với hơn 25 triệu người dùng, giúp bạn có thể dễ dàng quản lý các môi trường ảo. Mình khuyến khích các bạn mới bắt đầu học làm quen và dùng Anaconda bởi những lí do sau: \n", - "\n", - "- Anaconda đơn giản, dễ sử dụng với 1 hệ sinh thái nguồn mở, đáp ứng nhu cầu về khoa học dữ liệu với Python.\n", - "- Mình khuyến khích mọi người dùng môi trường ảo và cài những thư viện cần thiết cho 1 dự án của bạn trên 1 môi trường ảo. \n", - " - Khi ta làm nhiều dự án và cài hết các thư viện cần thiết từ các dự án khác nhau về 1 nơi thì các thư viện, tài nguyên dễ bị xung đột phiên bản, ảnh hưởng đến việc thực thi mã nguồn của bạn. Với Anaconda, mỗi dự án ta có thể dễ dàng tạo 1 môi trường ảo riêng, sau khi kết thúc dự án ta chỉ cần xoá cái môi trường ảo là xong, đơn giản và nhanh gọn.\n", - " - Khi làm theo nhóm, việc sử dụng môi trường ảo cũng giúp các bạn chia sẻ các thư viện đúng phiên bản được cài đặt với người khác, đảm bảo sự đồng nhất trong phiên bản giữa các bạn trong nhóm. \n", - "\n", - "Thông thường, khi bạn cài Anaconda thì nó sẽ tự động cài thêm cho bạn Python và 1 số thư viện Python hữu ích như Matplotlib, NumPy và SciPy. Ta có thể làm quen với Terminal/Anaconda Prompt bằng 1 số câu lệnh cơ bản:\n", - "> `python --version`: kiểm tra phiên bản của Python, phiên bản mới nhất hiện tại là 3.9.6\n", - "\n", - "> `where conda`: tìm xem vị trí conda được lưu tại đâu trong máy \n", - "\n", - "> `pwd` (print working directory): vị trí hiện tại đang trỏ đến, đang làm việc\n", - "\n", - "> `open folder-name`: mở thư mục\n", - "\n", - "Một số câu lệnh cơ bản với Anaconda:\n", - "> `conda create -n zootopi python=3.8`: tạo môi trường ảo với python phiên bản 3.8\n", - "\n", - "> `conda install packagename`: cài đặt thư viện với `packagename` là tên của thư viện, ví dụ `pandas`, `seaborn`, ...\n", - "\n", - "> `conda env remove -n zootopi`: xoá môi trường ảo\n", - "\n", - "> `conda env list`: liệt kê các môi trường ảo đã tạo" - ] - }, - { - "cell_type": "markdown", - "id": "a6b2f4e2", - "metadata": {}, - "source": [ - "Tham khảo:\n", - "- Tải Anaconda tại [đây](https://www.anaconda.com/products/individual)." - ] - }, - { - "cell_type": "markdown", - "id": "4d59ce86", - "metadata": {}, - "source": [ - "## 3. Jupyter vs Colab\n", - "\n", - "### 3.1. Jupyter, Jupyter Notebook, Jupyter Lab\n", - "\n", - "Jupyter là một nền tảng tính toán khoa học mã nguồn mở cho phép tương tác trực tiếp với từng dòng code, hỗ trợ hơn 40 ngôn ngữ lập trình, trong đó tập trung vào 3 ngôn ngữ là Julia, Python và R và cái tên Jupyter bắt nguồn từ cách chơi chữ Jupyter = Julia + Python + R. Trước đây là nó có tên là IPython, đến năm 2014 lại đổi tên thành Jupyter.\n", - "\n", - "- Là môi trường làm việc phổ biến nhất cho phân tích **Khoa học dữ liệu** bằng Python\n", - " > `Jupyter Notebook >> Jupyter Lab`\n", - "\n", - "- Các file Python gốc sẽ có đuôi `.py`. và file jupyter sẽ có đuôi là `.ipynb`.\n", - "\n", - "- **Jupyter** cung cấp môi trường làm việc:\n", - " 1. Đa ngôn ngữ \n", - " > `Jupyter = Julia + Python + R`\n", - " 2. Đa nền tảng: `Windows`,`Ubuntu`,`MacOS`\n", - " 3. Nền web\n", - " 4. Tích hợp hiển thị kết quả lập trình và trực quan hoá dữ liệu\n", - "\n", - "Để chạy notebook, ta sử dụng câu lệnh `jupyter lab` hoặc `jupyter notebook`.\n", - "\n", - "### 3.2. Trình quản lý thử viện PIP\n", - "\n", - "- PIP `pip` viết tắt của `Package Installer for Python`, là trình quản lý giúp người dùng cài đặt, gỡ bỏ và cập nhật các gói thư viện trên Python. \n", - "\n", - "- Để cài đặt, ta sử dụng câu lệnh `pip install tử Anaconda Prompt` và `pip` sẽ tự động tải xuống và cài đặt theo yêu cầu của người dùng, ví dụ:\n", - "\n", - "```python\n", - "!pip install scikit-learn\n", - "```\n", - "\n", - "### 3.3. Google Colab (optional)\n", - "- Môi trường `colab` là 1 nền tảng được cung cấp miễn phí bởi Google và chạy trên nền Jupyter, giúp người dùng có thể dễ dàng mở và chạy `Jupyter notebook` từ bất kỳ đâu mà không cần cài đặt bất kỳ thứ gì.\n", - "\n", - "- Truy cập `colab` tại địa chỉ https://colab.research.google.com/ và bắt đầu viết code.\n", - "\n", - "- Giao diện `colab` gần như giống y hệt với `Jupyter notebook`. Tuy nhiên tính năng thì vô vàn! Ví dụ khả năng kết nối tới Google Drive hay khả năng chia sẽ notebook ..." - ] - }, - { - "cell_type": "markdown", - "id": "eac56327", - "metadata": {}, - "source": [ - "Ở trong [bài tiếp theo](./foundation), chúng ta sẽ làm quen với những thành phần cơ bản của Python." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.8.2 64-bit", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.2" - }, - "vscode": { - "interpreter": { - "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/docs/tutorial/python/03.install.md b/docs/tutorial/python/03.install.md new file mode 100644 index 00000000..acc97067 --- /dev/null +++ b/docs/tutorial/python/03.install.md @@ -0,0 +1,159 @@ +# Bài 1: Hướng dẫn cài đặt + +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được GitHub là gì và cách sử dụng GitHub Desktop +- [ ] Cài đặt và sử dụng Anaconda để quản lý môi trường ảo +- [ ] Phân biệt được Jupyter Notebook, Jupyter Lab và Google Colab +- [ ] Sử dụng pip để cài đặt các thư viện Python +- [ ] Thiết lập môi trường làm việc cho Khoa học dữ liệu + +## 1. Github & Github Desktop + +**GitHub** là sự kết hợp giữa 2 từ: + +> **Git** – hệ thống quản lý dự án và phiên bản code. + +> **Hub** – nơi biến những dòng lệnh trên Git thành mạng xã hội cho lập trình viên. + +Khi chúng ta tham gia các dự án công nghệ, ta thường làm việc theo nhóm. Github được sinh ra như một nơi lưu trữ các tài nguyên chung của nhóm, nơi quản lý các phiên bản giúp các thành viên trong nhóm có thể giám sát toàn bộ thay đổi của dự án, từ đó tăng sự hiệu quả làm việc. + +Một thuật ngữ cần nhớ: +> **Repository**: kho lưu trữ, chứa toàn bộ dữ liệu thông tin, hình ảnh, video, bảng biểu… và các thay đổi trong quá trình thực hiện dự án. + +> **Push**: lệnh đưa nội dung mà bạn commit từ kho lưu trữ ở local lên kho lưu trữ server. + +> **Fetch**: lệnh lấy dữ liệu trên kho lưu trữ server để tích hợp vào branch. + +> **Pull/rebase**: lệnh sử dụng trên kho lưu trữ server, giúp bạn di chuyển toàn bộ dữ liệu trên kho này về máy tính để tích hợp dữ liệu vào branch. + +> **Commit**: thao tác ghi lại việc thêm/thay đổi file hay thư mục vào kho lưu trữ. + +![](https://user-images.githubusercontent.com/359239/44031406-4c5ebaf0-9eda-11e8-826a-b0d4ab6fd98f.png) + +Với những bạn mới bắt đầu tìm hiểu và chưa quen sử dụng những câu lệnh trên Terminal/Prompt máy tính, Github Desktop là một công cụ trực quan quản lý kho lưu trữ local, cũng như giải pháp giúp cho các bạn làm quen với Github dễ dàng hơn khi thực hiện các thao tác, dòng lệch Git cơ bản thông qua những button có thể dễ dàng click chuột trên giao diện. + +Tham khảo: +- Tải Github Desktop tại [đây](https://desktop.github.com/). +- Sổ tay Git tại [đây](https://rogerdudler.github.io/git-guide/index.vi.html?fbclid=IwAR3obi7MWsbgXSiTUSg9QCzP7a8-vX7k9ASu8u5A-WuUVvP_eigo4E0hCbo). + +## 2. Anaconda + +Anaconda là 1 nền tảng phân phối các thư viện Python với hơn 25 triệu người dùng, giúp bạn có thể dễ dàng quản lý các môi trường ảo. Mình khuyến khích các bạn mới bắt đầu học làm quen và dùng Anaconda bởi những lí do sau: + +- Anaconda đơn giản, dễ sử dụng với 1 hệ sinh thái nguồn mở, đáp ứng nhu cầu về khoa học dữ liệu với Python. +- Mình khuyến khích mọi người dùng môi trường ảo và cài những thư viện cần thiết cho 1 dự án của bạn trên 1 môi trường ảo. + - Khi ta làm nhiều dự án và cài hết các thư viện cần thiết từ các dự án khác nhau về 1 nơi thì các thư viện, tài nguyên dễ bị xung đột phiên bản, ảnh hưởng đến việc thực thi mã nguồn của bạn. Với Anaconda, mỗi dự án ta có thể dễ dàng tạo 1 môi trường ảo riêng, sau khi kết thúc dự án ta chỉ cần xoá cái môi trường ảo là xong, đơn giản và nhanh gọn. + - Khi làm theo nhóm, việc sử dụng môi trường ảo cũng giúp các bạn chia sẻ các thư viện đúng phiên bản được cài đặt với người khác, đảm bảo sự đồng nhất trong phiên bản giữa các bạn trong nhóm. + +Thông thường, khi bạn cài Anaconda thì nó sẽ tự động cài thêm cho bạn Python và 1 số thư viện Python hữu ích như Matplotlib, NumPy và SciPy. Ta có thể làm quen với Terminal/Anaconda Prompt bằng 1 số câu lệnh cơ bản: +> `python --version`: kiểm tra phiên bản của Python, phiên bản mới nhất hiện tại là 3.12+ (khuyến nghị sử dụng Python 3.10 hoặc 3.11) + +> `where conda`: tìm xem vị trí conda được lưu tại đâu trong máy + +> `pwd` (print working directory): vị trí hiện tại đang trỏ đến, đang làm việc + +> `open folder-name`: mở thư mục + +Một số câu lệnh cơ bản với Anaconda: +> `conda create -n zootopi python=3.8`: tạo môi trường ảo với python phiên bản 3.8 + +> `conda install packagename`: cài đặt thư viện với `packagename` là tên của thư viện, ví dụ `pandas`, `seaborn`, ... + +> `conda env remove -n zootopi`: xoá môi trường ảo + +> `conda env list`: liệt kê các môi trường ảo đã tạo + +Tham khảo: +- Tải Anaconda tại [đây](https://www.anaconda.com/products/individual). + +## 3. Jupyter vs Colab + +### 3.1. Jupyter, Jupyter Notebook, Jupyter Lab + +Jupyter là một nền tảng tính toán khoa học mã nguồn mở cho phép tương tác trực tiếp với từng dòng code, hỗ trợ hơn 40 ngôn ngữ lập trình, trong đó tập trung vào 3 ngôn ngữ là Julia, Python và R và cái tên Jupyter bắt nguồn từ cách chơi chữ Jupyter = Julia + Python + R. Trước đây là nó có tên là IPython, đến năm 2014 lại đổi tên thành Jupyter. + +- Là môi trường làm việc phổ biến nhất cho phân tích **Khoa học dữ liệu** bằng Python + > `Jupyter Notebook >> Jupyter Lab` + +- Các file Python gốc sẽ có đuôi `.py`. và file jupyter sẽ có đuôi là `.ipynb`. + +- **Jupyter** cung cấp môi trường làm việc: + 1. Đa ngôn ngữ + > `Jupyter = Julia + Python + R` + 2. Đa nền tảng: `Windows`,`Ubuntu`,`MacOS` + 3. Nền web + 4. Tích hợp hiển thị kết quả lập trình và trực quan hoá dữ liệu + +Để chạy notebook, ta sử dụng câu lệnh `jupyter lab` hoặc `jupyter notebook`. + +### 3.2. Trình quản lý thử viện PIP + +- PIP `pip` viết tắt của `Package Installer for Python`, là trình quản lý giúp người dùng cài đặt, gỡ bỏ và cập nhật các gói thư viện trên Python. + +- Để cài đặt, ta sử dụng câu lệnh `pip install từ Anaconda Prompt` và `pip` sẽ tự động tải xuống và cài đặt theo yêu cầu của người dùng, ví dụ: + +```python +!pip install scikit-learn +``` + +### 3.3. Google Colab (optional) +- Môi trường `colab` là 1 nền tảng được cung cấp miễn phí bởi Google và chạy trên nền Jupyter, giúp người dùng có thể dễ dàng mở và chạy `Jupyter notebook` từ bất kỳ đâu mà không cần cài đặt bất kỳ thứ gì. + +- Truy cập `colab` tại địa chỉ https://colab.research.google.com/ và bắt đầu viết code. + +- Giao diện `colab` gần như giống y hệt với `Jupyter notebook`. Tuy nhiên tính năng thì vô vàn! Ví dụ khả năng kết nối tới Google Drive hay khả năng chia sẽ notebook ... + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **GitHub & GitHub Desktop**: Công cụ quản lý phiên bản code và dự án + - Repository, Push, Pull, Commit, Fetch +- **Anaconda**: Nền tảng quản lý môi trường ảo và thư viện Python + - Tạo môi trường: `conda create -n tên_môi_trường python=3.x` + - Cài đặt thư viện: `conda install packagename` +- **Jupyter**: Môi trường làm việc tương tác cho Khoa học dữ liệu + - Jupyter Notebook vs Jupyter Lab + - File extension: `.ipynb` +- **PIP**: Trình quản lý thư viện Python + - Cài đặt: `pip install tên_thư_viện` +- **Google Colab**: Nền tảng miễn phí chạy Jupyter trên cloud + +## 💡 Lưu ý quan trọng + +- **Môi trường ảo**: Luôn sử dụng môi trường ảo riêng cho mỗi dự án để tránh xung đột phiên bản +- **Python version**: Khuyến nghị sử dụng Python 3.10 hoặc 3.11 +- **GitHub Desktop**: Công cụ hữu ích cho người mới bắt đầu với Git + +## 🧪 Thực hành + +Hãy thử thực hiện các bước sau: + +1. **Cài đặt Anaconda** (nếu chưa có) +2. **Tạo môi trường ảo mới**: + ```bash + conda create -n my_project python=3.11 + conda activate my_project + ``` +3. **Cài đặt Jupyter Lab**: + ```bash + pip install jupyterlab + jupyter lab + ``` +4. **Cài đặt các thư viện cơ bản**: + ```bash + pip install pandas numpy matplotlib seaborn + ``` + +## 📚 Tài liệu tham khảo + +- [GitHub Desktop](https://desktop.github.com/) +- [Sổ tay Git](https://rogerdudler.github.io/git-guide/index.vi.html) +- [Anaconda](https://www.anaconda.com/products/individual) +- [Google Colab](https://colab.research.google.com/) + +## ➡️ Bước tiếp theo + +Ở trong [bài tiếp theo](./04.foundation.ipynb), chúng ta sẽ làm quen với những thành phần cơ bản của Python. diff --git a/docs/tutorial/python/04.foundation.ipynb b/docs/tutorial/python/04.foundation.ipynb index 73022535..030df3e1 100644 --- a/docs/tutorial/python/04.foundation.ipynb +++ b/docs/tutorial/python/04.foundation.ipynb @@ -1,2218 +1,2254 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "59c8a7dc", - "metadata": {}, - "source": [ - "# Bài 2: Giới thiệu về PYTHON " - ] - }, - { - "cell_type": "markdown", - "id": "b3672a13", - "metadata": {}, - "source": [ - "\n", - "![](https://laptrinhcanban.com/python/nhap-mon-lap-trinh-python/gioi-thieu-python/python-la-gi/Python.jpg)" - ] - }, - { - "cell_type": "markdown", - "id": "2012c7f7", - "metadata": {}, - "source": [ - "## 1. Giới thiệu\n", - "\n", - "### 1.1. Tổng quan\n", - "\n", - "Theo thống kê của Wiki, tính đến đến tháng 10/2010, trên thế giới có khoảng hơn 700 ngôn ngữ lập trình (xem danh sách các ngôn ngữ lập trình tại [đây](https://vi.wikipedia.org/wiki/Danh_s%C3%A1ch_c%C3%A1c_ng%C3%B4n_ng%E1%BB%AF_l%E1%BA%ADp_tr%C3%ACnh)). Theo thống kê của [FOLDOC](http://foldoc.org/), 1 trang từ điển tin học trực tuyến miễn phí, con số này đã lên 1000 vào tháng 9/2020, 1 con số đáng kinh ngạc.\n", - "\n", - "![Độ phổ biến của 1 số ngôn ngữ lập trình được sử dụng trên các kho lưu trữ trên GitHub.](./img/python.png)\n", - "> *Độ phổ biến của 1 số ngôn ngữ lập trình được sử dụng trên các kho lưu trữ trên GitHub tính đến tháng 4/2014 theo [Githut](https://githut.info/).*\n", - "\n", - "Cũng giống như ngôn ngữ nói, ngôn ngữ lập trình cũng tuân theo 1 hệ thống phân cấp bao gồm 2 loại chính.\n", - "\n", - "- ***Ngôn ngữ bậc thấp***: Cú pháp cực kỳ gần với ngôn ngữ máy nên rất khó sử dụng, tuy nhiên lại cung cấp nhiều chức năng hơn và cho phép ta tạo ra chương trình chi tiết và hiệu quả hơn rất nhiều, thích hợp để viết các chương trình liên quan đến kiến trúc và phần cứng của máy tính. Các ngôn ngữ này có thể được chia thành hai loại: ngôn ngữ máy và hợp ngữ.\n", - "\n", - "- ***Ngôn ngữ bậc cao***: Cú pháp gẫn gũi với ngôn ngữ của con người hơn, tương tự như ngôn ngữ tiếng Anh nên dễ hiểu và thân thiện với người dùng hơn, xử lý lỗi nhanh hơn, thích hợp để phát triển phần mềm, xử lý dữ liệu... Một số ngôn ngữ bậc cao nổi tiếng nhất có thể kể đến C, C ++, Java và Python. \n", - "\n", - "### 1.2. Ngôn ngữ lập trình Python\n", - "\n", - "Một số điểm nổi bật:\n", - "- Được phát triển từ 1980s và chính thức phát hành vào năm 1991 bởi `Guido van Rossum`.\n", - "- Là ngôn ngữ lập trình `bậc cao` - `high level`: người dùng lập trình bằng ngôn ngữ dễ hiểu và dễ sử dụng, sau đó máy tính sẽ chuyển các đoạn mã thành ngôn ngữ máy tính.\n", - "- Là ngôn ngữ `thông dịch`-`intepreted`: code được viết ra sẽ được chuyển thể trực tiếp thành ngôn ngữ máy tính và chạy trực tiếp cùng 1 lúc. Điều này khiến việc sử dụng Python dễ dàng hơn so với các ngôn ngữ `biên dịch`-`compiled` - code được chuyển thể thành ngôn ngữ máy tính trước rồi mới có thể chạy.\n", - "- Là ngôn ngữ `đa năng`-`generally purposed`: có thể sử dụng cho nhiều mục đích khác nhau.\n", - "- Là ngôn ngữ `dynamic type`: không cần khai báo trước kiểu dữ liệu của biến, có thể ghi đè trực tiếp kiểu dữ liệu của biến.\n", - "- Là ngôn ngữ `tuần tự`-`procedural` đồng thời `hướng đối tượng`-`object-oriented`.\n", - "- Phiên bản ổn định hiện tại là phiên bản `3.8.10`.\n", - "\n", - "Python được sử dụng như một công cụ hoàn hảo cho các tác vụ tính toán khoa học, bao gồm phân tích và trực quan hóa các bộ dữ liệu lớn nhờ hệ sinh thái lớn và hoạt động của các gói bên thứ ba như: \n", - "- [NumPy](https://numpy.org/) \n", - "- [Pandas](https://pandas.pydata.org/) \n", - "- [Matplotlib](https://matplotlib.org/), [Seaborn](https://seaborn.pydata.org/)\n", - "- [Scikit-Learn](https://scikit-learn.org/stable/)\n", - "\n", - "### 1.3. Tôn chỉ Python (The Zen of Python)\n", - "\n", - "> 1. Beautiful is better than ugly >> Thà đẹp hơn xấu\n", - "\n", - "> 2. Explicit is better than implicit >> Thà rõ ràng hơn là ngấm ngầm\n", - "\n", - "> 3. Simple is better than complex >> Thà đơn giản hơn phức hợp\n", - "\n", - "> 4. Complex is better than complicated >> Thà phức hợp hơn phức tạp\n", - "\n", - "> 5. Readability counts >> Dễ đọc là 1 điểm cộng\n", - "\n", - "#### Perl\n", - "\n", - "> There is more than one way to do it.\n", - "\n", - "#### Python\n", - "\n", - "> There is more than one way to do it. But, there should be one - and preferable one - obvious way to do it.\n", - "\n", - "Tham khảo [PEP 8](https://www.python.org/dev/peps/pep-0008/)\n", - "\n", - "\n", - "### 1.4. Vì sao nên dùng Python\n", - "\n", - "In ra `Hello, World!` với các ngôn ngữ:\n", - "\n", - "#### C ++\n", - "```C++\n", - "#include \n", - " \n", - "int main()\n", - "{\n", - " std::cout << \"Hello, world!\";\n", - " return 0;\n", - "}\n", - "```\n", - "\n", - "### C#\n", - "```C#\n", - "using System;\n", - "class Program\n", - "{\n", - " public static void Main(string[] args)\n", - " {\n", - " Console.WriteLine(\"Hello, world!\");\n", - " }\n", - "}\n", - "```\n", - "\n", - "### Java\n", - "```Java\n", - "class HelloWorld {\n", - " public static void main(String[] args) {\n", - " System.out.println(\"Hello, World!\"); \n", - " }\n", - "}\n", - "```\n", - "\n", - "### Python\n", - "```Python\n", - "print(\"Hello, World!\");\n", - "```\n", - "\n", - "Nguồn tài liệu:\n", - "- Thư viện để phân tích dữ liệu: Pandas, Numpy, Matplotlib.\n", - "- Python thuần tuý: Euler project, Python Module of the week.\n", - "- Web với Python: Flask.\n", - "- Automate với Python: Sách Automate the boring stuff with Python." - ] - }, - { - "cell_type": "markdown", - "id": "caf8c768", - "metadata": {}, - "source": [ - "Để bắt đầu bài học với Python, chúng ta sẽ cùng nhau thực thi chương trình đầu tiên `print('Hello, world')`. Lưu ý, ta có thể nhấn tổ hợp phím `Shift Return` (MacOS) hoặc `Shift Enter` (Windows, Ubuntu) để thực thi câu lệnh trong cell code." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "4875c459", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, world!\n" - ] - } - ], - "source": [ - "print('Hello, world!')" - ] - }, - { - "cell_type": "markdown", - "id": "cce4f6fc", - "metadata": {}, - "source": [ - "Dòng chú thích (Comments) là các câu giải thích đoạn mã để con người có thể đọc được. Nó cung cấp một số thông tin hoặc giải thích về những gì mỗi phần của một chương trình thực hiện. Dòng chú thích bị trình biên dịch và thông dịch bỏ qua.\n", - "\n", - "Python hỗ trợ chú thích một dòng:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "4f59eea4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, World!\n" - ] - } - ], - "source": [ - "# Print Hello, World! on the screen\n", - "print(\"Hello, World!\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "47d139de", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, World!\n" - ] - } - ], - "source": [ - "print(\"Hello, World!\") # Print Hello, World! on the screen" - ] - }, - { - "cell_type": "markdown", - "id": "0f4232e6", - "metadata": {}, - "source": [ - "Để chú thích nhiều dòng, bạn có thể chèn `'''` hoặc `\"\"\"`cho mỗi dòng:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "dc1bb074", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, World!\n" - ] - } - ], - "source": [ - "'''\n", - "Use print() function\n", - "to print Hello, World! on the screen\n", - "'''\n", - "print(\"Hello, World!\")" - ] - }, - { - "cell_type": "markdown", - "id": "556a436f", - "metadata": {}, - "source": [ - "Ok, bây giờ chúng ta sẽ cũng nhau tìm hiểu sâu hơn về các thành phần cơ bản trong Python nha." - ] - }, - { - "cell_type": "markdown", - "id": "13f5bfa9", - "metadata": {}, - "source": [ - "## 2. Các thành phần cơ bản trong Python" - ] - }, - { - "cell_type": "markdown", - "id": "632d1783", - "metadata": {}, - "source": [ - "### 2.1. Biến (Variables)\n", - "\n", - "Khái niệm biến chắc hẳn không còn xa lạ gì với các bạn từ những ngày cấp 2, cấp 3. Khi ta làm các bài toán đại số, các bạn luôn phải chạm mặt các biến như là biến x, y,... Trong lập trình, biến là tên của một vùng trong bộ nhớ RAM, được sử dụng để lưu trữ thông tin. Khi một biến được khai báo, một vùng trong bộ nhớ sẽ dành cho các biến.\n", - "\n", - "Tại sao chúng ta cần sử dụng biến?" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "9d47da4a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2738087168627369076568435079514204928692" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "5233255324824524012138 * 523209168801482034 # nhân hai số" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "4f2be3ef", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2738087168627369076568435079514208069405" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "2738087168627369076568435079514204928692 + 3140713 # lấy kết quả phép tính trên tiếp tục thực hiện cộng hai số" - ] - }, - { - "cell_type": "markdown", - "id": "4bd456d9", - "metadata": {}, - "source": [ - "Từ ví dụ trên, ta có thể thấy rằng những con số với nhiều chữ số gây khó khăn trong việc sử dụng vì chúng có quá nhiều chữ số, đôi lúc chúng ta cũng có thể vô tình gây sai lệnh giá trị. Để tránh điều này xảy ra, ta có thể nhờ tới sự giúp đỡ của các biến để lưu trữ dữ liệu và lấy ra để tính toán được thuận tiện và chính xác hơn." - ] - }, - { - "cell_type": "markdown", - "id": "47c01844", - "metadata": {}, - "source": [ - "#### 2.1.1 Khởi tạo biến \n", - "\n", - "Để khai báo biến trong Python thì mọi người sử dụng cú pháp `tên_biến = giá_trị`, trong đó:\n", - "- `tên_biến`: tên của biến mà các bạn muốn đặt. \n", - "- `giá_trị` : giá trị của biến mà bạn muốn gán.\n", - "- `=`: phép gán." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "81cb3097", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2738087168627369076568435079514208069405" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = 5233255324824524012138 # gán giá trị 5233255324824524012138 cho biến x\n", - "y = 523209168801482034 # gán giá trị 523209168801482034 cho biến y\n", - "z = 3140713 # gán giá trị 3140713 cho biến z\n", - "x * y + z" - ] - }, - { - "cell_type": "markdown", - "id": "1bb13b48", - "metadata": {}, - "source": [ - "Dễ thấy cùng 1 kết quả tương tự, dùng biến giúp ta dễ dàng tính toán, giảm thiểu tỉ lệ sai lệnh giá trị hơn khi không sử dụng tới biến. Ta có thể khai báo mỗi biến 1 dòng như trên hoặc khai báo chúng trên cùng 1 dòng." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "9d17e025", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2738087168627369076568435079514208069405" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x , y , z = 5233255324824524012138, 523209168801482034, 3140713\n", - "x * y + z" - ] - }, - { - "cell_type": "markdown", - "id": "005681c4", - "metadata": {}, - "source": [ - "Một số lưu ý khi đặt tên biến:\n", - "- Tên của biến có thể bắt đầu với các kí tự hoặc dấu `_` nhưng không được bắt đầu bằng số.\n", - "- Tên biến phân biệt chữ hoa chữ thường: `Num`, `NUM`,`num` là 3 tên biến khác nhau.\n", - "- Tên biến không được trùng với các từ khóa của Python." - ] - }, - { - "cell_type": "markdown", - "id": "7495cda7", - "metadata": {}, - "source": [ - "#### 2.1.2 Các kiểu dữ liệu" - ] - }, - { - "cell_type": "markdown", - "id": "be5ad8a1", - "metadata": {}, - "source": [ - "Với Python, khi ta khai báo một biến thì kiểu dữ liệu của biến sẽ tự động được nhận biết. Vì vậy, ta cũng không phải quá vất vả khi khai báo 1 biến. Mặc định, Python có các kiểu dữ liệu cơ bản sau:\n", - "\n", - "|Kiểu dữ liệu|Ý nghĩa|Ví dụ|\n", - "|-|-|-|\n", - "|`int`|Kiểu số nguyên (không có chứa dấu chấm thập phân)|`11`, `-15`|\n", - "|`float`|Kiểu số thực (có chứa dấu chấm thập phân)|`3.14`, `4.02`|\n", - "|`complex`|Kiểu số phức|`1 + 2j`, `3 - 4j`|\n", - "|`str`|Kiểu chuỗi|`'MCI'`,`'Python'`|\n", - "|`bool`|Kiểu luận lý|`TRUE`, `FALSE`|" - ] - }, - { - "cell_type": "markdown", - "id": "07f3b8f0", - "metadata": {}, - "source": [ - "#### 2.1.3 Kiểm tra kiểu dữ liệu của biến" - ] - }, - { - "cell_type": "markdown", - "id": "40ec694e", - "metadata": {}, - "source": [ - "Để kiểm tra kiểu dữ liệu giá trị của một biến đã khởi tạo, ta sử dụng hàm `type()`\n", - "- Cú pháp: `type(tên_biến)`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "1d4745ff", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "int" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(x) # số nguyên" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "ff77faa8", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "float" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float_number = 3.14 # số thực\n", - "type(float_number)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "15382e5b", - "metadata": {}, - "outputs": [], - "source": [ - "# type = 0 # Không nên gán như thế này" - ] - }, - { - "cell_type": "markdown", - "id": "4fe62d1e", - "metadata": {}, - "source": [ - "#### 2.1.4 Ép kiểu dữ liệu\n", - "Python hỗ trợ chuyển đổi kiểu dữ liệu của một biến x (casting) qua 1 số hàm cơ bản sau:\n", - "\n", - "|Cú pháp| Ý nghĩa|\n", - "|-|-|\n", - "|`float(x)`| chuyển đổi sang kiểu số thực|\n", - "|`int(x)` | chuyển đổi sang kiểu số|\n", - "|`str(x)` | chuyển đổi sang dạng chuỗi|\n", - "|`complex(x)`| chuyển đổi sang kiểu phức hợp|\n", - "|`tuple(x)`| chuyển đổi sang kiểu Tuple|\n", - "|`dict(x)` | chuyển đổi sang kiểu Dictionary|\n", - "|`hex(x)` | chuyển đổi sang hệ 16|\n", - "|`oct(x)` | chuyển đổi sang hệ 8|\n", - "|`chr(x)` | chuyển đổi sang dạng ký tự|" - ] - }, - { - "cell_type": "markdown", - "id": "13f4b114", - "metadata": {}, - "source": [ - "Ví dụ:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "b0f6117a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "3" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = 3.14\n", - "int(x)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "47dbd714", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(3.14+0j)" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "complex(x)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "b7a215d5", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'3.14'" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "str(x)" - ] - }, - { - "cell_type": "markdown", - "id": "8eb52083", - "metadata": {}, - "source": [ - "### 2.2. Toán tử số học" - ] - }, - { - "cell_type": "markdown", - "id": "f9a88b84", - "metadata": {}, - "source": [ - "|Phép toán|Biểu thức|Ý nghĩa |\n", - "|-|-|-|\n", - "|+|`x + y`|Phép cộng|\n", - "|-|`x - y`|Phép trừ|\n", - "|*|`x * y`|Phép nhân|\n", - "|/|`x / y`|Phép chia|\n", - "|%|`x % y`|Phép chia lấy phần dư|\n", - "|//|`x // y`|Phép chia làm tròn xuống|\n", - "|**|`x ** y`|Phép mũ|\n", - "\n", - "Ví dụ:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "b240fb43", - "metadata": {}, - "outputs": [], - "source": [ - "x, y = 11, 3" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "4a92fd90", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " x + y = 14 \n", - " x - y = 8 \n", - " x * y = 33 \n", - " x / y = 3.6666666666666665 \n", - " x % y = 2 \n", - " x // y = 3 \n", - " x ** y = 1331 \n", - " x ** (1/2) = 3.3166247903554\n" - ] - } - ], - "source": [ - "print(' x + y =',x + y, \n", - " '\\n x - y =',x - y,\n", - " '\\n x * y =',x * y,\n", - " '\\n x / y =',x / y ,\n", - " '\\n x % y =',x % y, \n", - " '\\n x // y =',x // y,\n", - " '\\n x ** y = ',x ** y,\n", - " '\\n x ** (1/2) =',x ** (1/2))" - ] - }, - { - "cell_type": "markdown", - "id": "577b7db6", - "metadata": {}, - "source": [ - "Ngoài ra, ta có thể tận dụng thư viện `math` để tính toán" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "b361a151", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "4\n", - "3\n" - ] - } - ], - "source": [ - "import math\n", - "print(math.ceil(3.14)) # làm tròn lên\n", - "print(math.floor(3.14)) # làm tròn xuống" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "929bc290", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "data": { - "text/plain": [ - "4" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "round(3.54)" - ] - }, - { - "cell_type": "markdown", - "id": "82d35614", - "metadata": {}, - "source": [ - "### 2.3. Toán tử luận lý" - ] - }, - { - "cell_type": "markdown", - "id": "20bfbd5f", - "metadata": {}, - "source": [ - "Để thực hiện các phép toán luận lý trong python, ta có thể sử dụng các toán tử luận lý dưới đây:\n", - "\n", - "|Toán tử|Biểu thức|Ý nghĩa|\n", - "|-|-|-|\n", - "|AND|`X and Y`|True nếu cả X và Y đều đúng|\n", - "|OR |`X or Y`|True nếu ít nhất một trong hai vế X hoặc Y đúng|\n", - "|NOT|`not X`|True nếu X sai và False nếu X đúng|\n", - "|XOR|`bool(Y) ^ bool(Y)`|True nếu X và Y khác giá trị và False nếu ngược lại|\n", - "\n", - "Phép toán luận lý sẽ kiểm tra hai vế của toán tử là đúng hay sai và kết hợp chúng lại để đưa ra kết quả. Kết quả của phép toán luận lý là kiểu boolean trong Python với hai giá trị là True (đúng) hoặc False (sai)." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "93abffa3", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "True and False" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "627cb08f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "True or False" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "d92b55c8", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "True and not False or (True and False)" - ] - }, - { - "cell_type": "markdown", - "id": "0800ff83", - "metadata": {}, - "source": [ - "### 2.4. Toán tử điều kiện" - ] - }, - { - "cell_type": "markdown", - "id": "faec71ff", - "metadata": {}, - "source": [ - "|Toán tử| Biểu thức| Ý nghĩa|\n", - "|-|-|-|\n", - "|<| `x < y` |So sánh giá trị x có nhỏ hơn y hay không|\n", - "|>| `x > y `|So sánh giá trị x có lớn hơn y hay không|\n", - "|<=|`x <= y`|So sánh giá trị x có nhỏ hơn hoặc bằng y hay không|\n", - "|>=|`x >= y`|So sánh giá trị x có lớn hơn hoặc bằng y hay không|\n", - "|==|`x == y`|So sánh giá trị x có bằng y hay không|\n", - "|!=|`x != y`|So sánh giá trị x có khác y hay không|\n", - "\n", - "Tương tự toán tử luận lý, kết quả của phép toán điều kiện là kiểu boolean với hai giá trị là True (đúng) hoặc False (sai).\n", - "\n", - "Lưu ý trong Python, dấu `=` sử dụng cho phép gán, dấu `==` sử dụng cho phép so sánh." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "decabdaf", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "3 > 5" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "01eac8a8", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "1 != 2" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "5ec4f580", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(5 == 6) or (9 >= 2) and (8 != 3)" - ] - }, - { - "cell_type": "markdown", - "id": "2b695c88", - "metadata": {}, - "source": [ - "### 2.5. Cấu trúc dữ liệu" - ] - }, - { - "cell_type": "markdown", - "id": "047b0ac5", - "metadata": {}, - "source": [ - "Cấu trúc dữ liệu (data structures) và giải thuật (algorithms) được xem là 2 yếu tố quan trọng nhất trong lập trình, đúng như câu nói nổi tiếng của Niklaus Wirth:\n", - "\n", - "`Programs = Data Structures + Algorithms`\n", - "\n", - "Như tên gọi của mình, cấu trúc dữ liệu cho phép ta tổ chức, sắp xếp, quản lý và lưu trữ dữ liệu sao cho đảm bảo việc truy cập dễ dàng hơn và sửa đổi hiệu quả hơn. Trong thực tế, cấu trúc dữ liệu có thể chia thành 2 nhánh: được định nghĩa sẵn chỉ lôi ra dùng thôi (built-in) và định nghĩa bởi người dùng (user-defined).\n", - "\n", - "![](https://sunway.edu.np/wp-content/uploads/2020/11/Blank-diagram-700x365.png)\n", - "\n", - "Trong nôi dụng bài viết này, chúng ta sẽ đi sâu vào 4 cấu trúc dữ liệu: List, Dictionary, Tuple, và Set.\n", - "\n", - "|Cấu trúc dữ liệu| Cú pháp | Đặc điểm|\n", - "|-|-|-|\n", - "|List|`[]`,`list()`|có index, có thứ tự, có thể thay đổi|\n", - "|Dictionary|`{key:value}`|không trùng lặp|\n", - "|Tuple|`()`,`tuple()`|có index, có thứ tự, không thể thay đổi|\n", - "|Set|`{}`,`set`|không index, không trùng lặp, có thứ tự , có thể thay đổi|" - ] - }, - { - "cell_type": "markdown", - "id": "609190e0", - "metadata": {}, - "source": [ - "#### 2.5.1 List\n", - "\n", - "List là một danh sách các phần tử dữ liệu phân cách bởi dấu phẩy và được bao ngoài bởi dấu ngoặc vuông `[]`, có thể chứa 1 hoặc nhiều kiểu dữ liệu khác nhau từ 1 số cho đến 1 chuỗi được lưu theo 1 cách tuần tự.\n", - "\n", - "##### 2.5.1.0 Khai báo list\n", - "\n", - "- Cú pháp: `[value1, value2, ...]`\n", - "\n", - "Mỗi phần tử trong list đều được gán 1 vị trí, gọi là chỉ số index. Giá trị index bắt đầu từ 0 và tiếp tục cho đến phần tử cuối cùng được gọi là index dương (theo chiều từ trái qua phải). Ngoài ra, ta có thể sử dụng index phủ định bắt đầu từ -1 cho phép bạn truy cập các phần tử từ cuối cùng đến đầu tiên (theo chiều từ phải qua trái). \n" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "31ca5ad9", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 2, 'Python', 3.14]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li = [1 , 2, \"Python\", 3.14] \n", - "# 0 1 2 3 -> index từ trái sang phải\n", - "# -4 -3 -2 -1 -> index từ phải sang trái \n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "f10a79dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "4" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "len(li)" - ] - }, - { - "cell_type": "markdown", - "id": "9e495c4d", - "metadata": {}, - "source": [ - "##### 2.5.1.1 Truy xuất phần tử trong list\n", - "\n", - "Như đề cập ở trên, mỗi phần tử trong List sẽ có một vị trí nhất định tương ứng với một con số index, bắt đầu từ số 0 và tăng dần từ trái qua phải. Chúng ta có thể truy xuất đến các phần tử trong danh sách với cú pháp: `tên_list[index]`" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "08513343", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "dd2a5a6b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[2, 'Python']" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li[1:3]" - ] - }, - { - "cell_type": "markdown", - "id": "f1e451da", - "metadata": {}, - "source": [ - "Mỗi phần tử bên trong list cũng có thể là 1 list con, khi đó mỗi list con cũng có 1 index duy nhất." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "id": "7dafb292", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 2, 3]" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li1 = [1, [1,2,3], 3.14, 'Hn']\n", - "li1[1]" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "05585c65", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li1[1][0]" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "cc327094", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[3.14, 2, 'Python', 3.14]" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li[0] = 3.14\n", - "li" - ] - }, - { - "cell_type": "markdown", - "id": "c03e4524", - "metadata": {}, - "source": [ - "##### 2.5.1.2 Các hàm cơ bản với List" - ] - }, - { - "cell_type": "markdown", - "id": "3984d01f", - "metadata": {}, - "source": [ - "Để biết có những hàm có sẵn nào hỗ trợ cho cấu trúc dữ liệu list, ta sử dụng câu lệnh dưới đây:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "f5c5c66b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['append',\n", - " 'clear',\n", - " 'copy',\n", - " 'count',\n", - " 'extend',\n", - " 'index',\n", - " 'insert',\n", - " 'pop',\n", - " 'remove',\n", - " 'reverse',\n", - " 'sort']" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[method for method in dir(li) if not method.startswith('_')]" - ] - }, - { - "cell_type": "markdown", - "id": "1de94834", - "metadata": {}, - "source": [ - "Các hàm này được chia thành 2 loại:\n", - "- Gán giá trị trực tiếp vào biến và không trả về cái gì\n", - "- Trả về giá trị và cần gán giá trị đó vào 1 biến mới\n", - "\n", - "Dưới đây là 1 số ví dụ về các hàm kể trên:\n", - "\n", - "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", - "|-|-|-|\n", - "|`append()`|Thêm một phần tử vào vị trí cuối cùng trong list|Không|\n", - "|`clear()`|Xóa sạch các phần tử trong list|Không|\n", - "|`copy()`|Sao chép toàn bộ list|Có|\n", - "|`count()`|Đếm số lần một phần tử xuất hiện trong list|Có|\n", - "|`extend()`|Kết hợp list với một list khác|Không|\n", - "|`index()`|Trả về vị trí phần tử trong list|Có|\n", - "|`insert()`|Chèn một phần tử vào list tại vị trí cho trước|Không|\n", - "|`pop()`|Loại bỏ phần tử ở vị trí index cho trước trong list|Không|\n", - "|`remove()`|Loại bỏ một phần tử khỏi list|Không|\n", - "|`reverse()`|Đảo ngược thứ tự các phần tử trong list|Không|\n", - "|`sort()`|Sắp xếp các phần tử trong list|Không|" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "9fa6218b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[3.14, 2, 'Python', 3.14, 10]" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.append(10)\n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "18a1e6c2", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.clear()\n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "d4132307", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 2, 'Hanoi', 2.14, 'c']" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li = [1, 2, 'Hanoi', 2.14, 'c']\n", - "li.copy()" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "9c685bfd", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.count('Hanoi')" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "e93a0028", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 2, 'Hanoi', 2.14, 'c', 1, 2, 3]" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.extend([1,2,3])\n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "id": "01289f8e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "7" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.index(3)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "861c15ca", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 2, 'Hanoi', 2.14, 'c', 1, 2, 3, 0]" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.insert(10,0)\n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "73cb0df4", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 'Hanoi', 2.14, 'c', 1, 2, 3, 0]" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.pop(1)\n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "4eb7e9c0", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 'Hanoi', 2.14, 'c', 1, 2, 3]" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.remove(0)\n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "id": "635ac1ea", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[0, 1, 2, 9]" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li = [1, 9, 2,0]\n", - "li.sort()\n", - "li" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "6e699f1c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[9, 2, 1, 0]" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "li.reverse()\n", - "li" - ] - }, - { - "cell_type": "markdown", - "id": "aefd57aa", - "metadata": {}, - "source": [ - "Đố vui:\n", - "- Hàm `extend()` và hàm `append()` khác gì nhau?\n", - "- Hàm `remove()` and `pop()` khác gì nhau?" - ] - }, - { - "cell_type": "markdown", - "id": "35b06841", - "metadata": {}, - "source": [ - "#### 2.5.2 Tuple\n", - "\n", - "Tuple là 1 cấu trúc dữ liệu gần tương tự như list trong Python, tuy nhiên tuple là 1 danh sách bất biến, không thể thay đổi nội dung. Tuple có tốc độ xử lý nhanh hơn list, do tuple được lưu trữ một khối bộ nhớ xác định còn list thì thường xuyên phải thay đổi không gian lưu trữ. Chính vì vậy, với những dữ liệu dạng hằng số, dữ liệu không thay đổi theo thời gian, tuple là 1 sự lựa chọn hoàn hảo. Ngoài ra, tuple còn được sử dụng làm khóa trong dictionary, một cấu trúc dữ liệu ta sẽ được tìm hiểu trong phần sau.\n", - "\n", - "##### 2.5.2.0 Khai báo tuple\n", - "\n", - "- Cú pháp: `(value1, value2, ...)`" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "id": "9f432b48", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(1, 2, 3.14, 'Vietnam')" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tup = (1, 2, 3.14, 'Vietnam')\n", - "tup" - ] - }, - { - "cell_type": "markdown", - "id": "ff885d8b", - "metadata": {}, - "source": [ - "##### 2.5.2.1 Các hàm cơ bản trong tuple\n", - "\n", - "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", - "|-|-|-|\n", - "|`count()`|Đếm số lần một phần tử xuất hiện trong tuple|Có|\n", - "|`index()`|Trả về vị trí phần tử trong tuple|Có|" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "575a1f99", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['count', 'index']" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[method for method in dir(tup) if not method.startswith('_')]" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "id": "407dd25a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tup.count(3.14)" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "id": "74c141ca", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tup.index(2)" - ] - }, - { - "cell_type": "markdown", - "id": "57a7de3a", - "metadata": {}, - "source": [ - "#### 2.5.3 Set\n", - "\n", - "Set là danh sách không theo thứ tự các phần tử không trùng nhau nằm trong hai dấu ngoặc nhọn `{}` hoặc được khởi tạo bằng `set()`. Set có hỗ trợ các tính toán tổ hợp như là phép toán giao, phép toán hợp, phép đối xứng, phép loại trừ...\n", - "\n", - "##### 2.5.3.0 Khai báo set\n", - "\n", - "- Cú pháp: `{value1, value2, ...}`\n" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "7464c228", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{1, 2, 3}" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "s = {1, 1, 1, 2, 3}\n", - "s" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "id": "ebd94fd0", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{1, 2, 3}" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "s.difference()" - ] - }, - { - "cell_type": "markdown", - "id": "cf1ff04b", - "metadata": {}, - "source": [ - "##### 2.5.3.1 Các hàm cơ bản trong set\n", - "\n", - "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", - "|-|-|-|\n", - "|`add()`|Thêm một phần tử vào vị trí cuối cùng trong set|Không|\n", - "|`clear()`|Xóa sạch các phần tử trong set|Không|\n", - "|`copy()`|Sao chép toàn bộ set|Có|\n", - "|`A.difference(B)`|Phép trừ sẽ trả về một tập hợp các giá trị chỉ có ở set A hoặc set B|Có|\n", - "|`A.difference_update(B)`|Xóa các thành phần tồn tại trong cả hai bộ của set A|Không|\n", - "|`discard()`|Xoá phần tử trong set|Có|\n", - "|`A.intersection(B)`|Phép giao giữa 2 set|Có|\n", - "|`A.intersection_update(B)`|Loại bỏ các thành phần không có trong cả 2 set|Không|\n", - "|`A.isdisjoint(B)`|Trả về True nếu 2 set là tập hợp rời rạc|Có|\n", - "|`A.issubset(B)`|Trả về True nếu tất cả các item của set A đều có trong set B, False nếu ngược lại|Có|\n", - "|`A.issuperset(B)`|Trả về True nếu tất cả các item của set B đều có trong set A, False nếu ngược lại|Có|\n", - "|`pop()`|Loại bỏ phần tử ở vị trí index cho trước|Không|\n", - "|`remove()`|Loại bỏ một phần tử |Không|\n", - "|`A.symmetric_difference(B)`|Phép đối xứng trả về tập hợp những giá trị có trong set A không có trong set B và ngược lại|Có|\n", - "|`A.symmetric_difference_update(B)`|Xóa các thành phần có trong cả hai nhóm và chèn các mục không có trong cả hai nhóm|Có|\n", - "|`A.union(B)`|Phép hợp của 2 set|Có|\n", - "|`update()`|Thêm phần tử|Không|\n", - "\n", - "\n", - "Để có thể nắm được ý nghĩa của các hàm trong set hay bất kì cấu trúc dữ liệu nào, ta có thể tận dụng `docstring` hoặc `help()`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "id": "ee5aaeb8", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['add',\n", - " 'clear',\n", - " 'copy',\n", - " 'difference',\n", - " 'difference_update',\n", - " 'discard',\n", - " 'intersection',\n", - " 'intersection_update',\n", - " 'isdisjoint',\n", - " 'issubset',\n", - " 'issuperset',\n", - " 'pop',\n", - " 'remove',\n", - " 'symmetric_difference',\n", - " 'symmetric_difference_update',\n", - " 'union',\n", - " 'update']" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[method for method in dir(s) if not method.startswith('_')]" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "id": "7452a366", - "metadata": {}, - "outputs": [], - "source": [ - "s = {1, 1, 1, 2, 3}\n", - "s1 = {2,3,4,5}" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "id": "5151dabe", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{1}" - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "s.difference(s1)" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "id": "85828899", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{4, 5}" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "s1.difference(s)" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "id": "6ebafade", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{2, 3}" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "s.intersection(s1)" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "id": "dfc15c60", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{1, 4, 5}" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "s.symmetric_difference(s1)" - ] - }, - { - "cell_type": "markdown", - "id": "d0f03862", - "metadata": {}, - "source": [ - "#### 5.4 Dictionary\n", - "\n", - "##### 5.4.0 Khai báo dictionary\n", - "\n", - "Để khai báo một dictionary chúng ta dùng cặp dấu `{}` theo cú pháp sau:\n", - "- Cú pháp: `{key1:value1, key2:value2,...}`\n", - "\n", - "Lưu ý:\n", - "- Các phần tử đều phải có key.\n", - "- Key chỉ có thể là số hoặc chuỗi.\n", - "- Key phải là duy nhất, nếu không nó sẽ nhận giá trị của phần tử có key được xuất hiện cuối cùng.\n", - "- Key khi đã được khai báo thì không thể đổi được tên.\n", - "- Key có phân biệt hoa thường." - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "id": "443c9948", - "metadata": {}, - "outputs": [], - "source": [ - "my_dict = {}" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "id": "7cae2057", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'language1': 'Java', 'language2': 'Python', 'language3': 'C++'}" - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1 = {'language1':'Java', 'language2':'Python', 'language3':'C++'}\n", - "my_dict1" - ] - }, - { - "cell_type": "markdown", - "id": "8545d94f", - "metadata": {}, - "source": [ - "##### 5.4.1 Truy xuất các phần tử trong dictionary" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "id": "196b5959", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Java'" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1['language1']" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "id": "b100719b", - "metadata": {}, - "outputs": [], - "source": [ - "my_dict1['language4'] = 'C' # dict_name[key] = value" - ] - }, - { - "cell_type": "markdown", - "id": "13101e15", - "metadata": {}, - "source": [ - "Dictionary có thể lồng trong dictionary" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "id": "695d8160", - "metadata": {}, - "outputs": [], - "source": [ - "my_dict2 = {'name':'Zootpi',\n", - " 'info':{'age':1,\n", - " 'sex':'unknown'}}" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "id": "b96f582c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'unknown'" - ] - }, - "execution_count": 61, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict2['info']['sex']" - ] - }, - { - "cell_type": "markdown", - "id": "e7d25ef3", - "metadata": {}, - "source": [ - "##### 2.5.4.2 Các hàm cơ bản trong dictionary\n", - "\n", - "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", - "|-|-|-|\n", - "|`clear()`|Xóa sạch các phần tử|Không|\n", - "|`copy()`|Sao chép các phần tử|Có|\n", - "|`fromkeys()`|Tạo ra một dictionary với các khóa và giá trị được chỉ định|Có|\n", - "|`get()`|Trả về giá trị của khóa được chỉ định|Có|\n", - "|`items()`| Trả về danh sách chứa bộ giá trị cho mỗi cặp giá trị khóa|Có|\n", - "|`keys()`|Trả về danh sách chứa các key của dictionary|Có|\n", - "|`pop()`|Loại bỏ phần tử có key được chỉ định|Không|\n", - "|`popitem()`|Xóa cặp key-value được chèn cuối cùng|Không|\n", - "|`setdefault()`|Trả về giá trị của key được chỉ định. Nếu key không tồn tại: hãy chèn key, với giá trị được chỉ định|Không|\n", - "|`update()`|Cập nhật dictionary với các cặp key-value được chỉ định|Không|\n", - "|`values()`| Trả về danh sách tất cả các value trong từ điển |Có|" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "id": "3206e9c4", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['clear',\n", - " 'copy',\n", - " 'fromkeys',\n", - " 'get',\n", - " 'items',\n", - " 'keys',\n", - " 'pop',\n", - " 'popitem',\n", - " 'setdefault',\n", - " 'update',\n", - " 'values']" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[method for method in dir(my_dict1) if not method.startswith('_')]" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "id": "63251530", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'key1': 0, 'key2': 0, 'key3': 0}" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = ('key1', 'key2', 'key3')\n", - "y = 0\n", - "dict.fromkeys(x, y)" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "id": "e5376641", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Python'" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1.get('language2')" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "id": "65fb8de1", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "dict_items([('language1', 'Java'), ('language2', 'Python'), ('language3', 'C++'), ('language4', 'C')])" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1.items()" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "id": "f6260ef0", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "dict_keys(['language1', 'language2', 'language3', 'language4'])" - ] - }, - "execution_count": 66, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1.keys()" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "id": "f36943c0", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'C'" - ] - }, - "execution_count": 67, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1.pop('language4')" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "id": "5e74a0b4", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'language1': 'Java', 'language2': 'Python', 'language3': 'C++'}" - ] - }, - "execution_count": 68, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "id": "5b24a8a8", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('language3', 'C++')" - ] - }, - "execution_count": 69, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1.popitem()" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "id": "d79541e5", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'language1': 'Java', 'language2': 'Python'}" - ] - }, - "execution_count": 70, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "id": "5ed142be", - "metadata": {}, - "outputs": [], - "source": [ - "my_dict1.setdefault('language3')" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "id": "f7df8462", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'language1': 'Java', 'language2': 'Python', 'language3': None}" - ] - }, - "execution_count": 72, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "id": "e0824b22", - "metadata": {}, - "outputs": [], - "source": [ - "my_dict1.update({'language3':'Ruby'})" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "id": "15573f7b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'language1': 'Java', 'language2': 'Python', 'language3': 'Ruby'}" - ] - }, - "execution_count": 74, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_dict1" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "id": "eb4e4962", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "dict_values(['Java', 'Python', 'Ruby'])" - ] - }, - "execution_count": 75, - "metadata": {}, - "output_type": "execute_result" + "cells": [ + { + "cell_type": "markdown", + "id": "59c8a7dc", + "metadata": {}, + "source": [ + "# Bài 2: Giới thiệu về PYTHON\n", + "\n", + "## 🎯 Mục tiêu học tập\n", + "\n", + "Sau khi hoàn thành bài này, bạn sẽ có thể:\n", + "- [ ] Hiểu được Python là gì và các đặc điểm của nó\n", + "- [ ] Nắm vững các khái niệm cơ bản về ngôn ngữ lập trình\n", + "- [ ] Hiểu được tôn chỉ Python (The Zen of Python)\n", + "- [ ] Sử dụng các cấu trúc cơ bản trong Python (biến, kiểu dữ liệu, cấu trúc điều khiển) " + ] + }, + { + "cell_type": "markdown", + "id": "b3672a13", + "metadata": {}, + "source": [ + "\n", + "![](https://laptrinhcanban.com/python/nhap-mon-lap-trinh-python/gioi-thieu-python/python-la-gi/Python.jpg)" + ] + }, + { + "cell_type": "markdown", + "id": "2012c7f7", + "metadata": {}, + "source": [ + "## 1. Giới thiệu\n", + "\n", + "### 1.1. Tổng quan\n", + "\n", + "Theo thống kê của Wiki, tính đến đến tháng 10/2010, trên thế giới có khoảng hơn 700 ngôn ngữ lập trình (xem danh sách các ngôn ngữ lập trình tại [đây](https://vi.wikipedia.org/wiki/Danh_s%C3%A1ch_c%C3%A1c_ng%C3%B4n_ng%E1%BB%AF_l%E1%BA%ADp_tr%C3%ACnh)). Theo thống kê của [FOLDOC](http://foldoc.org/), 1 trang từ điển tin học trực tuyến miễn phí, con số này đã lên 1000 vào tháng 9/2020, 1 con số đáng kinh ngạc.\n", + "\n", + "![Độ phổ biến của 1 số ngôn ngữ lập trình được sử dụng trên các kho lưu trữ trên GitHub.](./img/python.png)\n", + "> *Độ phổ biến của 1 số ngôn ngữ lập trình được sử dụng trên các kho lưu trữ trên GitHub tính đến tháng 4/2014 theo [Githut](https://githut.info/).*\n", + "\n", + "Cũng giống như ngôn ngữ nói, ngôn ngữ lập trình cũng tuân theo 1 hệ thống phân cấp bao gồm 2 loại chính.\n", + "\n", + "- ***Ngôn ngữ bậc thấp***: Cú pháp cực kỳ gần với ngôn ngữ máy nên rất khó sử dụng, tuy nhiên lại cung cấp nhiều chức năng hơn và cho phép ta tạo ra chương trình chi tiết và hiệu quả hơn rất nhiều, thích hợp để viết các chương trình liên quan đến kiến trúc và phần cứng của máy tính. Các ngôn ngữ này có thể được chia thành hai loại: ngôn ngữ máy và hợp ngữ.\n", + "\n", + "- ***Ngôn ngữ bậc cao***: Cú pháp gần gũi với ngôn ngữ của con người hơn, tương tự như ngôn ngữ tiếng Anh nên dễ hiểu và thân thiện với người dùng hơn, xử lý lỗi nhanh hơn, thích hợp để phát triển phần mềm, xử lý dữ liệu... Một số ngôn ngữ bậc cao nổi tiếng nhất có thể kể đến C, C ++, Java và Python. \n", + "\n", + "### 1.2. Ngôn ngữ lập trình Python\n", + "\n", + "Một số điểm nổi bật:\n", + "- Được phát triển từ 1980s và chính thức phát hành vào năm 1991 bởi `Guido van Rossum`.\n", + "- Là ngôn ngữ lập trình `bậc cao` - `high level`: người dùng lập trình bằng ngôn ngữ dễ hiểu và dễ sử dụng, sau đó máy tính sẽ chuyển các đoạn mã thành ngôn ngữ máy tính.\n", + "- Là ngôn ngữ `thông dịch`-`interpreted`: code được viết ra sẽ được chuyển thể trực tiếp thành ngôn ngữ máy tính và chạy trực tiếp cùng 1 lúc. Điều này khiến việc sử dụng Python dễ dàng hơn so với các ngôn ngữ `biên dịch`-`compiled` - code được chuyển thể thành ngôn ngữ máy tính trước rồi mới có thể chạy.\n", + "- Là ngôn ngữ `đa năng`-`generally purposed`: có thể sử dụng cho nhiều mục đích khác nhau.\n", + "- Là ngôn ngữ `dynamic type`: không cần khai báo trước kiểu dữ liệu của biến, có thể ghi đè trực tiếp kiểu dữ liệu của biến.\n", + "- Là ngôn ngữ `tuần tự`-`procedural` đồng thời `hướng đối tượng`-`object-oriented`.\n", + "- Phiên bản ổn định hiện tại là phiên bản `3.12+` (khuyến nghị sử dụng Python 3.10 hoặc 3.11 để đảm bảo tương thích tốt với các thư viện).\n", + "\n", + "Python được sử dụng như một công cụ hoàn hảo cho các tác vụ tính toán khoa học, bao gồm phân tích và trực quan hóa các bộ dữ liệu lớn nhờ hệ sinh thái lớn và hoạt động của các gói bên thứ ba như: \n", + "- [NumPy](https://numpy.org/) \n", + "- [Pandas](https://pandas.pydata.org/) \n", + "- [Matplotlib](https://matplotlib.org/), [Seaborn](https://seaborn.pydata.org/)\n", + "- [Scikit-Learn](https://scikit-learn.org/stable/)\n", + "\n", + "### 1.3. Tôn chỉ Python (The Zen of Python)\n", + "\n", + "> 1. Beautiful is better than ugly >> Thà đẹp hơn xấu\n", + "\n", + "> 2. Explicit is better than implicit >> Thà rõ ràng hơn là ngấm ngầm\n", + "\n", + "> 3. Simple is better than complex >> Thà đơn giản hơn phức hợp\n", + "\n", + "> 4. Complex is better than complicated >> Thà phức hợp hơn phức tạp\n", + "\n", + "> 5. Readability counts >> Dễ đọc là 1 điểm cộng\n", + "\n", + "#### Perl\n", + "\n", + "> There is more than one way to do it.\n", + "\n", + "#### Python\n", + "\n", + "> There is more than one way to do it. But, there should be one - and preferable one - obvious way to do it.\n", + "\n", + "Tham khảo [PEP 8](https://www.python.org/dev/peps/pep-0008/)\n", + "\n", + "\n", + "### 1.4. Vì sao nên dùng Python\n", + "\n", + "In ra `Hello, World!` với các ngôn ngữ:\n", + "\n", + "#### C ++\n", + "```C++\n", + "#include \n", + " \n", + "int main()\n", + "{\n", + " std::cout << \"Hello, world!\";\n", + " return 0;\n", + "}\n", + "```\n", + "\n", + "### C#\n", + "```C#\n", + "using System;\n", + "class Program\n", + "{\n", + " public static void Main(string[] args)\n", + " {\n", + " Console.WriteLine(\"Hello, world!\");\n", + " }\n", + "}\n", + "```\n", + "\n", + "### Java\n", + "```Java\n", + "class HelloWorld {\n", + " public static void main(String[] args) {\n", + " System.out.println(\"Hello, World!\"); \n", + " }\n", + "}\n", + "```\n", + "\n", + "### Python\n", + "```Python\n", + "print(\"Hello, World!\");\n", + "```\n", + "\n", + "Nguồn tài liệu:\n", + "- Thư viện để phân tích dữ liệu: Pandas, Numpy, Matplotlib.\n", + "- Python thuần tuý: Euler project, Python Module of the week.\n", + "- Web với Python: Flask.\n", + "- Automate với Python: Sách Automate the boring stuff with Python." + ] + }, + { + "cell_type": "markdown", + "id": "caf8c768", + "metadata": {}, + "source": [ + "Để bắt đầu bài học với Python, chúng ta sẽ cùng nhau thực thi chương trình đầu tiên `print('Hello, world')`. Lưu ý, ta có thể nhấn tổ hợp phím `Shift Return` (MacOS) hoặc `Shift Enter` (Windows, Ubuntu) để thực thi câu lệnh trong cell code." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "4875c459", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, world!\n" + ] + } + ], + "source": [ + "print('Hello, world!')" + ] + }, + { + "cell_type": "markdown", + "id": "cce4f6fc", + "metadata": {}, + "source": [ + "Dòng chú thích (Comments) là các câu giải thích đoạn mã để con người có thể đọc được. Nó cung cấp một số thông tin hoặc giải thích về những gì mỗi phần của một chương trình thực hiện. Dòng chú thích bị trình biên dịch và thông dịch bỏ qua.\n", + "\n", + "Python hỗ trợ chú thích một dòng:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4f59eea4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n" + ] + } + ], + "source": [ + "# Print Hello, World! on the screen\n", + "print(\"Hello, World!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "47d139de", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n" + ] + } + ], + "source": [ + "print(\"Hello, World!\") # Print Hello, World! on the screen" + ] + }, + { + "cell_type": "markdown", + "id": "0f4232e6", + "metadata": {}, + "source": [ + "Để chú thích nhiều dòng, bạn có thể chèn `'''` hoặc `\"\"\"`cho mỗi dòng:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "dc1bb074", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n" + ] + } + ], + "source": [ + "'''\n", + "Use print() function\n", + "to print Hello, World! on the screen\n", + "'''\n", + "print(\"Hello, World!\")" + ] + }, + { + "cell_type": "markdown", + "id": "556a436f", + "metadata": {}, + "source": [ + "Ok, bây giờ chúng ta sẽ cùng nhau tìm hiểu sâu hơn về các thành phần cơ bản trong Python nha." + ] + }, + { + "cell_type": "markdown", + "id": "13f5bfa9", + "metadata": {}, + "source": [ + "## 2. Các thành phần cơ bản trong Python" + ] + }, + { + "cell_type": "markdown", + "id": "632d1783", + "metadata": {}, + "source": [ + "### 2.1. Biến (Variables)\n", + "\n", + "Khái niệm biến chắc hẳn không còn xa lạ gì với các bạn từ những ngày cấp 2, cấp 3. Khi ta làm các bài toán đại số, các bạn luôn phải chạm mặt các biến như là biến x, y,... Trong lập trình, biến là tên của một vùng trong bộ nhớ RAM, được sử dụng để lưu trữ thông tin. Khi một biến được khai báo, một vùng trong bộ nhớ sẽ dành cho các biến.\n", + "\n", + "Tại sao chúng ta cần sử dụng biến?" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9d47da4a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2738087168627369076568435079514204928692" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5233255324824524012138 * 523209168801482034 # nhân hai số" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "4f2be3ef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2738087168627369076568435079514208069405" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2738087168627369076568435079514204928692 + 3140713 # lấy kết quả phép tính trên tiếp tục thực hiện cộng hai số" + ] + }, + { + "cell_type": "markdown", + "id": "4bd456d9", + "metadata": {}, + "source": [ + "Từ ví dụ trên, ta có thể thấy rằng những con số với nhiều chữ số gây khó khăn trong việc sử dụng vì chúng có quá nhiều chữ số, đôi lúc chúng ta cũng có thể vô tình gây sai lệnh giá trị. Để tránh điều này xảy ra, ta có thể nhờ tới sự giúp đỡ của các biến để lưu trữ dữ liệu và lấy ra để tính toán được thuận tiện và chính xác hơn." + ] + }, + { + "cell_type": "markdown", + "id": "47c01844", + "metadata": {}, + "source": [ + "#### 2.1.1 Khởi tạo biến \n", + "\n", + "Để khai báo biến trong Python thì mọi người sử dụng cú pháp `tên_biến = giá_trị`, trong đó:\n", + "- `tên_biến`: tên của biến mà các bạn muốn đặt. \n", + "- `giá_trị` : giá trị của biến mà bạn muốn gán.\n", + "- `=`: phép gán." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "81cb3097", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2738087168627369076568435079514208069405" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = 5233255324824524012138 # gán giá trị 5233255324824524012138 cho biến x\n", + "y = 523209168801482034 # gán giá trị 523209168801482034 cho biến y\n", + "z = 3140713 # gán giá trị 3140713 cho biến z\n", + "x * y + z" + ] + }, + { + "cell_type": "markdown", + "id": "1bb13b48", + "metadata": {}, + "source": [ + "Dễ thấy cùng 1 kết quả tương tự, dùng biến giúp ta dễ dàng tính toán, giảm thiểu tỉ lệ sai lệnh giá trị hơn khi không sử dụng tới biến. Ta có thể khai báo mỗi biến 1 dòng như trên hoặc khai báo chúng trên cùng 1 dòng." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "9d17e025", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2738087168627369076568435079514208069405" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x , y , z = 5233255324824524012138, 523209168801482034, 3140713\n", + "x * y + z" + ] + }, + { + "cell_type": "markdown", + "id": "005681c4", + "metadata": {}, + "source": [ + "Một số lưu ý khi đặt tên biến:\n", + "- Tên của biến có thể bắt đầu với các kí tự hoặc dấu `_` nhưng không được bắt đầu bằng số.\n", + "- Tên biến phân biệt chữ hoa chữ thường: `Num`, `NUM`,`num` là 3 tên biến khác nhau.\n", + "- Tên biến không được trùng với các từ khóa của Python." + ] + }, + { + "cell_type": "markdown", + "id": "7495cda7", + "metadata": {}, + "source": [ + "#### 2.1.2 Các kiểu dữ liệu" + ] + }, + { + "cell_type": "markdown", + "id": "be5ad8a1", + "metadata": {}, + "source": [ + "Với Python, khi ta khai báo một biến thì kiểu dữ liệu của biến sẽ tự động được nhận biết. Vì vậy, ta cũng không phải quá vất vả khi khai báo 1 biến. Mặc định, Python có các kiểu dữ liệu cơ bản sau:\n", + "\n", + "|Kiểu dữ liệu|Ý nghĩa|Ví dụ|\n", + "|-|-|-|\n", + "|`int`|Kiểu số nguyên (không có chứa dấu chấm thập phân)|`11`, `-15`|\n", + "|`float`|Kiểu số thực (có chứa dấu chấm thập phân)|`3.14`, `4.02`|\n", + "|`complex`|Kiểu số phức|`1 + 2j`, `3 - 4j`|\n", + "|`str`|Kiểu chuỗi|`'MCI'`,`'Python'`|\n", + "|`bool`|Kiểu luận lý|`TRUE`, `FALSE`|" + ] + }, + { + "cell_type": "markdown", + "id": "07f3b8f0", + "metadata": {}, + "source": [ + "#### 2.1.3 Kiểm tra kiểu dữ liệu của biến" + ] + }, + { + "cell_type": "markdown", + "id": "40ec694e", + "metadata": {}, + "source": [ + "Để kiểm tra kiểu dữ liệu giá trị của một biến đã khởi tạo, ta sử dụng hàm `type()`\n", + "- Cú pháp: `type(tên_biến)`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1d4745ff", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(x) # số nguyên" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ff77faa8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float_number = 3.14 # số thực\n", + "type(float_number)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "15382e5b", + "metadata": {}, + "outputs": [], + "source": [ + "# type = 0 # Không nên gán như thế này" + ] + }, + { + "cell_type": "markdown", + "id": "4fe62d1e", + "metadata": {}, + "source": [ + "#### 2.1.4 Ép kiểu dữ liệu\n", + "Python hỗ trợ chuyển đổi kiểu dữ liệu của một biến x (casting) qua 1 số hàm cơ bản sau:\n", + "\n", + "|Cú pháp| Ý nghĩa|\n", + "|-|-|\n", + "|`float(x)`| chuyển đổi sang kiểu số thực|\n", + "|`int(x)` | chuyển đổi sang kiểu số|\n", + "|`str(x)` | chuyển đổi sang dạng chuỗi|\n", + "|`complex(x)`| chuyển đổi sang kiểu phức hợp|\n", + "|`tuple(x)`| chuyển đổi sang kiểu Tuple|\n", + "|`dict(x)` | chuyển đổi sang kiểu Dictionary|\n", + "|`hex(x)` | chuyển đổi sang hệ 16|\n", + "|`oct(x)` | chuyển đổi sang hệ 8|\n", + "|`chr(x)` | chuyển đổi sang dạng ký tự|" + ] + }, + { + "cell_type": "markdown", + "id": "13f4b114", + "metadata": {}, + "source": [ + "Ví dụ:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "b0f6117a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = 3.14\n", + "int(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "47dbd714", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(3.14+0j)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b7a215d5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'3.14'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(x)" + ] + }, + { + "cell_type": "markdown", + "id": "8eb52083", + "metadata": {}, + "source": [ + "### 2.2. Toán tử số học" + ] + }, + { + "cell_type": "markdown", + "id": "f9a88b84", + "metadata": {}, + "source": [ + "|Phép toán|Biểu thức|Ý nghĩa |\n", + "|-|-|-|\n", + "|+|`x + y`|Phép cộng|\n", + "|-|`x - y`|Phép trừ|\n", + "|*|`x * y`|Phép nhân|\n", + "|/|`x / y`|Phép chia|\n", + "|%|`x % y`|Phép chia lấy phần dư|\n", + "|//|`x // y`|Phép chia làm tròn xuống|\n", + "|**|`x ** y`|Phép mũ|\n", + "\n", + "Ví dụ:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "b240fb43", + "metadata": {}, + "outputs": [], + "source": [ + "x, y = 11, 3" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "4a92fd90", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " x + y = 14 \n", + " x - y = 8 \n", + " x * y = 33 \n", + " x / y = 3.6666666666666665 \n", + " x % y = 2 \n", + " x // y = 3 \n", + " x ** y = 1331 \n", + " x ** (1/2) = 3.3166247903554\n" + ] + } + ], + "source": [ + "print(' x + y =',x + y, \n", + " '\\n x - y =',x - y,\n", + " '\\n x * y =',x * y,\n", + " '\\n x / y =',x / y ,\n", + " '\\n x % y =',x % y, \n", + " '\\n x // y =',x // y,\n", + " '\\n x ** y = ',x ** y,\n", + " '\\n x ** (1/2) =',x ** (1/2))" + ] + }, + { + "cell_type": "markdown", + "id": "577b7db6", + "metadata": {}, + "source": [ + "Ngoài ra, ta có thể tận dụng thư viện `math` để tính toán" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b361a151", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "3\n" + ] + } + ], + "source": [ + "import math\n", + "print(math.ceil(3.14)) # làm tròn lên\n", + "print(math.floor(3.14)) # làm tròn xuống" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "929bc290", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(3.54)" + ] + }, + { + "cell_type": "markdown", + "id": "82d35614", + "metadata": {}, + "source": [ + "### 2.3. Toán tử luận lý" + ] + }, + { + "cell_type": "markdown", + "id": "20bfbd5f", + "metadata": {}, + "source": [ + "Để thực hiện các phép toán luận lý trong python, ta có thể sử dụng các toán tử luận lý dưới đây:\n", + "\n", + "|Toán tử|Biểu thức|Ý nghĩa|\n", + "|-|-|-|\n", + "|AND|`X and Y`|True nếu cả X và Y đều đúng|\n", + "|OR |`X or Y`|True nếu ít nhất một trong hai vế X hoặc Y đúng|\n", + "|NOT|`not X`|True nếu X sai và False nếu X đúng|\n", + "|XOR|`bool(Y) ^ bool(Y)`|True nếu X và Y khác giá trị và False nếu ngược lại|\n", + "\n", + "Phép toán luận lý sẽ kiểm tra hai vế của toán tử là đúng hay sai và kết hợp chúng lại để đưa ra kết quả. Kết quả của phép toán luận lý là kiểu boolean trong Python với hai giá trị là True (đúng) hoặc False (sai)." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "93abffa3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "True and False" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "627cb08f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "True or False" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "d92b55c8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "True and not False or (True and False)" + ] + }, + { + "cell_type": "markdown", + "id": "0800ff83", + "metadata": {}, + "source": [ + "### 2.4. Toán tử điều kiện" + ] + }, + { + "cell_type": "markdown", + "id": "faec71ff", + "metadata": {}, + "source": [ + "|Toán tử| Biểu thức| Ý nghĩa|\n", + "|-|-|-|\n", + "|<| `x < y` |So sánh giá trị x có nhỏ hơn y hay không|\n", + "|>| `x > y `|So sánh giá trị x có lớn hơn y hay không|\n", + "|<=|`x <= y`|So sánh giá trị x có nhỏ hơn hoặc bằng y hay không|\n", + "|>=|`x >= y`|So sánh giá trị x có lớn hơn hoặc bằng y hay không|\n", + "|==|`x == y`|So sánh giá trị x có bằng y hay không|\n", + "|!=|`x != y`|So sánh giá trị x có khác y hay không|\n", + "\n", + "Tương tự toán tử luận lý, kết quả của phép toán điều kiện là kiểu boolean với hai giá trị là True (đúng) hoặc False (sai).\n", + "\n", + "Lưu ý trong Python, dấu `=` sử dụng cho phép gán, dấu `==` sử dụng cho phép so sánh." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "decabdaf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 > 5" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "01eac8a8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 != 2" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "5ec4f580", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(5 == 6) or (9 >= 2) and (8 != 3)" + ] + }, + { + "cell_type": "markdown", + "id": "2b695c88", + "metadata": {}, + "source": [ + "### 2.5. Cấu trúc dữ liệu" + ] + }, + { + "cell_type": "markdown", + "id": "047b0ac5", + "metadata": {}, + "source": [ + "Cấu trúc dữ liệu (data structures) và giải thuật (algorithms) được xem là 2 yếu tố quan trọng nhất trong lập trình, đúng như câu nói nổi tiếng của Niklaus Wirth:\n", + "\n", + "`Programs = Data Structures + Algorithms`\n", + "\n", + "Như tên gọi của mình, cấu trúc dữ liệu cho phép ta tổ chức, sắp xếp, quản lý và lưu trữ dữ liệu sao cho đảm bảo việc truy cập dễ dàng hơn và sửa đổi hiệu quả hơn. Trong thực tế, cấu trúc dữ liệu có thể chia thành 2 nhánh: được định nghĩa sẵn chỉ lôi ra dùng thôi (built-in) và định nghĩa bởi người dùng (user-defined).\n", + "\n", + "![](https://sunway.edu.np/wp-content/uploads/2020/11/Blank-diagram-700x365.png)\n", + "\n", + "Trong nôi dụng bài viết này, chúng ta sẽ đi sâu vào 4 cấu trúc dữ liệu: List, Dictionary, Tuple, và Set.\n", + "\n", + "|Cấu trúc dữ liệu| Cú pháp | Đặc điểm|\n", + "|-|-|-|\n", + "|List|`[]`,`list()`|có index, có thứ tự, có thể thay đổi|\n", + "|Dictionary|`{key:value}`|không trùng lặp|\n", + "|Tuple|`()`,`tuple()`|có index, có thứ tự, không thể thay đổi|\n", + "|Set|`{}`,`set`|không index, không trùng lặp, có thứ tự , có thể thay đổi|" + ] + }, + { + "cell_type": "markdown", + "id": "609190e0", + "metadata": {}, + "source": [ + "#### 2.5.1 List\n", + "\n", + "List là một danh sách các phần tử dữ liệu phân cách bởi dấu phẩy và được bao ngoài bởi dấu ngoặc vuông `[]`, có thể chứa 1 hoặc nhiều kiểu dữ liệu khác nhau từ 1 số cho đến 1 chuỗi được lưu theo 1 cách tuần tự.\n", + "\n", + "##### 2.5.1.0 Khai báo list\n", + "\n", + "- Cú pháp: `[value1, value2, ...]`\n", + "\n", + "Mỗi phần tử trong list đều được gán 1 vị trí, gọi là chỉ số index. Giá trị index bắt đầu từ 0 và tiếp tục cho đến phần tử cuối cùng được gọi là index dương (theo chiều từ trái qua phải). Ngoài ra, ta có thể sử dụng index phủ định bắt đầu từ -1 cho phép bạn truy cập các phần tử từ cuối cùng đến đầu tiên (theo chiều từ phải qua trái). \n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "31ca5ad9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 'Python', 3.14]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li = [1 , 2, \"Python\", 3.14] \n", + "# 0 1 2 3 -> index từ trái sang phải\n", + "# -4 -3 -2 -1 -> index từ phải sang trái \n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "f10a79dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(li)" + ] + }, + { + "cell_type": "markdown", + "id": "9e495c4d", + "metadata": {}, + "source": [ + "##### 2.5.1.1 Truy xuất phần tử trong list\n", + "\n", + "Như đề cập ở trên, mỗi phần tử trong List sẽ có một vị trí nhất định tương ứng với một con số index, bắt đầu từ số 0 và tăng dần từ trái qua phải. Chúng ta có thể truy xuất đến các phần tử trong danh sách với cú pháp: `tên_list[index]`" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "08513343", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "dd2a5a6b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 'Python']" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li[1:3]" + ] + }, + { + "cell_type": "markdown", + "id": "f1e451da", + "metadata": {}, + "source": [ + "Mỗi phần tử bên trong list cũng có thể là 1 list con, khi đó mỗi list con cũng có 1 index duy nhất." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "7dafb292", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li1 = [1, [1,2,3], 3.14, 'Hn']\n", + "li1[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "05585c65", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li1[1][0]" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "cc327094", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[3.14, 2, 'Python', 3.14]" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li[0] = 3.14\n", + "li" + ] + }, + { + "cell_type": "markdown", + "id": "c03e4524", + "metadata": {}, + "source": [ + "##### 2.5.1.2 Các hàm cơ bản với List" + ] + }, + { + "cell_type": "markdown", + "id": "3984d01f", + "metadata": {}, + "source": [ + "Để biết có những hàm có sẵn nào hỗ trợ cho cấu trúc dữ liệu list, ta sử dụng câu lệnh dưới đây:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "f5c5c66b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['append',\n", + " 'clear',\n", + " 'copy',\n", + " 'count',\n", + " 'extend',\n", + " 'index',\n", + " 'insert',\n", + " 'pop',\n", + " 'remove',\n", + " 'reverse',\n", + " 'sort']" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[method for method in dir(li) if not method.startswith('_')]" + ] + }, + { + "cell_type": "markdown", + "id": "1de94834", + "metadata": {}, + "source": [ + "Các hàm này được chia thành 2 loại:\n", + "- Gán giá trị trực tiếp vào biến và không trả về cái gì\n", + "- Trả về giá trị và cần gán giá trị đó vào 1 biến mới\n", + "\n", + "Dưới đây là 1 số ví dụ về các hàm kể trên:\n", + "\n", + "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", + "|-|-|-|\n", + "|`append()`|Thêm một phần tử vào vị trí cuối cùng trong list|Không|\n", + "|`clear()`|Xóa sạch các phần tử trong list|Không|\n", + "|`copy()`|Sao chép toàn bộ list|Có|\n", + "|`count()`|Đếm số lần một phần tử xuất hiện trong list|Có|\n", + "|`extend()`|Kết hợp list với một list khác|Không|\n", + "|`index()`|Trả về vị trí phần tử trong list|Có|\n", + "|`insert()`|Chèn một phần tử vào list tại vị trí cho trước|Không|\n", + "|`pop()`|Loại bỏ phần tử ở vị trí index cho trước trong list|Không|\n", + "|`remove()`|Loại bỏ một phần tử khỏi list|Không|\n", + "|`reverse()`|Đảo ngược thứ tự các phần tử trong list|Không|\n", + "|`sort()`|Sắp xếp các phần tử trong list|Không|" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "9fa6218b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[3.14, 2, 'Python', 3.14, 10]" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.append(10)\n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "18a1e6c2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.clear()\n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "d4132307", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 'Hanoi', 2.14, 'c']" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li = [1, 2, 'Hanoi', 2.14, 'c']\n", + "li.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "9c685bfd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.count('Hanoi')" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "e93a0028", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 'Hanoi', 2.14, 'c', 1, 2, 3]" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.extend([1,2,3])\n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "01289f8e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.index(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "861c15ca", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 'Hanoi', 2.14, 'c', 1, 2, 3, 0]" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.insert(10,0)\n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "73cb0df4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 'Hanoi', 2.14, 'c', 1, 2, 3, 0]" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.pop(1)\n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "4eb7e9c0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 'Hanoi', 2.14, 'c', 1, 2, 3]" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.remove(0)\n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "635ac1ea", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 1, 2, 9]" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li = [1, 9, 2,0]\n", + "li.sort()\n", + "li" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "6e699f1c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[9, 2, 1, 0]" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "li.reverse()\n", + "li" + ] + }, + { + "cell_type": "markdown", + "id": "aefd57aa", + "metadata": {}, + "source": [ + "Đố vui:\n", + "- Hàm `extend()` và hàm `append()` khác gì nhau?\n", + "- Hàm `remove()` and `pop()` khác gì nhau?" + ] + }, + { + "cell_type": "markdown", + "id": "35b06841", + "metadata": {}, + "source": [ + "#### 2.5.2 Tuple\n", + "\n", + "Tuple là 1 cấu trúc dữ liệu gần tương tự như list trong Python, tuy nhiên tuple là 1 danh sách bất biến, không thể thay đổi nội dung. Tuple có tốc độ xử lý nhanh hơn list, do tuple được lưu trữ một khối bộ nhớ xác định còn list thì thường xuyên phải thay đổi không gian lưu trữ. Chính vì vậy, với những dữ liệu dạng hằng số, dữ liệu không thay đổi theo thời gian, tuple là 1 sự lựa chọn hoàn hảo. Ngoài ra, tuple còn được sử dụng làm khóa trong dictionary, một cấu trúc dữ liệu ta sẽ được tìm hiểu trong phần sau.\n", + "\n", + "##### 2.5.2.0 Khai báo tuple\n", + "\n", + "- Cú pháp: `(value1, value2, ...)`" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "9f432b48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 2, 3.14, 'Vietnam')" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tup = (1, 2, 3.14, 'Vietnam')\n", + "tup" + ] + }, + { + "cell_type": "markdown", + "id": "ff885d8b", + "metadata": {}, + "source": [ + "##### 2.5.2.1 Các hàm cơ bản trong tuple\n", + "\n", + "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", + "|-|-|-|\n", + "|`count()`|Đếm số lần một phần tử xuất hiện trong tuple|Có|\n", + "|`index()`|Trả về vị trí phần tử trong tuple|Có|" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "575a1f99", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['count', 'index']" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[method for method in dir(tup) if not method.startswith('_')]" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "407dd25a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tup.count(3.14)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "74c141ca", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tup.index(2)" + ] + }, + { + "cell_type": "markdown", + "id": "57a7de3a", + "metadata": {}, + "source": [ + "#### 2.5.3 Set\n", + "\n", + "Set là danh sách không theo thứ tự các phần tử không trùng nhau nằm trong hai dấu ngoặc nhọn `{}` hoặc được khởi tạo bằng `set()`. Set có hỗ trợ các tính toán tổ hợp như là phép toán giao, phép toán hợp, phép đối xứng, phép loại trừ...\n", + "\n", + "##### 2.5.3.0 Khai báo set\n", + "\n", + "- Cú pháp: `{value1, value2, ...}`\n" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "7464c228", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3}" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s = {1, 1, 1, 2, 3}\n", + "s" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "ebd94fd0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3}" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s.difference()" + ] + }, + { + "cell_type": "markdown", + "id": "cf1ff04b", + "metadata": {}, + "source": [ + "##### 2.5.3.1 Các hàm cơ bản trong set\n", + "\n", + "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", + "|-|-|-|\n", + "|`add()`|Thêm một phần tử vào vị trí cuối cùng trong set|Không|\n", + "|`clear()`|Xóa sạch các phần tử trong set|Không|\n", + "|`copy()`|Sao chép toàn bộ set|Có|\n", + "|`A.difference(B)`|Phép trừ sẽ trả về một tập hợp các giá trị chỉ có ở set A hoặc set B|Có|\n", + "|`A.difference_update(B)`|Xóa các thành phần tồn tại trong cả hai bộ của set A|Không|\n", + "|`discard()`|Xoá phần tử trong set|Có|\n", + "|`A.intersection(B)`|Phép giao giữa 2 set|Có|\n", + "|`A.intersection_update(B)`|Loại bỏ các thành phần không có trong cả 2 set|Không|\n", + "|`A.isdisjoint(B)`|Trả về True nếu 2 set là tập hợp rời rạc|Có|\n", + "|`A.issubset(B)`|Trả về True nếu tất cả các item của set A đều có trong set B, False nếu ngược lại|Có|\n", + "|`A.issuperset(B)`|Trả về True nếu tất cả các item của set B đều có trong set A, False nếu ngược lại|Có|\n", + "|`pop()`|Loại bỏ phần tử ở vị trí index cho trước|Không|\n", + "|`remove()`|Loại bỏ một phần tử |Không|\n", + "|`A.symmetric_difference(B)`|Phép đối xứng trả về tập hợp những giá trị có trong set A không có trong set B và ngược lại|Có|\n", + "|`A.symmetric_difference_update(B)`|Xóa các thành phần có trong cả hai nhóm và chèn các mục không có trong cả hai nhóm|Có|\n", + "|`A.union(B)`|Phép hợp của 2 set|Có|\n", + "|`update()`|Thêm phần tử|Không|\n", + "\n", + "\n", + "Để có thể nắm được ý nghĩa của các hàm trong set hay bất kì cấu trúc dữ liệu nào, ta có thể tận dụng `docstring` hoặc `help()`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "ee5aaeb8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['add',\n", + " 'clear',\n", + " 'copy',\n", + " 'difference',\n", + " 'difference_update',\n", + " 'discard',\n", + " 'intersection',\n", + " 'intersection_update',\n", + " 'isdisjoint',\n", + " 'issubset',\n", + " 'issuperset',\n", + " 'pop',\n", + " 'remove',\n", + " 'symmetric_difference',\n", + " 'symmetric_difference_update',\n", + " 'union',\n", + " 'update']" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[method for method in dir(s) if not method.startswith('_')]" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "7452a366", + "metadata": {}, + "outputs": [], + "source": [ + "s = {1, 1, 1, 2, 3}\n", + "s1 = {2,3,4,5}" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "5151dabe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{1}" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s.difference(s1)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "85828899", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{4, 5}" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s1.difference(s)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "6ebafade", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 3}" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s.intersection(s1)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "dfc15c60", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 4, 5}" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s.symmetric_difference(s1)" + ] + }, + { + "cell_type": "markdown", + "id": "d0f03862", + "metadata": {}, + "source": [ + "#### 5.4 Dictionary\n", + "\n", + "##### 5.4.0 Khai báo dictionary\n", + "\n", + "Để khai báo một dictionary chúng ta dùng cặp dấu `{}` theo cú pháp sau:\n", + "- Cú pháp: `{key1:value1, key2:value2,...}`\n", + "\n", + "Lưu ý:\n", + "- Các phần tử đều phải có key.\n", + "- Key chỉ có thể là số hoặc chuỗi.\n", + "- Key phải là duy nhất, nếu không nó sẽ nhận giá trị của phần tử có key được xuất hiện cuối cùng.\n", + "- Key khi đã được khai báo thì không thể đổi được tên.\n", + "- Key có phân biệt hoa thường." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "443c9948", + "metadata": {}, + "outputs": [], + "source": [ + "my_dict = {}" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "7cae2057", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'language1': 'Java', 'language2': 'Python', 'language3': 'C++'}" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1 = {'language1':'Java', 'language2':'Python', 'language3':'C++'}\n", + "my_dict1" + ] + }, + { + "cell_type": "markdown", + "id": "8545d94f", + "metadata": {}, + "source": [ + "##### 5.4.1 Truy xuất các phần tử trong dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "196b5959", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Java'" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1['language1']" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "b100719b", + "metadata": {}, + "outputs": [], + "source": [ + "my_dict1['language4'] = 'C' # dict_name[key] = value" + ] + }, + { + "cell_type": "markdown", + "id": "13101e15", + "metadata": {}, + "source": [ + "Dictionary có thể lồng trong dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "695d8160", + "metadata": {}, + "outputs": [], + "source": [ + "my_dict2 = {'name':'Zootpi',\n", + " 'info':{'age':1,\n", + " 'sex':'unknown'}}" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "b96f582c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'unknown'" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict2['info']['sex']" + ] + }, + { + "cell_type": "markdown", + "id": "e7d25ef3", + "metadata": {}, + "source": [ + "##### 2.5.4.2 Các hàm cơ bản trong dictionary\n", + "\n", + "|Tên hàm|Ý nghĩa|Trả về giá trị|\n", + "|-|-|-|\n", + "|`clear()`|Xóa sạch các phần tử|Không|\n", + "|`copy()`|Sao chép các phần tử|Có|\n", + "|`fromkeys()`|Tạo ra một dictionary với các khóa và giá trị được chỉ định|Có|\n", + "|`get()`|Trả về giá trị của khóa được chỉ định|Có|\n", + "|`items()`| Trả về danh sách chứa bộ giá trị cho mỗi cặp giá trị khóa|Có|\n", + "|`keys()`|Trả về danh sách chứa các key của dictionary|Có|\n", + "|`pop()`|Loại bỏ phần tử có key được chỉ định|Không|\n", + "|`popitem()`|Xóa cặp key-value được chèn cuối cùng|Không|\n", + "|`setdefault()`|Trả về giá trị của key được chỉ định. Nếu key không tồn tại: hãy chèn key, với giá trị được chỉ định|Không|\n", + "|`update()`|Cập nhật dictionary với các cặp key-value được chỉ định|Không|\n", + "|`values()`| Trả về danh sách tất cả các value trong từ điển |Có|" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "3206e9c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['clear',\n", + " 'copy',\n", + " 'fromkeys',\n", + " 'get',\n", + " 'items',\n", + " 'keys',\n", + " 'pop',\n", + " 'popitem',\n", + " 'setdefault',\n", + " 'update',\n", + " 'values']" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[method for method in dir(my_dict1) if not method.startswith('_')]" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "63251530", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'key1': 0, 'key2': 0, 'key3': 0}" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = ('key1', 'key2', 'key3')\n", + "y = 0\n", + "dict.fromkeys(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "e5376641", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python'" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1.get('language2')" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "65fb8de1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('language1', 'Java'), ('language2', 'Python'), ('language3', 'C++'), ('language4', 'C')])" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1.items()" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "f6260ef0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['language1', 'language2', 'language3', 'language4'])" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "f36943c0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'C'" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1.pop('language4')" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "5e74a0b4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'language1': 'Java', 'language2': 'Python', 'language3': 'C++'}" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "5b24a8a8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('language3', 'C++')" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1.popitem()" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "d79541e5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'language1': 'Java', 'language2': 'Python'}" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "5ed142be", + "metadata": {}, + "outputs": [], + "source": [ + "my_dict1.setdefault('language3')" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "f7df8462", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'language1': 'Java', 'language2': 'Python', 'language3': None}" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "e0824b22", + "metadata": {}, + "outputs": [], + "source": [ + "my_dict1.update({'language3':'Ruby'})" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "15573f7b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'language1': 'Java', 'language2': 'Python', 'language3': 'Ruby'}" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "eb4e4962", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_values(['Java', 'Python', 'Ruby'])" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_dict1.values()" + ] + }, + { + "cell_type": "markdown", + "id": "61c33af1", + "metadata": {}, + "source": [ + "## ✅ Tóm tắt\n", + "\n", + "Trong bài này, chúng ta đã tìm hiểu:\n", + "\n", + "- **Python**: Ngôn ngữ lập trình bậc cao, thông dịch, đa năng\n", + "- **Tôn chỉ Python**: The Zen of Python - 19 nguyên tắc thiết kế\n", + "- **Các thành phần cơ bản**:\n", + " - Biến và kiểu dữ liệu (int, float, str, bool)\n", + " - Toán tử số học, luận lý, điều kiện\n", + " - Cấu trúc dữ liệu: List, Tuple, Set, Dictionary\n", + "- **Ép kiểu dữ liệu**: int(), float(), str(), bool()\n", + "\n", + "## 💡 Lưu ý quan trọng\n", + "\n", + "- Python là dynamic typing - không cần khai báo kiểu dữ liệu\n", + "- List có thể thay đổi (mutable), Tuple không thể thay đổi (immutable)\n", + "- Dictionary sử dụng key-value pairs để lưu trữ dữ liệu\n", + "- Set tự động loại bỏ phần tử trùng lặp\n", + "\n", + "## 🧪 Thực hành\n", + "\n", + "Hãy thử tạo các cấu trúc dữ liệu sau:\n", + "- Một list chứa tên 5 người bạn\n", + "- Một dictionary lưu thông tin cá nhân (tên, tuổi, thành phố)\n", + "- Một set chứa các số nguyên tố nhỏ hơn 20\n", + "\n", + "## ➡️ Bước tiếp theo\n", + "\n", + "Ở bài tiếp theo, chúng ta sẽ tìm hiểu về vòng lặp, câu điều kiện, cũng như cách viết 1 hàm trong Python." + ] } - ], - "source": [ - "my_dict1.values()" - ] - }, - { - "cell_type": "markdown", - "id": "61c33af1", - "metadata": {}, - "source": [ - "Ở bài tiếp theo, chúng ta sẽ tìm hiểu về vòng lặp, câu điều kiện, cũng như cách viết 1 hàm trong Python." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.8.2 64-bit", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.2" - }, - "vscode": { - "interpreter": { - "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.2 64-bit", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.2" + }, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/docs/tutorial/python/04.foundation.md b/docs/tutorial/python/04.foundation.md new file mode 100644 index 00000000..17a08a85 --- /dev/null +++ b/docs/tutorial/python/04.foundation.md @@ -0,0 +1,1311 @@ +# Bài 2: Giới thiệu về PYTHON + +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được Python là gì và các đặc điểm của nó +- [ ] Nắm vững các khái niệm cơ bản về ngôn ngữ lập trình +- [ ] Hiểu được tôn chỉ Python (The Zen of Python) +- [ ] Sử dụng các cấu trúc cơ bản trong Python (biến, kiểu dữ liệu, cấu trúc điều khiển) + + +![](https://laptrinhcanban.com/python/nhap-mon-lap-trinh-python/gioi-thieu-python/python-la-gi/Python.jpg) + +## 1. Giới thiệu + +### 1.1. Tổng quan + +Theo thống kê của Wiki, tính đến đến tháng 10/2010, trên thế giới có khoảng hơn 700 ngôn ngữ lập trình (xem danh sách các ngôn ngữ lập trình tại [đây](https://vi.wikipedia.org/wiki/Danh_s%C3%A1ch_c%C3%A1c_ng%C3%B4n_ng%E1%BB%AF_l%E1%BA%ADp_tr%C3%ACnh)). Theo thống kê của [FOLDOC](http://foldoc.org/), 1 trang từ điển tin học trực tuyến miễn phí, con số này đã lên 1000 vào tháng 9/2020, 1 con số đáng kinh ngạc. + +![Độ phổ biến của 1 số ngôn ngữ lập trình được sử dụng trên các kho lưu trữ trên GitHub.](./img/python.png) +> *Độ phổ biến của 1 số ngôn ngữ lập trình được sử dụng trên các kho lưu trữ trên GitHub tính đến tháng 4/2014 theo [Githut](https://githut.info/).* + +Cũng giống như ngôn ngữ nói, ngôn ngữ lập trình cũng tuân theo 1 hệ thống phân cấp bao gồm 2 loại chính. + +- ***Ngôn ngữ bậc thấp***: Cú pháp cực kỳ gần với ngôn ngữ máy nên rất khó sử dụng, tuy nhiên lại cung cấp nhiều chức năng hơn và cho phép ta tạo ra chương trình chi tiết và hiệu quả hơn rất nhiều, thích hợp để viết các chương trình liên quan đến kiến trúc và phần cứng của máy tính. Các ngôn ngữ này có thể được chia thành hai loại: ngôn ngữ máy và hợp ngữ. + +- ***Ngôn ngữ bậc cao***: Cú pháp gần gũi với ngôn ngữ của con người hơn, tương tự như ngôn ngữ tiếng Anh nên dễ hiểu và thân thiện với người dùng hơn, xử lý lỗi nhanh hơn, thích hợp để phát triển phần mềm, xử lý dữ liệu... Một số ngôn ngữ bậc cao nổi tiếng nhất có thể kể đến C, C ++, Java và Python. + +### 1.2. Ngôn ngữ lập trình Python + +Một số điểm nổi bật: +- Được phát triển từ 1980s và chính thức phát hành vào năm 1991 bởi `Guido van Rossum`. +- Là ngôn ngữ lập trình `bậc cao` - `high level`: người dùng lập trình bằng ngôn ngữ dễ hiểu và dễ sử dụng, sau đó máy tính sẽ chuyển các đoạn mã thành ngôn ngữ máy tính. +- Là ngôn ngữ `thông dịch`-`interpreted`: code được viết ra sẽ được chuyển thể trực tiếp thành ngôn ngữ máy tính và chạy trực tiếp cùng 1 lúc. Điều này khiến việc sử dụng Python dễ dàng hơn so với các ngôn ngữ `biên dịch`-`compiled` - code được chuyển thể thành ngôn ngữ máy tính trước rồi mới có thể chạy. +- Là ngôn ngữ `đa năng`-`generally purposed`: có thể sử dụng cho nhiều mục đích khác nhau. +- Là ngôn ngữ `dynamic type`: không cần khai báo trước kiểu dữ liệu của biến, có thể ghi đè trực tiếp kiểu dữ liệu của biến. +- Là ngôn ngữ `tuần tự`-`procedural` đồng thời `hướng đối tượng`-`object-oriented`. +- Phiên bản ổn định hiện tại là phiên bản `3.12+` (khuyến nghị sử dụng Python 3.10 hoặc 3.11 để đảm bảo tương thích tốt với các thư viện). + +Python được sử dụng như một công cụ hoàn hảo cho các tác vụ tính toán khoa học, bao gồm phân tích và trực quan hóa các bộ dữ liệu lớn nhờ hệ sinh thái lớn và hoạt động của các gói bên thứ ba như: +- [NumPy](https://numpy.org/) +- [Pandas](https://pandas.pydata.org/) +- [Matplotlib](https://matplotlib.org/), [Seaborn](https://seaborn.pydata.org/) +- [Scikit-Learn](https://scikit-learn.org/stable/) + +### 1.3. Tôn chỉ Python (The Zen of Python) + +> 1. Beautiful is better than ugly >> Thà đẹp hơn xấu + +> 2. Explicit is better than implicit >> Thà rõ ràng hơn là ngấm ngầm + +> 3. Simple is better than complex >> Thà đơn giản hơn phức hợp + +> 4. Complex is better than complicated >> Thà phức hợp hơn phức tạp + +> 5. Readability counts >> Dễ đọc là 1 điểm cộng + +#### Perl + +> There is more than one way to do it. + +#### Python + +> There is more than one way to do it. But, there should be one - and preferable one - obvious way to do it. + +Tham khảo [PEP 8](https://www.python.org/dev/peps/pep-0008/) + + +### 1.4. Vì sao nên dùng Python + +In ra `Hello, World!` với các ngôn ngữ: + +#### C ++ +```C++ +#include + +int main() +{ + std::cout << "Hello, world!"; + return 0; +} +``` + +### C# +```C# +using System; +class Program +{ + public static void Main(string[] args) + { + Console.WriteLine("Hello, world!"); + } +} +``` + +### Java +```Java +class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} +``` + +### Python +```Python +print("Hello, World!"); +``` + +Nguồn tài liệu: +- Thư viện để phân tích dữ liệu: Pandas, Numpy, Matplotlib. +- Python thuần tuý: Euler project, Python Module of the week. +- Web với Python: Flask. +- Automate với Python: Sách Automate the boring stuff with Python. + +Để bắt đầu bài học với Python, chúng ta sẽ cùng nhau thực thi chương trình đầu tiên `print('Hello, world')`. Lưu ý, ta có thể nhấn tổ hợp phím `Shift Return` (MacOS) hoặc `Shift Enter` (Windows, Ubuntu) để thực thi câu lệnh trong cell code. + + +```python +print('Hello, world!') +``` + + Hello, world! + + +Dòng chú thích (Comments) là các câu giải thích đoạn mã để con người có thể đọc được. Nó cung cấp một số thông tin hoặc giải thích về những gì mỗi phần của một chương trình thực hiện. Dòng chú thích bị trình biên dịch và thông dịch bỏ qua. + +Python hỗ trợ chú thích một dòng: + + +```python +# Print Hello, World! on the screen +print("Hello, World!") +``` + + Hello, World! + + + +```python +print("Hello, World!") # Print Hello, World! on the screen +``` + + Hello, World! + + +Để chú thích nhiều dòng, bạn có thể chèn `'''` hoặc `"""`cho mỗi dòng: + + +```python +''' +Use print() function +to print Hello, World! on the screen +''' +print("Hello, World!") +``` + + Hello, World! + + +Ok, bây giờ chúng ta sẽ cùng nhau tìm hiểu sâu hơn về các thành phần cơ bản trong Python nha. + +## 2. Các thành phần cơ bản trong Python + +### 2.1. Biến (Variables) + +Khái niệm biến chắc hẳn không còn xa lạ gì với các bạn từ những ngày cấp 2, cấp 3. Khi ta làm các bài toán đại số, các bạn luôn phải chạm mặt các biến như là biến x, y,... Trong lập trình, biến là tên của một vùng trong bộ nhớ RAM, được sử dụng để lưu trữ thông tin. Khi một biến được khai báo, một vùng trong bộ nhớ sẽ dành cho các biến. + +Tại sao chúng ta cần sử dụng biến? + + +```python +5233255324824524012138 * 523209168801482034 # nhân hai số +``` + + + + + 2738087168627369076568435079514204928692 + + + + +```python +2738087168627369076568435079514204928692 + 3140713 # lấy kết quả phép tính trên tiếp tục thực hiện cộng hai số +``` + + + + + 2738087168627369076568435079514208069405 + + + +Từ ví dụ trên, ta có thể thấy rằng những con số với nhiều chữ số gây khó khăn trong việc sử dụng vì chúng có quá nhiều chữ số, đôi lúc chúng ta cũng có thể vô tình gây sai lệnh giá trị. Để tránh điều này xảy ra, ta có thể nhờ tới sự giúp đỡ của các biến để lưu trữ dữ liệu và lấy ra để tính toán được thuận tiện và chính xác hơn. + +#### 2.1.1 Khởi tạo biến + +Để khai báo biến trong Python thì mọi người sử dụng cú pháp `tên_biến = giá_trị`, trong đó: +- `tên_biến`: tên của biến mà các bạn muốn đặt. +- `giá_trị` : giá trị của biến mà bạn muốn gán. +- `=`: phép gán. + + +```python +x = 5233255324824524012138 # gán giá trị 5233255324824524012138 cho biến x +y = 523209168801482034 # gán giá trị 523209168801482034 cho biến y +z = 3140713 # gán giá trị 3140713 cho biến z +x * y + z +``` + + + + + 2738087168627369076568435079514208069405 + + + +Dễ thấy cùng 1 kết quả tương tự, dùng biến giúp ta dễ dàng tính toán, giảm thiểu tỉ lệ sai lệnh giá trị hơn khi không sử dụng tới biến. Ta có thể khai báo mỗi biến 1 dòng như trên hoặc khai báo chúng trên cùng 1 dòng. + + +```python +x , y , z = 5233255324824524012138, 523209168801482034, 3140713 +x * y + z +``` + + + + + 2738087168627369076568435079514208069405 + + + +Một số lưu ý khi đặt tên biến: +- Tên của biến có thể bắt đầu với các kí tự hoặc dấu `_` nhưng không được bắt đầu bằng số. +- Tên biến phân biệt chữ hoa chữ thường: `Num`, `NUM`,`num` là 3 tên biến khác nhau. +- Tên biến không được trùng với các từ khóa của Python. + +#### 2.1.2 Các kiểu dữ liệu + +Với Python, khi ta khai báo một biến thì kiểu dữ liệu của biến sẽ tự động được nhận biết. Vì vậy, ta cũng không phải quá vất vả khi khai báo 1 biến. Mặc định, Python có các kiểu dữ liệu cơ bản sau: + +|Kiểu dữ liệu|Ý nghĩa|Ví dụ| +|-|-|-| +|`int`|Kiểu số nguyên (không có chứa dấu chấm thập phân)|`11`, `-15`| +|`float`|Kiểu số thực (có chứa dấu chấm thập phân)|`3.14`, `4.02`| +|`complex`|Kiểu số phức|`1 + 2j`, `3 - 4j`| +|`str`|Kiểu chuỗi|`'MCI'`,`'Python'`| +|`bool`|Kiểu luận lý|`TRUE`, `FALSE`| + +#### 2.1.3 Kiểm tra kiểu dữ liệu của biến + +Để kiểm tra kiểu dữ liệu giá trị của một biến đã khởi tạo, ta sử dụng hàm `type()` +- Cú pháp: `type(tên_biến)` + + +```python +type(x) # số nguyên +``` + + + + + int + + + + +```python +float_number = 3.14 # số thực +type(float_number) +``` + + + + + float + + + + +```python +# type = 0 # Không nên gán như thế này +``` + +#### 2.1.4 Ép kiểu dữ liệu +Python hỗ trợ chuyển đổi kiểu dữ liệu của một biến x (casting) qua 1 số hàm cơ bản sau: + +|Cú pháp| Ý nghĩa| +|-|-| +|`float(x)`| chuyển đổi sang kiểu số thực| +|`int(x)` | chuyển đổi sang kiểu số| +|`str(x)` | chuyển đổi sang dạng chuỗi| +|`complex(x)`| chuyển đổi sang kiểu phức hợp| +|`tuple(x)`| chuyển đổi sang kiểu Tuple| +|`dict(x)` | chuyển đổi sang kiểu Dictionary| +|`hex(x)` | chuyển đổi sang hệ 16| +|`oct(x)` | chuyển đổi sang hệ 8| +|`chr(x)` | chuyển đổi sang dạng ký tự| + +Ví dụ: + + +```python +x = 3.14 +int(x) +``` + + + + + 3 + + + + +```python +complex(x) +``` + + + + + (3.14+0j) + + + + +```python +str(x) +``` + + + + + '3.14' + + + +### 2.2. Toán tử số học + +|Phép toán|Biểu thức|Ý nghĩa | +|-|-|-| +|+|`x + y`|Phép cộng| +|-|`x - y`|Phép trừ| +|*|`x * y`|Phép nhân| +|/|`x / y`|Phép chia| +|%|`x % y`|Phép chia lấy phần dư| +|//|`x // y`|Phép chia làm tròn xuống| +|**|`x ** y`|Phép mũ| + +Ví dụ: + + +```python +x, y = 11, 3 +``` + + +```python +print(' x + y =',x + y, + '\n x - y =',x - y, + '\n x * y =',x * y, + '\n x / y =',x / y , + '\n x % y =',x % y, + '\n x // y =',x // y, + '\n x ** y = ',x ** y, + '\n x ** (1/2) =',x ** (1/2)) +``` + + x + y = 14 + x - y = 8 + x * y = 33 + x / y = 3.6666666666666665 + x % y = 2 + x // y = 3 + x ** y = 1331 + x ** (1/2) = 3.3166247903554 + + +Ngoài ra, ta có thể tận dụng thư viện `math` để tính toán + + +```python +import math +print(math.ceil(3.14)) # làm tròn lên +print(math.floor(3.14)) # làm tròn xuống +``` + + 4 + 3 + + + +```python +round(3.54) +``` + + + + + 4 + + + +### 2.3. Toán tử luận lý + +Để thực hiện các phép toán luận lý trong python, ta có thể sử dụng các toán tử luận lý dưới đây: + +|Toán tử|Biểu thức|Ý nghĩa| +|-|-|-| +|AND|`X and Y`|True nếu cả X và Y đều đúng| +|OR |`X or Y`|True nếu ít nhất một trong hai vế X hoặc Y đúng| +|NOT|`not X`|True nếu X sai và False nếu X đúng| +|XOR|`bool(Y) ^ bool(Y)`|True nếu X và Y khác giá trị và False nếu ngược lại| + +Phép toán luận lý sẽ kiểm tra hai vế của toán tử là đúng hay sai và kết hợp chúng lại để đưa ra kết quả. Kết quả của phép toán luận lý là kiểu boolean trong Python với hai giá trị là True (đúng) hoặc False (sai). + + +```python +True and False +``` + + + + + False + + + + +```python +True or False +``` + + + + + True + + + + +```python +True and not False or (True and False) +``` + + + + + True + + + +### 2.4. Toán tử điều kiện + +|Toán tử| Biểu thức| Ý nghĩa| +|-|-|-| +|<| `x < y` |So sánh giá trị x có nhỏ hơn y hay không| +|>| `x > y `|So sánh giá trị x có lớn hơn y hay không| +|<=|`x <= y`|So sánh giá trị x có nhỏ hơn hoặc bằng y hay không| +|>=|`x >= y`|So sánh giá trị x có lớn hơn hoặc bằng y hay không| +|==|`x == y`|So sánh giá trị x có bằng y hay không| +|!=|`x != y`|So sánh giá trị x có khác y hay không| + +Tương tự toán tử luận lý, kết quả của phép toán điều kiện là kiểu boolean với hai giá trị là True (đúng) hoặc False (sai). + +Lưu ý trong Python, dấu `=` sử dụng cho phép gán, dấu `==` sử dụng cho phép so sánh. + + +```python +3 > 5 +``` + + + + + False + + + + +```python +1 != 2 +``` + + + + + True + + + + +```python +(5 == 6) or (9 >= 2) and (8 != 3) +``` + + + + + True + + + +### 2.5. Cấu trúc dữ liệu + +Cấu trúc dữ liệu (data structures) và giải thuật (algorithms) được xem là 2 yếu tố quan trọng nhất trong lập trình, đúng như câu nói nổi tiếng của Niklaus Wirth: + +`Programs = Data Structures + Algorithms` + +Như tên gọi của mình, cấu trúc dữ liệu cho phép ta tổ chức, sắp xếp, quản lý và lưu trữ dữ liệu sao cho đảm bảo việc truy cập dễ dàng hơn và sửa đổi hiệu quả hơn. Trong thực tế, cấu trúc dữ liệu có thể chia thành 2 nhánh: được định nghĩa sẵn chỉ lôi ra dùng thôi (built-in) và định nghĩa bởi người dùng (user-defined). + +![](https://sunway.edu.np/wp-content/uploads/2020/11/Blank-diagram-700x365.png) + +Trong nôi dụng bài viết này, chúng ta sẽ đi sâu vào 4 cấu trúc dữ liệu: List, Dictionary, Tuple, và Set. + +|Cấu trúc dữ liệu| Cú pháp | Đặc điểm| +|-|-|-| +|List|`[]`,`list()`|có index, có thứ tự, có thể thay đổi| +|Dictionary|`{key:value}`|không trùng lặp| +|Tuple|`()`,`tuple()`|có index, có thứ tự, không thể thay đổi| +|Set|`{}`,`set`|không index, không trùng lặp, có thứ tự , có thể thay đổi| + +#### 2.5.1 List + +List là một danh sách các phần tử dữ liệu phân cách bởi dấu phẩy và được bao ngoài bởi dấu ngoặc vuông `[]`, có thể chứa 1 hoặc nhiều kiểu dữ liệu khác nhau từ 1 số cho đến 1 chuỗi được lưu theo 1 cách tuần tự. + +##### 2.5.1.0 Khai báo list + +- Cú pháp: `[value1, value2, ...]` + +Mỗi phần tử trong list đều được gán 1 vị trí, gọi là chỉ số index. Giá trị index bắt đầu từ 0 và tiếp tục cho đến phần tử cuối cùng được gọi là index dương (theo chiều từ trái qua phải). Ngoài ra, ta có thể sử dụng index phủ định bắt đầu từ -1 cho phép bạn truy cập các phần tử từ cuối cùng đến đầu tiên (theo chiều từ phải qua trái). + + + +```python +li = [1 , 2, "Python", 3.14] +# 0 1 2 3 -> index từ trái sang phải +# -4 -3 -2 -1 -> index từ phải sang trái +li +``` + + + + + [1, 2, 'Python', 3.14] + + + + +```python +len(li) +``` + + + + + 4 + + + +##### 2.5.1.1 Truy xuất phần tử trong list + +Như đề cập ở trên, mỗi phần tử trong List sẽ có một vị trí nhất định tương ứng với một con số index, bắt đầu từ số 0 và tăng dần từ trái qua phải. Chúng ta có thể truy xuất đến các phần tử trong danh sách với cú pháp: `tên_list[index]` + + +```python +li[0] +``` + + + + + 1 + + + + +```python +li[1:3] +``` + + + + + [2, 'Python'] + + + +Mỗi phần tử bên trong list cũng có thể là 1 list con, khi đó mỗi list con cũng có 1 index duy nhất. + + +```python +li1 = [1, [1,2,3], 3.14, 'Hn'] +li1[1] +``` + + + + + [1, 2, 3] + + + + +```python +li1[1][0] +``` + + + + + 1 + + + + +```python +li[0] = 3.14 +li +``` + + + + + [3.14, 2, 'Python', 3.14] + + + +##### 2.5.1.2 Các hàm cơ bản với List + +Để biết có những hàm có sẵn nào hỗ trợ cho cấu trúc dữ liệu list, ta sử dụng câu lệnh dưới đây: + + +```python +[method for method in dir(li) if not method.startswith('_')] +``` + + + + + ['append', + 'clear', + 'copy', + 'count', + 'extend', + 'index', + 'insert', + 'pop', + 'remove', + 'reverse', + 'sort'] + + + +Các hàm này được chia thành 2 loại: +- Gán giá trị trực tiếp vào biến và không trả về cái gì +- Trả về giá trị và cần gán giá trị đó vào 1 biến mới + +Dưới đây là 1 số ví dụ về các hàm kể trên: + +|Tên hàm|Ý nghĩa|Trả về giá trị| +|-|-|-| +|`append()`|Thêm một phần tử vào vị trí cuối cùng trong list|Không| +|`clear()`|Xóa sạch các phần tử trong list|Không| +|`copy()`|Sao chép toàn bộ list|Có| +|`count()`|Đếm số lần một phần tử xuất hiện trong list|Có| +|`extend()`|Kết hợp list với một list khác|Không| +|`index()`|Trả về vị trí phần tử trong list|Có| +|`insert()`|Chèn một phần tử vào list tại vị trí cho trước|Không| +|`pop()`|Loại bỏ phần tử ở vị trí index cho trước trong list|Không| +|`remove()`|Loại bỏ một phần tử khỏi list|Không| +|`reverse()`|Đảo ngược thứ tự các phần tử trong list|Không| +|`sort()`|Sắp xếp các phần tử trong list|Không| + + +```python +li.append(10) +li +``` + + + + + [3.14, 2, 'Python', 3.14, 10] + + + + +```python +li.clear() +li +``` + + + + + [] + + + + +```python +li = [1, 2, 'Hanoi', 2.14, 'c'] +li.copy() +``` + + + + + [1, 2, 'Hanoi', 2.14, 'c'] + + + + +```python +li.count('Hanoi') +``` + + + + + 1 + + + + +```python +li.extend([1,2,3]) +li +``` + + + + + [1, 2, 'Hanoi', 2.14, 'c', 1, 2, 3] + + + + +```python +li.index(3) +``` + + + + + 7 + + + + +```python +li.insert(10,0) +li +``` + + + + + [1, 2, 'Hanoi', 2.14, 'c', 1, 2, 3, 0] + + + + +```python +li.pop(1) +li +``` + + + + + [1, 'Hanoi', 2.14, 'c', 1, 2, 3, 0] + + + + +```python +li.remove(0) +li +``` + + + + + [1, 'Hanoi', 2.14, 'c', 1, 2, 3] + + + + +```python +li = [1, 9, 2,0] +li.sort() +li +``` + + + + + [0, 1, 2, 9] + + + + +```python +li.reverse() +li +``` + + + + + [9, 2, 1, 0] + + + +Đố vui: +- Hàm `extend()` và hàm `append()` khác gì nhau? +- Hàm `remove()` and `pop()` khác gì nhau? + +#### 2.5.2 Tuple + +Tuple là 1 cấu trúc dữ liệu gần tương tự như list trong Python, tuy nhiên tuple là 1 danh sách bất biến, không thể thay đổi nội dung. Tuple có tốc độ xử lý nhanh hơn list, do tuple được lưu trữ một khối bộ nhớ xác định còn list thì thường xuyên phải thay đổi không gian lưu trữ. Chính vì vậy, với những dữ liệu dạng hằng số, dữ liệu không thay đổi theo thời gian, tuple là 1 sự lựa chọn hoàn hảo. Ngoài ra, tuple còn được sử dụng làm khóa trong dictionary, một cấu trúc dữ liệu ta sẽ được tìm hiểu trong phần sau. + +##### 2.5.2.0 Khai báo tuple + +- Cú pháp: `(value1, value2, ...)` + + +```python +tup = (1, 2, 3.14, 'Vietnam') +tup +``` + + + + + (1, 2, 3.14, 'Vietnam') + + + +##### 2.5.2.1 Các hàm cơ bản trong tuple + +|Tên hàm|Ý nghĩa|Trả về giá trị| +|-|-|-| +|`count()`|Đếm số lần một phần tử xuất hiện trong tuple|Có| +|`index()`|Trả về vị trí phần tử trong tuple|Có| + + +```python +[method for method in dir(tup) if not method.startswith('_')] +``` + + + + + ['count', 'index'] + + + + +```python +tup.count(3.14) +``` + + + + + 1 + + + + +```python +tup.index(2) +``` + + + + + 1 + + + +#### 2.5.3 Set + +Set là danh sách không theo thứ tự các phần tử không trùng nhau nằm trong hai dấu ngoặc nhọn `{}` hoặc được khởi tạo bằng `set()`. Set có hỗ trợ các tính toán tổ hợp như là phép toán giao, phép toán hợp, phép đối xứng, phép loại trừ... + +##### 2.5.3.0 Khai báo set + +- Cú pháp: `{value1, value2, ...}` + + + +```python +s = {1, 1, 1, 2, 3} +s +``` + + + + + {1, 2, 3} + + + + +```python +s.difference() +``` + + + + + {1, 2, 3} + + + +##### 2.5.3.1 Các hàm cơ bản trong set + +|Tên hàm|Ý nghĩa|Trả về giá trị| +|-|-|-| +|`add()`|Thêm một phần tử vào vị trí cuối cùng trong set|Không| +|`clear()`|Xóa sạch các phần tử trong set|Không| +|`copy()`|Sao chép toàn bộ set|Có| +|`A.difference(B)`|Phép trừ sẽ trả về một tập hợp các giá trị chỉ có ở set A hoặc set B|Có| +|`A.difference_update(B)`|Xóa các thành phần tồn tại trong cả hai bộ của set A|Không| +|`discard()`|Xoá phần tử trong set|Có| +|`A.intersection(B)`|Phép giao giữa 2 set|Có| +|`A.intersection_update(B)`|Loại bỏ các thành phần không có trong cả 2 set|Không| +|`A.isdisjoint(B)`|Trả về True nếu 2 set là tập hợp rời rạc|Có| +|`A.issubset(B)`|Trả về True nếu tất cả các item của set A đều có trong set B, False nếu ngược lại|Có| +|`A.issuperset(B)`|Trả về True nếu tất cả các item của set B đều có trong set A, False nếu ngược lại|Có| +|`pop()`|Loại bỏ phần tử ở vị trí index cho trước|Không| +|`remove()`|Loại bỏ một phần tử |Không| +|`A.symmetric_difference(B)`|Phép đối xứng trả về tập hợp những giá trị có trong set A không có trong set B và ngược lại|Có| +|`A.symmetric_difference_update(B)`|Xóa các thành phần có trong cả hai nhóm và chèn các mục không có trong cả hai nhóm|Có| +|`A.union(B)`|Phép hợp của 2 set|Có| +|`update()`|Thêm phần tử|Không| + + +Để có thể nắm được ý nghĩa của các hàm trong set hay bất kì cấu trúc dữ liệu nào, ta có thể tận dụng `docstring` hoặc `help()`. + + + +```python +[method for method in dir(s) if not method.startswith('_')] +``` + + + + + ['add', + 'clear', + 'copy', + 'difference', + 'difference_update', + 'discard', + 'intersection', + 'intersection_update', + 'isdisjoint', + 'issubset', + 'issuperset', + 'pop', + 'remove', + 'symmetric_difference', + 'symmetric_difference_update', + 'union', + 'update'] + + + + +```python +s = {1, 1, 1, 2, 3} +s1 = {2,3,4,5} +``` + + +```python +s.difference(s1) +``` + + + + + {1} + + + + +```python +s1.difference(s) +``` + + + + + {4, 5} + + + + +```python +s.intersection(s1) +``` + + + + + {2, 3} + + + + +```python +s.symmetric_difference(s1) +``` + + + + + {1, 4, 5} + + + +#### 5.4 Dictionary + +##### 5.4.0 Khai báo dictionary + +Để khai báo một dictionary chúng ta dùng cặp dấu `{}` theo cú pháp sau: +- Cú pháp: `{key1:value1, key2:value2,...}` + +Lưu ý: +- Các phần tử đều phải có key. +- Key chỉ có thể là số hoặc chuỗi. +- Key phải là duy nhất, nếu không nó sẽ nhận giá trị của phần tử có key được xuất hiện cuối cùng. +- Key khi đã được khai báo thì không thể đổi được tên. +- Key có phân biệt hoa thường. + + +```python +my_dict = {} +``` + + +```python +my_dict1 = {'language1':'Java', 'language2':'Python', 'language3':'C++'} +my_dict1 +``` + + + + + {'language1': 'Java', 'language2': 'Python', 'language3': 'C++'} + + + +##### 5.4.1 Truy xuất các phần tử trong dictionary + + +```python +my_dict1['language1'] +``` + + + + + 'Java' + + + + +```python +my_dict1['language4'] = 'C' # dict_name[key] = value +``` + +Dictionary có thể lồng trong dictionary + + +```python +my_dict2 = {'name':'Zootpi', + 'info':{'age':1, + 'sex':'unknown'}} +``` + + +```python +my_dict2['info']['sex'] +``` + + + + + 'unknown' + + + +##### 2.5.4.2 Các hàm cơ bản trong dictionary + +|Tên hàm|Ý nghĩa|Trả về giá trị| +|-|-|-| +|`clear()`|Xóa sạch các phần tử|Không| +|`copy()`|Sao chép các phần tử|Có| +|`fromkeys()`|Tạo ra một dictionary với các khóa và giá trị được chỉ định|Có| +|`get()`|Trả về giá trị của khóa được chỉ định|Có| +|`items()`| Trả về danh sách chứa bộ giá trị cho mỗi cặp giá trị khóa|Có| +|`keys()`|Trả về danh sách chứa các key của dictionary|Có| +|`pop()`|Loại bỏ phần tử có key được chỉ định|Không| +|`popitem()`|Xóa cặp key-value được chèn cuối cùng|Không| +|`setdefault()`|Trả về giá trị của key được chỉ định. Nếu key không tồn tại: hãy chèn key, với giá trị được chỉ định|Không| +|`update()`|Cập nhật dictionary với các cặp key-value được chỉ định|Không| +|`values()`| Trả về danh sách tất cả các value trong từ điển |Có| + + +```python +[method for method in dir(my_dict1) if not method.startswith('_')] +``` + + + + + ['clear', + 'copy', + 'fromkeys', + 'get', + 'items', + 'keys', + 'pop', + 'popitem', + 'setdefault', + 'update', + 'values'] + + + + +```python +x = ('key1', 'key2', 'key3') +y = 0 +dict.fromkeys(x, y) +``` + + + + + {'key1': 0, 'key2': 0, 'key3': 0} + + + + +```python +my_dict1.get('language2') +``` + + + + + 'Python' + + + + +```python +my_dict1.items() +``` + + + + + dict_items([('language1', 'Java'), ('language2', 'Python'), ('language3', 'C++'), ('language4', 'C')]) + + + + +```python +my_dict1.keys() +``` + + + + + dict_keys(['language1', 'language2', 'language3', 'language4']) + + + + +```python +my_dict1.pop('language4') +``` + + + + + 'C' + + + + +```python +my_dict1 +``` + + + + + {'language1': 'Java', 'language2': 'Python', 'language3': 'C++'} + + + + +```python +my_dict1.popitem() +``` + + + + + ('language3', 'C++') + + + + +```python +my_dict1 +``` + + + + + {'language1': 'Java', 'language2': 'Python'} + + + + +```python +my_dict1.setdefault('language3') +``` + + +```python +my_dict1 +``` + + + + + {'language1': 'Java', 'language2': 'Python', 'language3': None} + + + + +```python +my_dict1.update({'language3':'Ruby'}) +``` + + +```python +my_dict1 +``` + + + + + {'language1': 'Java', 'language2': 'Python', 'language3': 'Ruby'} + + + + +```python +my_dict1.values() +``` + + + + + dict_values(['Java', 'Python', 'Ruby']) + + + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **Python**: Ngôn ngữ lập trình bậc cao, thông dịch, đa năng +- **Tôn chỉ Python**: The Zen of Python - 19 nguyên tắc thiết kế +- **Các thành phần cơ bản**: + - Biến và kiểu dữ liệu (int, float, str, bool) + - Toán tử số học, luận lý, điều kiện + - Cấu trúc dữ liệu: List, Tuple, Set, Dictionary +- **Ép kiểu dữ liệu**: int(), float(), str(), bool() + +## 💡 Lưu ý quan trọng + +- Python là dynamic typing - không cần khai báo kiểu dữ liệu +- List có thể thay đổi (mutable), Tuple không thể thay đổi (immutable) +- Dictionary sử dụng key-value pairs để lưu trữ dữ liệu +- Set tự động loại bỏ phần tử trùng lặp + +## 🧪 Thực hành + +Hãy thử tạo các cấu trúc dữ liệu sau: +- Một list chứa tên 5 người bạn +- Một dictionary lưu thông tin cá nhân (tên, tuổi, thành phố) +- Một set chứa các số nguyên tố nhỏ hơn 20 + +## ➡️ Bước tiếp theo + +Ở bài tiếp theo, chúng ta sẽ tìm hiểu về vòng lặp, câu điều kiện, cũng như cách viết 1 hàm trong Python. diff --git a/docs/tutorial/python/05.foundation-part2.ipynb b/docs/tutorial/python/05.foundation-part2.ipynb index db338e4a..c09d2ff7 100644 --- a/docs/tutorial/python/05.foundation-part2.ipynb +++ b/docs/tutorial/python/05.foundation-part2.ipynb @@ -182,7 +182,7 @@ "- Các dòng đều chứa kí tự `*`\n", "- Số kí tự `*` của dòng sau nhiều hơn dòng trước 1 đơn vị.\n", "\n", - "Vậy liệu có cách nào để ta có thể thực hiện được bài đố vui trên chỉ với 1 hoặc 2 dòng lệnh ngay cả khi số lượng dòng cần in ra theo đặc trưng trên nhiều hơn? Hãy cũng nhau đến với vòng lặp. " + "Vậy liệu có cách nào để ta có thể thực hiện được bài đố vui trên chỉ với 1 hoặc 2 dòng lệnh ngay cả khi số lượng dòng cần in ra theo đặc trưng trên nhiều hơn? Hãy cùng nhau đến với vòng lặp. " ] }, { @@ -673,7 +673,7 @@ "`lambda parameters: business logic`\n", "\n", "Lưu ý, hàm vô danh có thể có nhiều tham số nhưng chỉ có 1 biểu thức trả về. \n", - "Khi bạn muốn biểu diễn các hàm số đơn giản một cách ngắn gọn hơn, hàm vô danh là 1 sự lựa chọn hoàn hảo. Tuy nhiên, khi muốn xây dưng các hàm số phức tạp với nhiều biểu thức và phép tính toán khác nhau thì hàm bình thường sẽ là sự lựa chọn tốt hơn.\n", + "Khi bạn muốn biểu diễn các hàm số đơn giản một cách ngắn gọn hơn, hàm vô danh là 1 sự lựa chọn hoàn hảo. Tuy nhiên, khi muốn xây dựng các hàm số phức tạp với nhiều biểu thức và phép tính toán khác nhau thì hàm bình thường sẽ là sự lựa chọn tốt hơn.\n", "\n" ] }, diff --git a/docs/tutorial/python/05.foundation-part2.md b/docs/tutorial/python/05.foundation-part2.md new file mode 100644 index 00000000..c6525504 --- /dev/null +++ b/docs/tutorial/python/05.foundation-part2.md @@ -0,0 +1,546 @@ +# Bài 3: Giới thiệu về PYTHON (tiếp) + +![](https://laptrinhcanban.com/python/nhap-mon-lap-trinh-python/gioi-thieu-python/python-la-gi/Python.jpg) + +Trong bài viết trước, chúng ta đã nắm được về các thành phần cơ bản trong Python bao gồm biến, các phép tính, các toán tử, cũng như làm quen với kiểu dữ liệu và cấu trúc dữ liệu cơ bản. Trong bài viết này, Zootopi sẽ cùng các bạn tìm hiểu về cách vận hành cũng như thứ tự thực thi của các đoạn mã 1 chương trình thông qua câu lệnh điều kiện, vòng lặp. Bên cạnh đó, Zootopi sẽ làm quen với hàm và cách để tạo ra hàm thông thường, hàm vô danh, và bao hàm. + +## 1. Câu lệnh tuần tự + +Trong Python, các câu lệnh ta viết được thực thi một cách tuần tự từ trên xuống dưới, ví dụ: + + +```python +print('Hello') +print('everyone') +print('!') +``` + + Hello + everyone + ! + + +Trong ví dụ trên, 3 hàm `print()` là 3 hàm độc lập với nhau. Khi ta thực thi các câu lệnh này, máy tính sẽ dịch và trả về kết quả các câu lệnh này theo thứ tự chúng xuất hiện trong cell code. Câu lệnh nào xuất hiện trước sẽ trả về kết quả trước và nó sẽ chạy và trả về kết quả cho đến khi hết các dòng lệnh hoặc cho đến khi gặp lỗi. + +***Đố vui***: In ra kết quả như sau: +``` +* +** +*** +**** +***** +``` + +Để giải bài đố vui này, ta có thể có 1 số phương pháp như sau: + +- Cách 1: + + +```python +print('*') +print('**') +print('***') +print('****') +print('*****') +``` + + * + ** + *** + **** + ***** + + +- Cách 2: + + +```python +print('*') +print('*'*2) +print('*'*3) +print('*'*4) +print('*'*5) +``` + + * + ** + *** + **** + ***** + + +- Cách 3: + + +```python +print('*\n**\n***\n****\n*****') +``` + + * + ** + *** + **** + ***** + + +Có thể thấy các dòng cần in ra có cùng 1 đặc trưng như sau: +- Các dòng đều chứa kí tự `*` +- Số kí tự `*` của dòng sau nhiều hơn dòng trước 1 đơn vị. + +Vậy liệu có cách nào để ta có thể thực hiện được bài đố vui trên chỉ với 1 hoặc 2 dòng lệnh ngay cả khi số lượng dòng cần in ra theo đặc trưng trên nhiều hơn? Hãy cùng nhau đến với vòng lặp. + +## 2. Vòng lặp + +### 2.1. Vòng lặp `for` + +`for` là 1 kiểu vòng lặp cho phép ta lặp lại các xử lý trong chương trình với một số lần cụ thể. + +- Cú pháp: +``` +for value in iterable: + business logic +``` + +hoặc + +``` +for index in range(len(iterable)): + business logic +``` + +Trong đó `iterable` là những cấu trúc dữ liệu có một hoặc nhiều phần tử như `List`,`Tuple`,`Set`, `Dictionary`,... + +Ở ví dụ phần 1, ta có thể sử dụng hàm `range()` để tạo ra 1 dãy số tương ứng với `iterable` trong cú pháp. Ví dụ, `range(10)` sẽ tạo một dãy số từ 0 đến 9 (10 số). + + +```python +print(list(range(10))) # list chạy từ 0 đến 10 (không bao gồm 10) +print(list(range(2,10))) # list chạy từ 2 đến 10 (không bao gồm 10) +print(list(range(2,10,2))) # list chạy từ 2 đến 10 (không bao gồm 10), cách nhau 2 đơn vị +print(len(range(2,10,2))) # độ dài của range(2,10,2) +``` + + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + [2, 3, 4, 5, 6, 7, 8, 9] + [2, 4, 6, 8] + 4 + + + +```python +# Cách 1 +li = list(range(6)) +for value in li: + print('*'*value) +``` + + + * + ** + *** + **** + ***** + + + +```python +# Cách 2 +for index in range(len(li)): + print('*'*li[index]) +``` + + + * + ** + *** + **** + ***** + + +**Bài tập**: + +Cho 1 danh sách `li` gồm 10 phần tử từ 1 đến 10, trả về danh sách mới `li1` sao cho mỗi phần tử trong `li1` là bình phương của phần tử có trong `li` sử dụng `for`. + + +```python +li = list(range(1,11)) # tạo 1 list gồm 10 phần tử từ 1 đến 10 và gán vào biến li +li1 = [] # khởi tạo 1 list rỗng gán vào biến li1 +for val in li: # với từng giá trị trong biến li + li1.append(val**2) # nối giá trị bình phương của phần tử trong li vào li1 +print(li) +print(li1) +``` + + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + + + +```python +li1 = [] #khởi tạo 1 list rỗng gán vào biến li1 +for idx in range(len(li)): # với từng index các phần tử trong biến i + li1.append(li[idx]**2) # nối giá trị bình phương của phần tử trong li vào li1 +print(li) +print(li1) +``` + + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + + +Thông thường, nếu số lần lặp là cố định thì vòng lặp `for` khuyến khích sử dụng, còn nếu số lần lặp không cố định thì nên sử dụng vòng lặp `while` dưới đây. + +### 2.2. Vòng lặp `while` + +- Cú pháp +``` +while condition(True/False): + business logic +``` + + +```python +i = 0 +while i < 6: + print('*'*i) + i += 1 +``` + + + * + ** + *** + **** + ***** + + +**Bài tập**: + +Cho 1 danh sách `li` gồm 10 phần tử từ 1 đến 10, trả về danh sách mới `li1` sao cho mỗi phần tử trong `li1` là bình phương của phần tử có trong `li` sử dụng `while`. + + +```python +li = list(range(1,11)) # tạo 1 list gồm 10 phần tử từ 1 đến 10 và gán vào biến li +li1 = [] # khởi tạo 1 list rỗng gán vào biến li1 +index = 0 +while index < len(li): # với giá trị của index nhỏ hơn độ dài biến li + li1.append(li[index]**2) # nối giá trị bình phương của phần tử trong li vào li1 + index += 1 +print(li) +print(li1) +``` + + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + + +## 3. Câu lệnh điều kiện + +Câu lệnh điều kiện trong Python sẽ thực thi các tính toán hoặc hành động tùy thuộc vào điều kiện ràng buộc là đúng hay sai. Các điều kiện ràng buộc này được mô tả thông qua toán tử so sánh và toán tử luận lý chúng ta đã học trong bài trước. Nếu kết quả của các phép so sánh hoặc luận lý này là True, các khối `business logic` bên trong kết quả đó mới được thực hiện. + +- Cú pháp: +``` +if condition(True/False): + business logic +elif condition(True/False): # --> optional + business logic +elif condition(True/False): # --> optional + business logic +elif condition(True/False): # --> optional + business logic + .... +else: + business logic # --> optional +``` + + +```python +x = 5 +y = 5 +if x < y: + print("x is smaller than y") # câu lệnh này không được thực thi vì x < y == False +elif x > y: + print("x is larger than y") # câu lệnh này không được thực thi vì x > y == False +else: + print("x is equal to y") # câu lệnh này không được thực thi vì x > y == True +``` + + x is equal to y + + +## 4. Hàm + +Hàm (function) là một khối lệnh đặc biệt có thể gọi để sử dụng ở các nơi khác nhau trong chương trình. Hàm còn có một tác dụng vô cùng quan trọng nữa là tránh việc phải lặp lại code để thực thi những tác vụ tương tự nhau, từ đó giúp cho ta có thể viết mã gọn hơn và đặc biệt có thể tái sử dụng cũng như quản lý mã nguồn chương trình dễ dàng hơn. + +Trong bài học trước, chúng ta đã làm quen với một số hàm có sẵn. Vậy để có thể tạo ra 1 hàm tự định nghĩa thì làm như thế nào? Ta cùng tham khảo cú pháp dưới đây: + +- Cú pháp +``` +def function_name(arg1, arg2, ...): + business logic + return #--> optional +``` + +Lưu ý: +- `arg` là các tham số truyền vào hàm. Một hàm có thể có 0, 1 hoặc nhiều tham số và các tham số này được ngăn cách nhau bởi dấu phẩy. +- Các biến và tham số của hàm chỉ có phạm vi trong hàm. Thời gian tồn tại của biến là khoảng thời gian mà biến đó xuất hiện trong bộ nhớ. Khi hàm được thực thi thì biến sẽ tồn tại. Biến bị hủy khi chúng ta thoát khỏi hàm. Hàm không nhớ giá trị của biến trong những lần gọi hàm trước đó. +- `return` dùng để trả về một giá trị (hoặc một biểu thức), hoặc đơn giản là trả về "không gì cả". Khi lệnh `return` được thực thi, hàm sẽ kết thúc. Tuy nhiên, `return` là lệnh không bắt buộc phải có trong thân hàm. + + +```python +def print_asterisk(number): + for i in range(number): + print('*'*i) +``` + + +```python +print_asterisk(6) +``` + + + * + ** + *** + **** + ***** + + +**Bài tập**: + +Viết 1 hàm tìm giá trị lớn nhất của 1 list các số nguyên. + + +```python +def max_list(li): + return max(li) +``` + + +```python +def max_list1(li): + max_num = 0 + for value in li: + if max_num < value: + max_num = value + return max_num +``` + + +```python +max_list([1,2,99,6]) +``` + + + + + 99 + + + + +```python +max_list1([1,2,99,6]) +``` + + + + + 99 + + + +Lưu ý, ta có thể thêm mô tả về hàm bằng cách sử dụng `'''` hoặc `"""` như cách ta viết chú thích trên nhiều dòng ở vị trí giữa `def function_name()` và `business logic`. + + +```python +def max_list(li): + """ + Return maximum number of a list + """ + return max(li) +``` + +Ngoài ra, trong Python ta có 1 khái niệm hàm khác, đó là **Hàm vô danh**. Nếu các hàm bình thường được định nghĩa bằng cách sử dụng từ khóa `def` và có tên hàm, thì hàm vô danh được định nghĩa bằng cách sử dụng từ khóa `lambda`. Đó cũng là lí do mà hàm vô danh còn được gọi là hàm Lambda. +- Cú pháp: +`lambda parameters: business logic` + +Lưu ý, hàm vô danh có thể có nhiều tham số nhưng chỉ có 1 biểu thức trả về. +Khi bạn muốn biểu diễn các hàm số đơn giản một cách ngắn gọn hơn, hàm vô danh là 1 sự lựa chọn hoàn hảo. Tuy nhiên, khi muốn xây dựng các hàm số phức tạp với nhiều biểu thức và phép tính toán khác nhau thì hàm bình thường sẽ là sự lựa chọn tốt hơn. + + + + +```python +def square(x): + return x**2 +``` + + +```python +square1 = lambda x : x**2 +``` + + +```python +square(2) +``` + + + + + 4 + + + + +```python +square1(2) +``` + + + + + 4 + + + + +```python +li = list(range(1,11)) +li1 = list(map(lambda a: a**2 , li)) +print(li) +print(li1) +``` + + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + + +## 5. Bao hàm + +Bao hàm (Comprehension) là một tính năng tuyệt vời của Python giúp bạn giảm được nhiều dòng code và cách tạo ra dữ liệu mới (kiểu list/set/tuple/dictionary) dựa vào dữ liệu cũ theo một điều kiện nào đó chỉ trong 1 dòng. Dữ liệu mới có thể ở cấu trúc dữ liệu list, set, tuple, hoặc dictionary tuy theo cú pháp định nghĩa. Không chỉ giúp ta có thể viết mã ngắn gọn hơn, tốc độ xử lý trên cùng 1 tác vụ cũng chứng minh sử dụng bao hàm nhanh hơn. + +- Cú pháp: + - List comprehension: `[expression for variable in iterable]` + - Set comprehension: `{expression for variable in iterable}` + - Tuple comprehension: `tuple(expression for variable in iterable)` + - Dictionary comprehension: `{key:value for variable in iterable]` + +Lưu ý: ta có thể thêm các điều kiện thông qua câu lệnh điều kiện `if else` với bao hàm, ví dụ cú pháp với list comprehension: +- `[expression for variable in iterable if condition]` +hoặc +- `[expression if condition else else-expression for variable in iterable ]` + + +```python +def square_list(li): + ''' + Hàm tính bình phương từng phần tử trong 1 danh sách + Trả về 1 danh sách mới chứa bình phương từng phần tử + ''' + li1 = [] + for value in li: + li1.append(value**2) + return li1 + +def square_list1(li): + ''' + Hàm tính bình phương từng phần tử trong 1 danh sách + Trả về 1 danh sách mới chứa bình phương từng phần tử + ''' + li1 = [] + for value in li: + li1.append(square(value)) + return li1 + +def square_list2(li): + return [square(x) for x in li] +``` + + +```python +li = [1,2,3] +print(square_list(li)) # list comprehension +print(square_list1(li)) +print(square_list2(li)) +``` + + [1, 4, 9] + [1, 4, 9] + [1, 4, 9] + + + +```python +[square(x) for x in li if x%2 == 0] # list comprehension kết hợp điều kiện +``` + + + + + [4] + + + + +```python +[square(x) if x%2 == 0 else x for x in li ] # list comprehension kết hợp điều kiện +``` + + + + + [1, 4, 3] + + + + +```python +[square(x) if x%2 == 0 else x if x > 2 else 0 for x in li ] # list comprehension kết hợp điều kiện +``` + + + + + [0, 4, 3] + + + + +```python +{square(x) for x in li} # set comprehension +``` + + + + + {1, 4, 9} + + + + +```python +tuple(square(x) for x in li) # tuple comprehension +``` + + + + + (1, 4, 9) + + + + +```python +{x: square(x) for x in li} # dictionary comprehension +``` + + + + + {1: 1, 2: 4, 3: 9} + + + +Trong các bài tiếp theo, chúng ta sẽ cùng nhau tìm hiểu 1 số thư viện bổ trợ cho Python trong việc thao tác, xử lý, phân tích, và trực quan hoá dữ liệu. diff --git a/docs/tutorial/python/06.numpy.md b/docs/tutorial/python/06.numpy.md new file mode 100644 index 00000000..ad44d133 --- /dev/null +++ b/docs/tutorial/python/06.numpy.md @@ -0,0 +1,790 @@ +# Bài 4: Giới thiệu về NumPy + +![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/31/NumPy_logo_2020.svg/1200px-NumPy_logo_2020.svg.png) + +## 1. Giới thiệu + +### 1.1. Numpy là gì? + +NumPy (Numerical Python) là một thư viện Python mã nguồn mở được sử dụng trong hầu hết các lĩnh vực khoa học và kỹ thuật. Đây là tiêu chuẩn chung để làm việc với dữ liệu số bằng Python và là tiêu chuẩn cốt lõi của hệ sinh thái Python và PyData. NumPy API được sử dụng rộng rãi trong Pandas, SciPy, Matplotlib, scikit-learning, scikit-image và hầu hết các gói Python khoa học và khoa học dữ liệu khác. + +`NumPy = multidimensional array + matrix data structures` + +Thư viện NumPy chứa các cấu trúc dữ liệu ma trận và mảng đa chiều (bạn sẽ tìm thấy thêm thông tin về điều này trong các phần sau). NumPy có thể được sử dụng để thực hiện nhiều phép toán khác nhau trên mảng. Nó bổ sung các cấu trúc dữ liệu mạnh mẽ vào Python để đảm bảo tính toán hiệu quả với các mảng và ma trận, đồng thời nó cung cấp một thư viện khổng lồ các hàm toán học cấp cao hoạt động trên các mảng và ma trận này. + +### 1.2. Cài đặt + +Để cài đặt thư viện NumPy trong môi trường ảo trên terminal (MacOS/Ubuntu) hoặc Anaconda Prompt (Windows), ta có thể thực hiện bằng 1 trong 2 cách dưới đây: +- Cách 1: Cài đặt khi tạo môi trường ảo thông qua câu lệnh `conda create -n hanh python=3.7 numpy` +- Cách 2: Cài đặt sau khi tạo xong môi trường ảo thông qua 1 trong 2 câu lệnh sau: + - `conda install numpy` + - `pip install numpy` + +Ngoài ra, ta cũng có thể cài đặt thư viện thẳng trong notebook bằng cách chạy 1 trong 2 câu lệnh sau trên cell code: + - `!conda install numpy` + - `!pip instal numpy` + +Để tìm hiểu xem `conda` và `pip` khác nhau như thế nào, các bạn có thể tham khảo tại [đây](https://www.anaconda.com/blog/understanding-conda-and-pip). + +### 1.3. Import thư viện + +Để có thể sử dụng NumPy và các hàm của nó, ta cần chạy câu lệnh sau: + +`import numpy as np` + +Chúng ta rút ngắn tên thư viện thành `np` để thuận tiện cho việc đọc code với NumPy. Đây là một quy ước được áp dụng rộng rãi mà bạn nên tuân theo để bất kỳ ai làm việc với code của bạn có thể dễ dàng hiểu nó. + + +```python +import numpy as np +``` + +### 1.4. Vì sao nên sử dụng NumPy? + +NumPy cung cấp cho bạn rất nhiều cách nhanh chóng và hiệu quả để tạo mảng (`array`) và thao tác dữ liệu số bên trong chúng. `list` có thể chứa các kiểu dữ liệu khác nhau, còn tất cả các phần tử trong `array` NumPy phải đồng nhất. Điều này cho phép các phép toán được thực hiện trên mảng sẽ cực kỳ kém hiệu quả nếu các mảng không đồng nhất. Cũng bởi vì thế, 1 `array` tiêu tốn ít bộ nhớ hơn, dẫn đến NumPy sử dụng ít bộ nhớ hơn nhiều để lưu trữ dữ liệu và nó cung cấp một cơ chế xác định các kiểu dữ liệu. Điều này cho phép mã được tối ưu hóa hơn nữa. + +NumPy có 2 tính chất chính: + +- **Vectorization**: + + +```python +[1, 2, 3] + [4, 5, 6] # Phép cộng 2 list là phép nối chuỗi phần tử +``` + + + + + [1, 2, 3, 4, 5, 6] + + + + +```python +import numpy as np +np.array([1,2,3]) + np.array([4,5,6]) +``` + + + + + array([5, 7, 9]) + + + +- **Broadcasting**: +![](https://i.stack.imgur.com/JcKv1.png) +*Reference*: [What is Numpy?](https://numpy.org/doc/stable/user/whatisnumpy.html#whatisnumpy) + +## 2. Thực hành các hàm cơ bản trong NumPy + +### 2.0. Ôn tập kiến thức về mảng đa chiều + +Đôi khi bạn có thể nghe thấy 1 array được gọi là `ndarray`, viết tắt của `N-dimensional array`(mảng N-chiều). `N-dimensional array` chỉ đơn giản là 1 array với 1 hay nhiều chiều dữ liệu. + +![](https://fgnt.github.io/python_crashkurs_doc/_images/numpy_array_t.png) + +Khi làm việc với mảng đa chiều, ta sẽ gặp những thuật ngữ sau: +- Scalar: Mảng 0 chiều (0-D array) +- Vector: Mảng 1 chiều (1-D array) +- Matrix: Mảng 2 chiều (2-D array) +- Tensor: Mảng 3 chiều (3-D array) + +![](https://www.kdnuggets.com/wp-content/uploads/scalar-vector-matrix-tensor.jpg) + +### 2.1. Tạo 1 mảng (Create an array) +#### 2.1.1 Tạo 1 mảng 1 chiều + +Để tạo ra 1 NumPy `array`, ta có thể tận dụng hàm `np.array()` và truyền một list cho nó. + + +```python +arr = np.array([1,2,3]) # khởi tạo 1 array +arr +``` + + + + + array([1, 2, 3]) + + + + +```python +type(arr) # kiểm tra kiểu dữ liệu của biến arr +``` + + + + + numpy.ndarray + + + + +```python +def addition(arr1,arr2): + ''' + Sum of 2 arrays. + + Parameters + ---------- + arr1 : First array. + arr2 : Second array. + ''' + return arr1 + arr2 +``` + + +```python +addition(np.array([1,2]),np.array([2,6])) +``` + + + + + array([3, 8]) + + + +Tham khảo mã nguồn xây dựng nên thư viện này tại [NumPy github](https://github.com/numpy/numpy). + +**Đố vui: Array khác list ở chỗ nào?** + +**Đáp án**: `list` Python có thể chứa các kiểu dữ liệu khác nhau, còn tất cả các phần tử trong `array` NumPy phải đồng nhất. + +Có hai quy tắc cơ bản cho mọi mảng trong NumPy. +- Mọi phần tử trong mảng phải có cùng kiểu và kích thước (same type and size). +- Nếu các phần tử của mảng cũng là mảng thì các mảng bên trong đó phải có cùng kiểu và số phần tử với nhau. + + +```python +# list có thể chứa các phần tử thuộc các kiểu dữ liệu khác nhau +li = [1,2,'H'] +type(li[0]), type(li[1]), type(li[2]) +``` + + + + + (int, int, str) + + + + +```python +# casting về string với kiểu dữ liệu là unicode string < 21 characters, casting int + float về float +arr = np.array([1,2,'H']) +type(arr[0]), type(arr[1]), type(arr[2]) +``` + + + + + (numpy.str_, numpy.str_, numpy.str_) + + + +Từ ví dụ trên, ta có thể thấy NumPy không trả về lỗi, nhưng nó chuyển các số nguyên thành chuỗi để đáp ứng thuộc tính rằng mọi phần tử đều là cùng một loại. Loại ‘ trả về 1 giá trị như nhau + +x1 = np.random.randint(10, size=6) # mảng 1 chiều +x2 = np.random.randint(10, size=(3, 4)) # mảng 2 chiều +x3 = np.random.randint(10, size=(3, 4, 5)) # mảng 3 chiều +``` + +#### 2.1.5 Lưu và tải mảng + + +```python +np.savetxt('test.txt',arr_2d) #save array +np.loadtxt('test.txt') #load array +``` + +### 2.2. Đổi chiều array + +Ta có thể sử dụng `np.newaxis` và `np.expand_dims` để tăng kích thước của mảng hiện có của bạn. + + +```python +arr = np.array([1, 2, 3, 4, 5, 6]) +arr.ndim +``` + + + + + 1 + + + + +```python +arr1 = np.expand_dims(arr, axis=1) +arr1.ndim +``` + + + + + 2 + + + + +```python +arr2 = arr[np.newaxis, :] +arr2.ndim +``` + + + + + 2 + + + + +```python +arr.reshape(2,3) +``` + + + + + array([[1, 2, 3], + [4, 5, 6]]) + + + +### 2.3. Index & Slicing + +![](https://media.springernature.com/original/springer-static/image/chp%3A10.1007%2F978-1-4842-4246-9_2/MediaObjects/332789_2_En_2_Fig1_HTML.png) + + +```python +arr = np.linspace(1,16,16).reshape(4,4) # tạo 1 mảng +arr +``` + + + + + array([[ 1., 2., 3., 4.], + [ 5., 6., 7., 8.], + [ 9., 10., 11., 12.], + [13., 14., 15., 16.]]) + + + +**Đố vui**: Trả về giá trị 5 và giá trị 15 trong `arr` + + +```python +arr[[1,3],[0,3]] +``` + + + + + array([ 5., 16.]) + + + +### 2.4. Tính toán với NumPy +#### 2.4.1 Tính toán ma trận + + +```python +arr1 = np.array([[1,2], + [2,3]]) +arr2 = np.array([[10,10], + [1,1]]) +``` + + +```python +arr1 + arr2, arr1 - arr2 +``` + + + + + (array([[11, 12], + [ 3, 4]]), + array([[-9, -8], + [ 1, 2]])) + + + + +```python +arr1 * arr2, arr1 / arr2, arr1 @ arr2 #element-wise vs matrix multiplication +``` + + + + + (array([[10, 20], + [ 2, 3]]), + array([[0.1, 0.2], + [2. , 3. ]]), + array([[12, 12], + [23, 23]])) + + + + +```python +arr1 + 5 +``` + + + + + array([[6, 7], + [7, 8]]) + + + +#### 4.2 Xác xuất thông kê + +NumPy hỗ trợ 1 số hàm cơ bản để ta tính toán thống kê dữ liệu bao gồm: +- `mean`, `median` +- `min`, `max` +- `std` +- `count_nonzero`, `unique` + + +```python +arr = np.linspace(1,12,12,dtype='int').reshape(3,4) +arr +``` + + + + + array([[ 1, 2, 3, 4], + [ 5, 6, 7, 8], + [ 9, 10, 11, 12]]) + + + + +```python +np.mean(arr), np.median(arr) +``` + + + + + (6.5, 6.5) + + + + +```python +np.max(arr), np.min(arr), np.std(arr) +``` + + + + + (12, 1, 3.452052529534663) + + + + +```python +np.sum(arr), np.sum(arr, axis=0) +``` + + + + + (78, array([15, 18, 21, 24])) + + + + +```python +np.count_nonzero(arr) +``` + + + + + 12 + + + + +```python +np.unique(arr) +``` + + + + + array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) + + + + +```python +np.unique(arr, return_counts = True) # tần suất xuất hiện của mỗi phân tử unique +``` + + + + + (array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), + array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])) + + + +Ngoài ra, ta có thể tận dùng hàm `where()` của NumPy như một phương pháp vector hóa để viết các câu lệnh if-else. + + +```python +arr = np.arange(10) +np.where(arr < 5, arr, 10*arr) +``` + + + + + array([ 0, 1, 2, 3, 4, 50, 60, 70, 80, 90]) + + + + +```python +np.where(arr < 5, '<5', '>=5') +``` + + + + + array(['<5', '<5', '<5', '<5', '<5', '>=5', '>=5', '>=5', '>=5', '>=5'], + dtype='5')) +``` + + + + + array(['<5', '<5', '<5', '<5', '<5', '=5', '>5', '>5', '>5', '>5'], + dtype=' 10) +tips_df[condition1] # cách 1 +``` + + +```python +tips_df.loc[condition1] # cách 2 +``` + + +```python +tips_df.query("sex == 'Male' & total_bill > 10") # cách 3 +``` + +2. Lọc tất cả các bản ghi chứa thông tin của khách hàng không hút thuốc và dùng suất ăn cho 3 người trở lên. + + +```python +condition2 = (tips_df['smoker'] == 'No') & (tips_df['size'] >=3) +tips_df[condition2] # cách 1 +``` + + +```python +tips_df.loc[condition2] # cách 2 +``` + + +```python +tips_df.query("smoker == 'No' & size >= 3") # cách 3 +``` + +3. Lọc tất cả các bản ghi chứa thông tin của khách hàng  +nữ ăn tối tại nhà hàng vào cuối tuần (Thứ 7, Chủ nhật) và tip cho nhân viên  +từ 5 đô trở lên. + + +```python +condition3 = (tips_df['sex'] == "Female") & (tips_df['time'] == "Dinner") & ((tips_df['day'] == "Sat") | (tips_df['day'] == "Sun")) & (tips_df['tip'] >=5) +tips_df[condition3] # cách 1 +``` + + +```python +tips_df.loc[condition3] # cách 2 +``` + + +```python +tips_df.query("(sex == 'Female') & (time == 'Dinner') & ((day == 'Sat') | (day == 'Sun')) & (tip >= 5)") # cách 3 +``` + +### 2.3. Tổng hợp dữ liệu (Aggregation) + +#### 2.3.1 Groupby() + +Trong Python, `groupby()` là một hàm linh hoạt trong Python cho phép bạn chia dữ liệu thành các nhóm riêng biệt để thực hiện các phép tính nhằm phân tích tốt hơn, cũng hoàn toàn tương tự như groupby trong SQL. + +`groupby()` được xây dựng theo cơ chế 3 bước: +- **Split**: Chia dữ liệu thành các nhóm dựa trên một số tiêu chí (ví dụ theo cột `x` trong ảnh mô tả ở dưới) +- **Apply**: Áp dụng một số phép tính toán cho từng nhóm một cách độc lập (ví dụ tính tổng các giá trị của từng nhóm trong cột `x`). Bước này có thể là 1 trong các thao tác dưới đây: + - Tổng hợp (Aggregation/Reduction): đưa ra 1 số phân tích thống kê trong một nhóm như sum, mean, std, min, max, size, count ... của nhóm dữ liệu. + - Chuyển đổi (Transformation): thực hiện một số tính toán nhóm như chuẩn hoá, lấp đầy các giá trị null ... + - Lọc (Filteration): loại bỏ một số nhóm, ... +- **Combine**: Kết hợp các kết quả vào một cấu trúc dữ liệu. + +![](https://blog.dask.org/images/split-apply-combine.png) + +Pandas hỗ trợ 13 chức năng tổng hợp sử dụng sau `groupby()` sau: + +|Tên hàm| Ý nghĩa| +|-|-| +|`mean()` | Tính trung bình của các nhóm| +|`sum()` | Tính tổng các giá trị của nhóm| +|`size()` | Tính kích thước nhóm| +|`count()`| Tính toán số lượng nhóm| +|`std()` | Độ lệch chuẩn của các nhóm| +|`var()` | Tính toán phương sai của các nhóm| +|`sem()` | Sai số chuẩn của giá trị trung bình của các nhóm| +|`describe()`| Tạo thống kê mô tả| +|`first()`| Tính toán giá trị đầu tiên của nhóm| +|`last()` | Tính giá trị cuối cùng của nhóm| +|`nth()` | Lấy giá trị thứ n hoặc một tập hợp con nếu n là một danh sách| +|`min()` | Tính toán giá trị nhỏ nhất nhóm| +|`max():` | Tính toán giá trị lớn nhất nhóm| + +#### **Thực hành**: Tính trung bình số tiền mỗi giới tính trả khi ăn tại nhà hàng theo thống kê trong `tips_df`. + + +```python +tips_df.groupby('sex').total_bill.mean() # riêng hoá đơn +``` + + +```python +tips_df['total_bill_tip'] = tips_df['total_bill'] + tips_df['tip'] #cả hoá đơn và tips +tips_df.groupby('sex').total_bill_tip.mean() +``` + +#### **Thực hành**: Tính số tiền lớn nhất, nhỏ nhất và trung bình hoá đơn và số tiền tips mỗi giới tính chi ra cho 1 bữa ăn tại nhà hàng theo thống kê trong `tips_df`. + + +```python +tips_df.groupby('sex').agg({'total_bill':['mean','min','max'],'tip':['mean','min','max']}) +``` + + +```python +# Ta có thể đổi tên cột bằng cách dưới đây +tips_df.groupby('sex').agg(mean_total_bill=('total_bill','mean'), mean_total_tip=('tip','mean')) +``` + +#### **Thực hành**: Tính số tiền trung bình trong hoá đơn và tips mỗi giới tính khi có hút thuốc và không hút thuốc chi ra cho 1 bữa ăn tại nhà hàng theo thống kê trong `tips_df`. + + +```python +tips_df.groupby(['sex','smoker'])[['total_bill','tip']].mean() +``` + +#### **Thực hành**: Tính số tiền trung bình trong hoá đơn và tips mỗi giới tính khi có hút thuốc và không hút thuốc chi ra cho 1 bữa ăn tối tại nhà hàng theo thống kê trong `tips_df`. + + +```python +tips_df[tips_df['time'] == 'Dinner'].groupby(['sex','smoker'])[['total_bill','tip']].mean() +``` + +#### **Bài tập**: +1. Tổng doanh thu của nhà hàng thời gian dùng bữa của khách hàng theo số liệu thống kế trong `tips_df`. +2. Tổng doanh thu không tính tips của nhà hàng theo thời gian dùng bữa với riêng khách hàng không hút thuốc vào cuối tuần (thứ 7, chủ nhật). + +#### **Bài giải**: +1. Tổng doanh thu của nhà hàng thời gian dùng bữa của khách hàng theo số liệu thống kế trong `tips_df`. + + +```python +tips_df.groupby('time').total_bill_tip.sum() +``` + + +2. Tổng doanh thu không tính tips của nhà hàng theo thời gian dùng bữa với riêng khách hàng không hút thuốc vào cuối tuần (thứ 7, chủ nhật). + + +```python +tips_df[(tips_df['smoker'] == 'No') & ((tips_df['day'] == 'Sat') | (tips_df['day'] == 'Sun'))].groupby('time').total_bill.sum() +``` + +#### 2.3.2 Pivot + +Nếu như bạn đã từng làm việc với Excel chắc hẳn không lạ gì với Pivot Table. Tương tự như Pivot Table trong Excel, Pandas hỗ trợ ta tổng hợp, trích lọc, phân tích dữ liệu dễ dàng và nhanh chóng với `pivot_table()`. + +`pivot_table()` khá tương đồng với `groupby()` bởi cùng theo nguyên lý split-apply-combine giống nhau, tuy nhiên dữ liệu sẽ được phân tích và tổng hợp dưới với `pivot_table()` đa chiều (mulitdimensional) chứ không phải là một chiều như `groupby()`. + +Một số tham số cần lưu ý: +- data: DataFrame +- values (optional): cột để tổng hợp +- index: cột, Group hoặc mảng. Nếu mảng được truyền vào thì phải có độ dài bằng với dữ liệu +- columns: cột, Group hoặc mảng. Tương tự như index +- aggfunc: (default: mean): hàm tổng hợp dữ liệu +- fill_value: giá trị được điền vào các ô dữ liệu NA (sau khi đã tính toán) +- margins: (bool, default: False). Thêm một cột tính tất cả các giá trị của các cột còn lại theo hàm tổng hợp +- margins_name (string, default: 'All'): Tên của cột margins +- dropna: (bool, default: False). Bỏ đi các hàng có chứa NA +- observed: (bool, default: False). Chỉ áp dụng nếu tất cả các nhóm dữ liệu đều là Categoricals.Nếu là True: chỉ hiển thị các giá trị quan sát được cho các nhóm phân loại. Nếu là False: hiển thị tất cả các giá trị cho các nhóm phân loại. + +#### **Thực hành**: Tính trung bình số tiền mỗi giới tính trả khi ăn tại nhà hàng theo thống kê trong `tips_df`. + + +```python +tips_df.pivot_table(values='total_bill_tip', index='sex', aggfunc='mean') +``` + +#### **Thực hành**: Tính tổng số tiền nhà hàng thu được không kể tips các ngày trong tuần theo bữa ăn được thống kê trong `tips_df`. + + +```python +tips_df.pivot_table(values='total_bill', index='day', columns='time',aggfunc='sum') +``` + +### 2.4. Hợp nhất dữ liệu (Merge, Concat) + +#### 2.4.1. Merge + +Pandas có đầy đủ tính năng, hiệu suất cao trong hoạt động in-memory join rất giống với SQL thông qua hàm `merge()`. Dưới đây là 1 số so sánh giữa phương pháp sử dụng trong Pandas và trong SQL và hình ảnh minh hoạ để có thể thấy rõ sự tương đồng này. + + +|Merge methods| SQL Join Name | Meaning| _merge| +|-|-|-|-| +|left| LEFT OUTER JOIN|Chỉ sử dụng keys bên trái|left_only| +|right| RIGHT OUTER JOIN|Chỉ sử dụng keys bên phải|right_only| +|outer|FULL OUTER JOIN|Sử dụng keys của cả 2 dataframes|both| +|inner|INNER JOIN|Chỉ sử dụng keys giao nhau của 2 dataframes|both| + +![](https://lh3.googleusercontent.com/-n76c6dtr5sw/YBO5d-3PzGI/AAAAAAAAAh8/xiT6YIzePLEXArb8uU1f1vgg8JRXYeg8ACLcBGAsYHQ/image.png) + +Dưới đây là 1 số ví dụ giúp bạn làm quen với cách sử dụng hàm với các tham số trên. + + +```python +left = tips_df.head(6)[['total_bill','tip']].reset_index() +right = tips_df.loc[4:8][['tip','sex']].reset_index() +``` + + +```python +left.merge(right, on=['index','tip'], how='left') +``` + + +```python +left.merge(right, on=['index','tip'], how='right') +``` + + +```python +left.merge(right, on=['index','tip'], how='inner') +``` + + +```python +left.merge(right, on=['index','tip'], how='outer') +``` + +#### 2.4.2 Concat + +Trong thực tế, ta có thể làm việc với các dữ liệu có cấu trúc cùng 1 format (bao gồm các cột tương đồng nhau) nhưng được chia nhỏ thành các file khác nhau. Để có thể thuận lợi đánh giá được dữ liệu 1 cách tổng quan nhất, ta cần nối các dữ liệu có cấu trúc này với nhau và Pandas hỗ trợ ta đắc lực thông qua hàm `pd.concat()`. Dưới đây là 1 ví dụ về cách thức hàm hoạt động. + + +```python +pd.concat([tips_df.head(3),tips_df.tail(3), tips_df.sample(3)[['total_bill','tip','sex']]]) +``` + +Trong bài sau, chúng ta sẽ cùng nhau tìm hiểu cách trực quan hoá dữ liệu thông qua 1 số thư viện phổ biến như Matplotlib, Seaborn. diff --git a/docs/tutorial/python/09.visualization.md b/docs/tutorial/python/09.visualization.md new file mode 100644 index 00000000..5566b79a --- /dev/null +++ b/docs/tutorial/python/09.visualization.md @@ -0,0 +1,527 @@ +# Bài 7: Trực quan hoá dữ liệu với Matplotlib và Seaborn + +## 1. Giới thiệu + + +Bất kì 1 thư viện nào sử dụng cho mục đích biểu diễn dữ liệu cũng sẽ theo anatomy sau. +![](https://matplotlib.org/stable/_images/sphx_glr_anatomy_001.png) + +Đây cũng là những điểm mình cần biết khi vẽ đồ thị với các thư viện trong Python. + +### 1.1. Thư viện Matplotlib + +Matplotlib (cụ thể trong bài học này ta quan tâm tới gói `Matplotlib.pyplot` hay được viết gọn là `plt`) là một gói Python được sử dụng cho đồ họa 2D. + +Matplotlib có 2 objects chính: +- `Figure`: chứa nhiều axes +- `Axes`: nơi vẽ đồ thị, thực tế chính là cái đồ thị + +Nói 1 cách dễ hiểu, `Figure` giống khung ảnh và 1 khung ảnh có thể không chứa hoặc chứa 1 đến nhiều bức ảnh, và `Axes` tương ứng với những bức ảnh trong cái khung ảnh. + +Các thành phần khác: +- `Title`: tên của đồ thị +- `X axis label`,`Y axis label`: 2 trục (ngang, đứng) của đồ thị +- `Major tick label`, `Minor tick label`: Tick chính chứa con số, tick nhỏ có thể hoặc không chứa con số +- `Legend`: Chú thích +- `Grid`: Lưới (dễ căn chỉnh) +- `Line plot`: Đồ thị đường +- `Scatter plot`: Đồ thị phân tán +- `Marker`: Những chấm tròn biểu diễn điểm dữ liệu trong đồ thị phân tán +- `Axis spine`: Đường ghi nhận ranh giới khu vực dữ liệu +- `Spine`: Đường kết nối các dấu ticks trên trục và ghi nhận ranh giới của khu vực dữ liệu. + +**Tham khảo**: [Thư viện Matplotlib](https://matplotlib.org/stable/gallery/index.html) + +### 1.2. Thư viện Seaborn + +Seaborn là thư viện mở rộng được viết trên nền Matplotlib, từ đó kế thừa các chức năng biểu diễn dữ liệu từ Matplotlib. Vì vậy, những gì Seaborn làm được thì Matplotlib cũng làm được tuy nhiên không có chiều ngược lại, cũng có nghĩa là Seaborn không thể thay thế hoàn toàn được Matplotlib (Seaborn hiểu đơn giản là 1 đứa con được Matplotlib tạo ra). + +**Tham khảo**: [Thư viện Seaborn](https://seaborn.pydata.org/examples/index.html) + +Mỗi thư viện đều có những ưu điểm riêng: Nếu Seaborn sử dụng ít cú pháp hơn và có các chủ đề mặc định tuyệt đẹp thì Matplotlib dễ dàng tùy chỉnh hơn thông qua việc truy cập các lớp. Và trong bài viết này, chúng ta sẽ cùng nhau khám phá vẻ đẹp của 2 thư viện. + +### 1.3. Cài đặt +- Cài đặt khi tạo môi trường ảo: + +`conda create -n hanh python=3.7 matplotlib seaborn` + +- Cài đặt sau khi tạo xong môi trường ảo: + +`conda install matplotlib` hoặc `pip install matplotlib`, + +`conda install seaborn` hoặc `pip install seaborn` + +### 1.4. Import + +```python +import matplotlib.pyplot as plt +import seaborn as sns +``` + +Để bắt đầu buổi học hôm nay, chúng ta sẽ import một số thư viện phục vụ cho buổi học: + + +```python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +``` + +## 2. Các kiểu trực quan hoá dữ liệu cơ bản + +### 2.1. Đồ thị đường + +Biểu đồ đường được sử dụng để theo dõi những thay đổi trong khoảng thời gian ngắn và dài. + +#### 2.1.1. Đồ thị 1 đường + +#### **Thực hành**: Vẽ đồ thị hình sin của 100 điểm trong khoảng từ -10 đến 10. + +**Gợi ý**: Cho trước giá trị x, y. Sử dụng hàm `plot()` để vẽ đồ thị đường dưới đây + +```python +x = np.linspace(-10,10,100) +y = np.sin(x) +``` +![](./visualization/line_plot.png) +#### **Sử dụng Matplotlib**: + + +```python +x = np.linspace(-10,10,100) +y = np.sin(x) + +# Mặc định plt.plot() là đồ thị đường +plt.plot(x,y,linestyle='-.') # linestyle để thay đổi đường biểu diễn, ví dụ: linestyle=(0,(3,1,1,1)) tương ứng linestyle='-.' +plt.title('Sin function') # Tên của đồ thị +plt.xlabel('X value') # Tên trục hoàng +plt.ylabel('Sin of x') # Tên trục tung +plt.grid() # Tạo lưới +plt.show() # Hiển thị biểu đồ +``` + +Tham khảo 1 số cách thay đổi linestyle tại [đây](https://matplotlib.org/stable/_images/sphx_glr_linestyles_001.png). + +#### **Sử dụng Seaborn**: + + +```python +df = pd.DataFrame({'x':x,'sin':y}) +sns.lineplot(x=df['x'], y=df['sin'],linestyle='-.') +plt.grid() +plt.show() +``` + +#### 2.1.2 Đồ thị đa đường + +#### **Thực hành**: Vẽ đồ thị hình sin và hình cos của 100 điểm trong khoảng từ -10 đến 10 trên cùng 1 đồ thị. + +**Gợi ý**: Cho trước giá trị x, y. Sử dụng hàm `plot()` để vẽ đồ thị đường dưới đây: + +```python +x = np.linspace(-10,10,100) +sin = np.sin(x) +cos = np.cos(x) +``` + +![](./visualization/multiline_plot.png) + + +```python +x = np.linspace(-10,10,100) +sin = np.sin(x) +cos = np.cos(x) +df = pd.DataFrame({'x':x,'sin': sin, 'cos':cos}) +``` + +**Cách 1** + + +```python +# truyền tham số c để thay đổi màu +plt.plot(x, sin, label = 'Sin', c='red') +plt.plot(x, cos, label = 'Cos', c='blue') +plt.title('Function plot') +plt.xlabel('X value') +plt.ylabel('Y value') +plt.legend(loc='best') # có thể tinh chỉnh 4 góc +plt.grid() +plt.show() +``` + +**Cách 2**: +```python +ax.set_xlabel('X Label') +ax.set_ylabel('Y Label') +ax.set_title('Function plot') +``` +có thể được viết gọn trong 1 dòng với hàm `set()` và truyền tham số tương ứng vào bên trong như sau: +```python +ax.set(xlabel='X_label', ylabel='Y_label', title='Function plot') +``` + + +```python +ax = plt.axes() # định nghĩa nơi vẽ đồ thị +ax.plot(x,sin,label='Sin', c = 'red') +ax.plot(x,cos,label='Cos', c= 'blue') +ax.set(xlabel='X value', ylabel='Y value', title='Function plot') +ax.legend(loc='best') +ax.grid() +plt.show() +``` + +#### **Thực hành**: Vẽ đồ thị hình sin và hình cos của 100 điểm trong khoảng từ -10 đến 10 trên 2 đồ thị cạnh nhau như hình sau: +![](./visualization/paraline_plot.png) + +**Cách 1** + + +```python +plt.figure(figsize=(15,5)) # chiều dài 15, chiều rộng 5 + +plt.subplot(1,2,1) #1 hàng 2 cột ở axis 1 +plt.plot(x,sin) +plt.title('Sin Function') +plt.xlabel('X value') +plt.ylabel('Y value') + +plt.subplot(1,2,2) +plt.plot(x,cos,c='red') +plt.title('Cos Function') +plt.xlabel('X value') +plt.ylabel('Y value') +plt.show() +``` + +**Cách 2** + + +```python +plt.figure(figsize=(15,5)) + +ax1 = plt.subplot(1,2,1) +ax2 = plt.subplot(1,2,2) + +ax1.plot(x,sin) +ax2.plot(x,cos,c='red') + +ax1.set(xlabel='X_label', ylabel='Y_label', title='Sin Function') +ax2.set(xlabel='X_label', ylabel='Y_label', title='Cos Function'); + +plt.show() +``` + +**Cách 3** + + +```python +fig, ax = plt.subplots(1,2, figsize=(15,5)) +ax[0].plot(x,sin) +ax[1].plot(x,cos,c='red'); +plt.show() +``` + +### 2.2. Đồ thị phân phối (Histogram/Distribution) + +Hist/Dist plot đếm theo bins, bins được tính bằng cách lấy max và min rồi của dữ liệu rồi chia đều thành số bins mình muốn. Số bins càng lớn, chia khoảng càng nhỏ thì các chính xác! + +Để giúp các bạn dễ dàng hình dung cách sử dụng các đồ thị vào bài toán dữ liệu thực tế, chúng ta sẽ cùng nhau thực hành các kiểu đồ thị trên tập dữ liệu `iris`. + + +```python +iris = sns.load_dataset('iris') +``` + +#### **Thực hành**: Vẽ đồ thị phân phối độ dài cánh hoa tương ứng cột `petal_length`. + +#### **Sử dụng Matplotlib**: + +![](./visualization/dist_plot.png) + + +```python +plt.hist(iris['petal_length'], bins = 10) +plt.show() +``` + +#### **Sử dụng Seaborn**: + +![](./visualization/dist_plot1.png) + + +```python +sns.histplot(iris, x = 'petal_length', bins = 10) +plt.show() +``` + +Ngoài ra, ta có thể sử dụng hàm `distplot()` như ví dụ dưới đây: + + +```python +sns.displot(iris['petal_length'], bins = 10) +plt.show() +``` + +### 2.3. Đồ thị phân tán (Scatter plot) + +Biểu đồ hay đồ thị phân tán là một đồ thị biểu hiện mối tương quan giữa nguyên nhân và kết quả hoặc giữa các yếu tố ảnh hưởng đến chất lượng. + +#### **Thực hành**: Vẽ đồ thị phân tán cánh hoa và đài hoa. + +#### **Sử dụng Matplotlib**: + +![](./visualization/scatter_plt.png) + + +```python +plt.scatter(iris.petal_length, iris.petal_width, label='Petal') +plt.scatter(iris.sepal_length, iris.sepal_width, marker='*', label = 'Sepal') +plt.legend() +plt.show() +``` + +#### **Sử dụng Seaborn**: + +![](./visualization/scatter_sns.png) + + +```python +sns.scatterplot(data=iris, x='petal_length', y='petal_width', label='Petal') +sns.scatterplot(data=iris, x='sepal_length', y='sepal_width', marker='*', label = 'Sepal') +plt.legend() +plt.show() +``` + +#### **Thực hành**: Vẽ đồ thị phân tán độ dài và độ rộng đài hoa tương ứng cột `petal_length`. + +#### **Sử dụng Matplotlib**: + +![](./visualization/scatter_plt_classify.png) + + +```python +plt.scatter(iris[iris['species'] == 'setosa'].petal_length, iris[iris['species'] == 'setosa'].petal_width, label='setosa') +plt.scatter(iris[iris['species'] == 'versicolor'].petal_length, iris[iris['species'] == 'versicolor'].petal_width, label='versicolor') +plt.scatter(iris[iris['species'] == 'virginica'].petal_length, iris[iris['species'] == 'virginica'].petal_width, label='virginica') +plt.legend() +plt.show() +``` + +#### **Sử dụng Seaborn**: + +![](./visualization/scatter_sns_classify.png) + + +```python +sns.scatterplot(data=iris, x='petal_length', y='petal_width', hue='species', palette = ['red','blue','brown']) +plt.show() +``` + +### 2.4. Đồ thị hộp (Box plot) + +#### **Thực hành**: Vẽ biểu đồ hộp độ dài đài hoa tương ứng cột `petal_length`. + +#### **Sử dụng Matplotlib**: + +![](./visualization/boxplot_plt.png) + + +```python +plt.boxplot(iris.petal_length, vert=False) +plt.show() +``` + +#### **Sử dụng Seaborn**: + +![](./visualization/boxplot_sns.png) + + +```python +sns.boxplot(data=iris,x='petal_length', width=0.2) +plt.show() +``` + +#### **Thực hành**: Vẽ biểu đồ hộp các cột dữ liệu trong `iris`. + +#### **Sử dụng Matplotlib**: + +![](./visualization/boxplot_plt1.png) + + +```python +plt.boxplot([iris.petal_length,iris.petal_width,iris.sepal_length,iris.sepal_width]) +plt.show() +``` + +#### **Sử dụng Seaborn**: + +![](./visualization/boxplot_sns1.png) + + +```python +sns.boxplot(data=iris, width=0.8) +plt.show() +``` + +Ngoài ra, chúng ta có 1 vài hàm biến thể nhằm trực quan hoá mô tả phân bố dữ liệu, tham khảo 1 số hàm plot dưới đây: + +**Boxenplot** + +![](./visualization/boxenplot.png) + + +```python +sns.boxenplot(data=iris) #Thêm số lượng bins/điểm +plt.show() +``` + +**Violin plot** + +![](./visualization/violinplot.png) + + +```python +sns.violinplot(data=iris) #Thêm phân bố bên ngoài +plt.show() +``` + +Từ những đồ thị hộp trên, ta có thể đưa ra 1 số phân tích, ví dụ như sau: +- Độ rộng cánh hoa (petal) và đài hoa (sepal) hẹp hơn độ dài +- Độ rộng cánh hoa phân tán hơn độ rộng đài hoa +- Các điểm dữ liệu tập trung trong khoảng percentile 50 nhiều hay ít +- Có outlier ở độ rộng đài hoa +... + +#### **Lưu ý: Một số thuật ngữ hay gặp** + +- Decile: chia dữ liệu thành 10 phần bằng nhau. +- Percentile: phần trăm vị trí xếp hạng (25%, 50%, 75%) +- Quantile: xếp hạng giá trị trong bộ dữ liệu tại phần trăm percentile (i.e ở 50% có 50% số điểm dữ liệu lớn hơn và 50% số điểm dữ liệu bé hơn), thường chia dữ liệu thành 4 phần bằng nhau: [min-25%, 25%-50%, 50%-75%, 75%-max] + +![](https://chidokun.github.io/images/post/box-and-whisker-plot/1.png) + +### 2.5. Biểu đồ tròn (Pie chart) + + +```python +tips = sns.load_dataset('tips') +``` + +#### **Thực hành**: Vẽ biểu đồ tròn thể hiện giới tính nam và nữ chiếm bao nhiêu phần trăm trong tổng hoá đơn. + +![](./visualization/pie.png) + +#### **Sử dụng Matplotlib**: + + +```python +sex = tips.groupby('sex').total_bill.sum() +plt.pie(sex, labels = sex.index, autopct='%.2f%%') +plt.show() +``` + +#### **Sử dụng Seaborn**: +Seaborn thì không hỗ trợ trực tiếp **pie chart**, ta có thể sử dụng **bar plot chart** thay thế. + +![](./visualization/bar.png) + + +```python +sex1 = sex.reset_index() #chuyển về DataFrame +sns.barplot(data=sex1, x='sex', y='total_bill') +plt.show() +``` + +#### **Thực hành**: Vẽ biểu đồ tròn thể hiện giới phần trăm tổng hoá đơn tiêu thụ của nam theo ngày. + +![](./visualization/pie1.png) + +**Cách 1** + + +```python +male = tips[tips['sex'] == 'Male'].groupby('day').total_bill.sum() +plt.figure(figsize=(6,6)) +plt.pie(male, labels = male.index, autopct='%.2f%%', explode=[0,0.1,0,0]) +plt.show() +``` + +**Cách 2** + + +```python +male.plot(kind='pie', autopct='%.2f%%', explode=[0,0.1,0,0], figsize=(6,6)) +plt.show() +``` + +#### **Bài tập về nhà**: Vẽ biểu đồ tròn thể hiện giới tính nam và nữ chiếm bao nhiêu phần trăm trong tổng hoá đơn phía bên trái và biểu đồ tròn thể hiện tổng hoá đơn tiêu thụ theo ngày phía bên phải như hình dưới đây. + +![](./visualization/joint_pie.png) + +### 2.6. Một số biểu đồ tương quan phức hợp + +#### 2.6.1 FacetGrid + +#### **Thực hành**: Vẽ biểu đồ phân tán thể hiện mối tương quan giữa tổng hoá đơn và tiền tips như hình dưới đây. + +![](./visualization/facetgrid.png) + + +```python +g = sns.FacetGrid(data=tips,col='sex',row='smoker',hue='time') #hue cho mã màu, mã màu thay đổi dựa theo time +g.map(sns.scatterplot,'total_bill','tip') +plt.legend() +plt.show() +``` + +#### 2.6.2 Swarm plot + +![](./visualization/swarm.png) + + +```python +sns.swarmplot(data=tips,x='time',y='total_bill',hue='smoker') #biến rời rạc vs biến liên tục +plt.show() +``` + +#### 2.6.3 Catplot + +![](./visualization/swarm.png) + + +```python +sns.catplot(data=tips,x='time',y='total_bill',hue='smoker', kind='swarm'); +plt.show() +``` + +![](./visualization/violin1.png) + + +```python +sns.catplot(data=tips,x='time',y='total_bill',hue='smoker', kind='violin') +plt.show() +``` + +![](./visualization/violin2.png) + + +```python +sns.catplot(data=tips,x='time',y='total_bill',row='sex', col='smoker',kind='violin') +plt.show() +``` + +#### 6.4 Pairplot + +Khi muốn nhìn tổng quan dữ liệu và mối tương quan giữa các chiều dữ liệu theo từng cặp với nhau, thì pair plots là lựa chọn vô cùng hoàn hảo. Ta có thể thử nghiệm đơn giản với các cột dữ liệu của tập `iris`. + +![](./visualization/pairplot.png) + + +```python +sns.pairplot(iris, hue='species', height=1.5) +plt.show() +``` + +Trong bài học tiếp theo, chúng ta sẽ cùng nhau tìm hiểu về Học máy, các bài toán trong Học máy và tiếp cận đến loại bài toán đầu tiên trong Học máy, đó là Học có giám sát. diff --git a/docs/tutorial/python/10.ml.ipynb b/docs/tutorial/python/10.ml.ipynb new file mode 100644 index 00000000..cc90dc25 --- /dev/null +++ b/docs/tutorial/python/10.ml.ipynb @@ -0,0 +1,117 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e48e4783-3013-4b8e-8653-b1db847e1741", + "metadata": {}, + "source": [ + "# Bài 8: Làm quen với Học máy" + ] + }, + { + "cell_type": "markdown", + "id": "71c8ded8-9527-4c5b-ad9f-2940de08854e", + "metadata": {}, + "source": [ + "## I. GIỚI THIỆU VỀ HỌC MÁY\n", + "\n", + "### 1. Lịch sử phát triển\n", + "\n", + "![](https://blogs.nvidia.com/wp-content/uploads/2016/07/Deep_Learning_Icons_R5_PNG.jpg.png)\n", + "\n", + "Alan Turing, có thể coi là cha đẻ của ngành trí tuệ nhân tạo, một bộ não cực kì vĩ đại, người được xem là đã cứu hàng ngàn sinh mạng vô tội trong Thế chiến thứ 2 trước quân phát xít Đức khi \"bẻ khóa\" thành công các thông điệp của quân đội Đức được mã hóa bởi Enigma. Công trình nghiên cứu của Alan có thể coi là khởi nguồn cho ngành AI những năm 1950. \n", + "\n", + "Học máy mới được ra đời những năm 1980 nhờ những phát hiện, những nghiên cứu sâu hơn trong thuật toán và từ 2010 trở lại đây, Học sâu (deep learning) bắt đầu phát triển và bùng nổ với khái niệm mô hình học máy trên mạng neurons. Học sâu cũng chính là công nghệ lõi đằng sau những sản phẩm săn đón hiện thời như xe tự hành Tesla, chấm điểm công dân Trung Quốc, hay những sản phẩm trong đời sống hàng ngày của mình Siri, Google Translate, ...\n", + "\n", + "### 2. Học máy là gì?\n", + "\n", + "Học máy là một tập hợp các phương pháp mà máy tính sử dụng để thực hiện và cải thiện các dự đoán hoặc hành vi dựa trên dữ liệu.\n", + "> A set of methods that computers use to make and improve predictions or behaviors based on data.\n", + "\n", + "Nhiều người cho rằng Học máy cũng không phải là cái gì cao siêu cả, nó chỉ đơn thuần là tập hợp các câu lệnh if - else nhưng gọi bằng cái tên nghe bắt tai vậy thôi? Dưới đây là 1 hình ảnh so sánh vui để giúp các bạn hiểu sự khác biệt giữa 1 chương trình phần mềm thông thường và chương trình học máy.\n", + "\n", + "![](https://christophm.github.io/interpretable-ml-book/images/programing-ml.png)\n", + "\n", + "- Với những code bình thường, thì ta cần cho máy tính những hướng dẫn cụ thể (nếu trời mưa thì phải mang ô, nếu trời mưa thì phải kéo quần áo vào trong nhà, ...)\n", + "\n", + "- Với Học máy, ta chỉ cần đưa vào dữ liệu có nhãn hoặc không có nhãn thì Học máy sẽ suy ra cho ta những cái mối quan hệ, những cái chỉ dẫn để giải quyết vấn đề.\n", + "\n", + "|Input|Types|Output|\n", + "|-|-|-|\n", + "|Data, Rules|Classical Programming|Answers|\n", + "|Data (with or without Answers)|Machine Learning|Rules|\n", + "\n", + "Tham khảo các khoá học về Học máy và Học sâu tại: \n", + "- [Machine Learning](https://www.coursera.org/learn/machine-learning)\n", + "- [Deep Learning](https://www.coursera.org/specializations/deep-learning)\n", + "\n", + "## II. CÁC KIỂU BÀI TOÁN TRONG HỌC MÁY\n", + "\n", + "![](https://www.7wdata.be/wp-content/uploads/2020/06/1FUZS9K4JPqzfXDcC83BQTw.png)\n", + "\n", + "### 1. Học máy có giám sát (Supervised Learning)\n", + "\n", + "Học máy có giám sát là phương pháp sử dụng những dữ liệu đã được gán nhãn từ trước để luận suy ra mối quan hệ giữa dữ liệu đầu vào và kết quả đầu ra. Các dữ liệu này được gọi là dữ liệu huấn luyện và chúng là cặp các đầu vào - đầu ra. Học có giám sát sẽ xem xét các tập huấn luyện này để từ đó có thể đưa ra dự đoán đầu ra cho 1 đầu vào mới chưa gặp bao giờ. Về mặt toán học, ta có thể hiểu:\n", + "\n", + "- Ma trận X tương ứng tập mẫu (samples = examples = observations = records)\n", + "- Ma trận Y tương ứng tập nhãn\n", + "\n", + "Ví dụ, những bài toán có dữ liệu lịch sử và có nhãn cho từng dữ liệu có thể kể đến phần loại thư rác (spam/ not spam), chấm điểm tín dụng (good credit/ bad credit), ... \n", + "\n", + "Học máy có giám sát có thể chia thành 2 nhóm:\n", + "\n", + "- Bài toán phân loại (Classification) xác định 1 hoặc nhiều nhãn cho các mẫu: \n", + " - Phân loại nhị phân (Binary classification): ví dụ như khách hàng có gian lận hay không (fraud detection), khách hàng có ở lại không hay rời bỏ dịch vụ (customer retention vs customer churn),...\n", + " - Phân loại đa lớp (Multi-class classification): số lượng nhãn nhiều hơn 2, ví dụ như phân loại ảnh (image classification), nhìn vào ảnh chụp Xray chẩn đoán xem bệnh nhân có ung thư không (diagnostics)...\n", + " - Thuật toán: Logistic Regression, Decision Trees, SVM, Naive Bayes, K-NN, ...\n", + "\n", + "\n", + "- Bài toán hồi quy (Regression): ví dụ giá cổ phiểu ngày mai tăng lên hay giảm bao nhiều đồng, căn nhà ấy với diện tích ấy với số phòng ấy tại địa điểm ấy thì thị trường sẽ chấp nhận khoảng giá bao nhiêu để có thể giao bán, giá trị vòng đời khách hàng ...\n", + " - Thuật toán: Linear Regression, Ridge/Lasso Regression, ...\n", + " \n", + "### 2. Học máy phi giám sát (Unsupervised Learning)\n", + "\n", + "Khác với các bài toán học máy có giám sát, học phi giám sát sử dụng những dữ liệu chưa được gán nhãn từ trước để luận suy. Phương pháp này thường được sử dụng để tìm cấu trúc của tập dữ liệu tuy nhiên lại không có phương pháp đánh giá được cấu trúc tìm ra được là đúng hay sai. Ví dụ như phân cụm dữ liệu, triết xuất thành phần chính của một chất nào đó. Về mặt toán học, ta không có Y mà chỉ có ma trận X tương ứng các quan sát.\n", + "\n", + "Học máy phi giám sát có thể chia thành 2 nhóm:\n", + "\n", + "- Bài toán phân cụm (Clustering): bài toán này không bao giờ có 1 đáp án duy nhất, luôn luôn có nhiều đáp án, và thậm chí đáp án tối ưu cho năm nay sẽ khác đáp án tối ưu cho năm sau hoàn toàn, ...\n", + " - Thuật toán: K-Means, DBScan, Mean-Shift, ...\n", + "\n", + "- Bài toán giảm chiều dữ liệu (Dimension Reduction) sử dụng rất nhiều trong xử lý ảnh. \n", + " - Thuật toán: t-SNE, PCA, SVD, LDA, ...\n", + "\n", + "### 3. Học tăng cường (Reinforcement Learning)\n", + "\n", + "Học tăng cường có thể coi người em sinh sau đẻ muộn so với anh cả Học máy có giám sát và anh hai Học máy phi giám sát nhưng đây sẽ là tương lai của học máy sau này. Phương pháp học tăng cường tập trung vào việc làm sao để cho 1 tác tử trong môi trường có thế hành động sao cho lấy được phần thưởng nhiều nhất có thể. Khác với học có giám sát, nó không có cặp dữ liệu gán nhãn trước làm đầu vào và cũng không có đánh giá các hành động là đúng hay sai. Cơ chế của học tăng cường giống như khi ta huấn luyện 1 con khỉ, con khỉ làm đúng thì mình thưởng, sai thì mình tét mông và ở đây, ta sử dụng toán học để tạo ra những phần thưởng huấn luyện để con khỉ biết làm đúng theo mong muốn của mình. \n", + "\n", + "Một trong những thành tựu tiêu biểu của Học tăng cường có thể kể đến AlphaGo thắng trong cờ vua, Xe tự hành Tesla của Elon Musk... Các bạn có thể tham khảo 1 số thuật toán như Q-Learning, Deep Q-Network,... \n", + "\n", + "Học tăng cường được đánh giá sẽ là ngành mũi nhọn trong thời gian tới, tuy nhiên phần toán bên dưới cũng như code rất nặng và khó, hiện tại nhiều bên đang nghiên cứu nhưng thực sự ở thời điểm hiện tại ở Việt Nam chưa có 1 sản phẩm cụ thể nào đã đưa vào production.\n", + "\n", + "Trong bài tiếp theo, chúng ta sẽ cũng nhau thực hành làm quen với bài toán học máy có giám sát sử dụng scikit-learn API, một thư viện phổ biến khi làm việc với học máy ở tầm sơ và trung cấp." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorial/python/10.ml.md b/docs/tutorial/python/10.ml.md new file mode 100644 index 00000000..ecaeaa76 --- /dev/null +++ b/docs/tutorial/python/10.ml.md @@ -0,0 +1,117 @@ +# Bài 8: Làm quen với Học máy + +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được học máy là gì và lịch sử phát triển +- [ ] Phân biệt được các loại bài toán trong học máy (Supervised, Unsupervised, Reinforcement Learning) +- [ ] Hiểu được sự khác biệt giữa Classification và Regression +- [ ] Nắm được các thuật toán cơ bản cho từng loại bài toán + +## I. GIỚI THIỆU VỀ HỌC MÁY + +### 1. Lịch sử phát triển + +![](https://blogs.nvidia.com/wp-content/uploads/2016/07/Deep_Learning_Icons_R5_PNG.jpg.png) + +Alan Turing, có thể coi là cha đẻ của ngành trí tuệ nhân tạo, một bộ não cực kì vĩ đại, người được xem là đã cứu hàng ngàn sinh mạng vô tội trong Thế chiến thứ 2 trước quân phát xít Đức khi "bẻ khóa" thành công các thông điệp của quân đội Đức được mã hóa bởi Enigma. Công trình nghiên cứu của Alan có thể coi là khởi nguồn cho ngành AI những năm 1950. + +Học máy mới được ra đời những năm 1980 nhờ những phát hiện, những nghiên cứu sâu hơn trong thuật toán và từ 2010 trở lại đây, Học sâu (deep learning) bắt đầu phát triển và bùng nổ với khái niệm mô hình học máy trên mạng neurons. Học sâu cũng chính là công nghệ lõi đằng sau những sản phẩm săn đón hiện thời như xe tự hành Tesla, chấm điểm công dân Trung Quốc, hay những sản phẩm trong đời sống hàng ngày của mình Siri, Google Translate, ... + +### 2. Học máy là gì? + +Học máy là một tập hợp các phương pháp mà máy tính sử dụng để thực hiện và cải thiện các dự đoán hoặc hành vi dựa trên dữ liệu. +> A set of methods that computers use to make and improve predictions or behaviors based on data. + +Nhiều người cho rằng Học máy cũng không phải là cái gì cao siêu cả, nó chỉ đơn thuần là tập hợp các câu lệnh if - else nhưng gọi bằng cái tên nghe bắt tai vậy thôi? Dưới đây là 1 hình ảnh so sánh vui để giúp các bạn hiểu sự khác biệt giữa 1 chương trình phần mềm thông thường và chương trình học máy. + +![](https://christophm.github.io/interpretable-ml-book/images/programing-ml.png) + +- Với những code bình thường, thì ta cần cho máy tính những hướng dẫn cụ thể (nếu trời mưa thì phải mang ô, nếu trời mưa thì phải kéo quần áo vào trong nhà, ...) + +- Với Học máy, ta chỉ cần đưa vào dữ liệu có nhãn hoặc không có nhãn thì Học máy sẽ suy ra cho ta những cái mối quan hệ, những cái chỉ dẫn để giải quyết vấn đề. + +|Input|Types|Output| +|-|-|-| +|Data, Rules|Classical Programming|Answers| +|Data (with or without Answers)|Machine Learning|Rules| + +Tham khảo các khoá học về Học máy và Học sâu tại: +- [Machine Learning](https://www.coursera.org/learn/machine-learning) +- [Deep Learning](https://www.coursera.org/specializations/deep-learning) + +## II. CÁC KIỂU BÀI TOÁN TRONG HỌC MÁY + +![](https://www.7wdata.be/wp-content/uploads/2020/06/1FUZS9K4JPqzfXDcC83BQTw.png) + +### 1. Học máy có giám sát (Supervised Learning) + +Học máy có giám sát là phương pháp sử dụng những dữ liệu đã được gán nhãn từ trước để luận suy ra mối quan hệ giữa dữ liệu đầu vào và kết quả đầu ra. Các dữ liệu này được gọi là dữ liệu huấn luyện và chúng là cặp các đầu vào - đầu ra. Học có giám sát sẽ xem xét các tập huấn luyện này để từ đó có thể đưa ra dự đoán đầu ra cho 1 đầu vào mới chưa gặp bao giờ. Về mặt toán học, ta có thể hiểu: + +- Ma trận X tương ứng tập mẫu (samples = examples = observations = records) +- Ma trận Y tương ứng tập nhãn + +Ví dụ, những bài toán có dữ liệu lịch sử và có nhãn cho từng dữ liệu có thể kể đến phần loại thư rác (spam/ not spam), chấm điểm tín dụng (good credit/ bad credit), ... + +Học máy có giám sát có thể chia thành 2 nhóm: + +- Bài toán phân loại (Classification) xác định 1 hoặc nhiều nhãn cho các mẫu: + - Phân loại nhị phân (Binary classification): ví dụ như khách hàng có gian lận hay không (fraud detection), khách hàng có ở lại không hay rời bỏ dịch vụ (customer retention vs customer churn),... + - Phân loại đa lớp (Multi-class classification): số lượng nhãn nhiều hơn 2, ví dụ như phân loại ảnh (image classification), nhìn vào ảnh chụp Xray chẩn đoán xem bệnh nhân có ung thư không (diagnostics)... + - Thuật toán: Logistic Regression, Decision Trees, SVM, Naive Bayes, K-NN, ... + + +- Bài toán hồi quy (Regression): ví dụ giá cổ phiểu ngày mai tăng lên hay giảm bao nhiều đồng, căn nhà ấy với diện tích ấy với số phòng ấy tại địa điểm ấy thì thị trường sẽ chấp nhận khoảng giá bao nhiêu để có thể giao bán, giá trị vòng đời khách hàng ... + - Thuật toán: Linear Regression, Ridge/Lasso Regression, ... + +### 2. Học máy phi giám sát (Unsupervised Learning) + +Khác với các bài toán học máy có giám sát, học phi giám sát sử dụng những dữ liệu chưa được gán nhãn từ trước để luận suy. Phương pháp này thường được sử dụng để tìm cấu trúc của tập dữ liệu tuy nhiên lại không có phương pháp đánh giá được cấu trúc tìm ra được là đúng hay sai. Ví dụ như phân cụm dữ liệu, triết xuất thành phần chính của một chất nào đó. Về mặt toán học, ta không có Y mà chỉ có ma trận X tương ứng các quan sát. + +Học máy phi giám sát có thể chia thành 2 nhóm: + +- Bài toán phân cụm (Clustering): bài toán này không bao giờ có 1 đáp án duy nhất, luôn luôn có nhiều đáp án, và thậm chí đáp án tối ưu cho năm nay sẽ khác đáp án tối ưu cho năm sau hoàn toàn, ... + - Thuật toán: K-Means, DBScan, Mean-Shift, ... + +- Bài toán giảm chiều dữ liệu (Dimension Reduction) sử dụng rất nhiều trong xử lý ảnh. + - Thuật toán: t-SNE, PCA, SVD, LDA, ... + +### 3. Học tăng cường (Reinforcement Learning) + +Học tăng cường có thể coi người em sinh sau đẻ muộn so với anh cả Học máy có giám sát và anh hai Học máy phi giám sát nhưng đây sẽ là tương lai của học máy sau này. Phương pháp học tăng cường tập trung vào việc làm sao để cho 1 tác tử trong môi trường có thế hành động sao cho lấy được phần thưởng nhiều nhất có thể. Khác với học có giám sát, nó không có cặp dữ liệu gán nhãn trước làm đầu vào và cũng không có đánh giá các hành động là đúng hay sai. Cơ chế của học tăng cường giống như khi ta huấn luyện 1 con khỉ, con khỉ làm đúng thì mình thưởng, sai thì mình tét mông và ở đây, ta sử dụng toán học để tạo ra những phần thưởng huấn luyện để con khỉ biết làm đúng theo mong muốn của mình. + +Một trong những thành tựu tiêu biểu của Học tăng cường có thể kể đến AlphaGo thắng trong cờ vua, Xe tự hành Tesla của Elon Musk... Các bạn có thể tham khảo 1 số thuật toán như Q-Learning, Deep Q-Network,... + +Học tăng cường được đánh giá sẽ là ngành mũi nhọn trong thời gian tới, tuy nhiên phần toán bên dưới cũng như code rất nặng và khó, hiện tại nhiều bên đang nghiên cứu nhưng thực sự ở thời điểm hiện tại ở Việt Nam chưa có 1 sản phẩm cụ thể nào đã đưa vào production. + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã tìm hiểu: + +- **Học máy là gì**: Một tập hợp các phương pháp mà máy tính sử dụng để thực hiện và cải thiện các dự đoán hoặc hành vi dựa trên dữ liệu +- **Lịch sử phát triển**: Từ AI những năm 1950, học máy ra đời những năm 1980, đến học sâu bùng nổ từ 2010 +- **Ba loại bài toán chính**: + - **Học máy có giám sát (Supervised Learning)**: Classification và Regression + - **Học máy phi giám sát (Unsupervised Learning)**: Clustering và Dimension Reduction + - **Học tăng cường (Reinforcement Learning)**: Học từ phần thưởng và hình phạt + +## 💡 Lưu ý quan trọng + +- Học máy có giám sát cần dữ liệu đã được gán nhãn (X và Y) +- Học máy phi giám sát chỉ cần dữ liệu không có nhãn (chỉ có X) +- Classification dùng để phân loại (ví dụ: spam/không spam) +- Regression dùng để dự đoán giá trị số (ví dụ: giá nhà, giá cổ phiếu) +- Học tăng cường phù hợp cho các bài toán tối ưu hóa hành động trong môi trường + +## 🧪 Thực hành + +Hãy thử phân loại các bài toán sau vào đúng loại: + +1. Dự đoán giá nhà dựa trên diện tích, số phòng, vị trí +2. Phân loại email là spam hay không spam +3. Phân nhóm khách hàng dựa trên hành vi mua sắm +4. Xe tự lái học cách điều khiển từ môi trường + +## ➡️ Bước tiếp theo + +Trong bài tiếp theo, chúng ta sẽ cùng nhau thực hành làm quen với bài toán học máy có giám sát sử dụng scikit-learn API, một thư viện phổ biến khi làm việc với học máy ở tầm sơ và trung cấp. diff --git a/docs/tutorial/python/11.ml_1.ipynb b/docs/tutorial/python/11.ml_1.ipynb new file mode 100644 index 00000000..4551e76a --- /dev/null +++ b/docs/tutorial/python/11.ml_1.ipynb @@ -0,0 +1,1011 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e48e4783-3013-4b8e-8653-b1db847e1741", + "metadata": {}, + "source": [ + "# Bài 9: Thực hành về Học máy" + ] + }, + { + "cell_type": "markdown", + "id": "f22400ae-4b70-4fa8-bc0f-ad00bd23ba55", + "metadata": {}, + "source": [ + "## I. QUY TRÌNH TỔNG QUAN" + ] + }, + { + "cell_type": "markdown", + "id": "28a21130-ebda-4399-9df8-0b865f1c9989", + "metadata": {}, + "source": [ + "Một bài toán sử dụng học máy có thể chia thành 3 bước chính dưới đây:\n", + "\n", + "- **Bước 1: Thu thập dữ liệu**. Dữ liệu được thu thập càng nhiều càng tốt. Đối với học máy có giám sát, dữ liệu thu thập phải chứa kết quả bạn muốn dự đoán và thông tin bổ sung (các thông tin đặc trưng) để từ đó đưa ra dự đoán. Ví dụ:\n", + " - Đối với máy dò biển báo đường phố (\"Có biển báo đường phố trong hình ảnh không?\"), ta sẽ thu thập hình ảnh đường phố và gắn nhãn xem biển báo đường phố có hiển thị hay không. \n", + " - Đối với ứng dụng dự đoán vỡ nợ tín dụng, ta cần dữ liệu trước đây về các khoản vay thực tế, thông tin về việc liệu khách hàng có bị vỡ nợ với các khoản vay của họ hay không và dữ liệu sẽ giúp bạn đưa ra dự đoán, chẳng hạn như thu nhập, các khoản nợ tín dụng trong quá khứ, v.v. \n", + " - Đối với chương trình ước tính giá trị ngôi nhà tự động, bạn có thể thu thập dữ liệu từ các lần bán nhà trước đây và thông tin về bất động sản như kích thước, vị trí, v.v.\n", + " \n", + "\n", + "- **Bước 2: Huấn luyện mô hình**. Nhập các thông tin thu thập trên vào thuật toán máy học để tạo mô hình đáp ứng bài toán đưa ra, ví dụ: phát hiện biển báo, mô hình xếp hạng tín dụng, hoặc công cụ ước tính giá trị nhà.\n", + "\n", + "\n", + "- **Bước 3: Sử dụng mô hình dự đoán với dữ liệu mới**. Tích hợp mô hình vào một sản phẩm hoặc quy trình, chẳng hạn như ô tô tự lái, quy trình đăng ký tín dụng hoặc trang web thị trường bất động sản.\n", + "\n", + "\n", + "Trong thực tế, dữ liệu chúng ta có đôi khi không phải từ 1 nguồn mà từ rất nhiều nguồn khác nhau tổng hợp về 1 nguồn rồi xử lý dữ liệu rồi mới có thể huấn luyện được. Dưới đây là 1 hình ảnh ví dụ tổng quan mở rộng quy trình 3 bước cơ bản phía trên.\n", + "\n", + "![](./img/flow2.png)\n", + "\n", + "Phần tiếp theo, ta sẽ cũng nhau thực hành làm quen với bài toán học máy có giám sát thông qua bài toán phân loại giống hoa Iris." + ] + }, + { + "cell_type": "markdown", + "id": "55524f62-a198-4135-ba06-dc42cbdb8eb4", + "metadata": {}, + "source": [ + "## II. THỰC HÀNH\n", + "\n", + "![](https://scikit-learn.org/stable/_static/ml_map.png)\n", + "\n", + "Các bạn mới làm quen với Học máy có thể tham khảo lộ trình trong ảnh trên. \n", + "\n", + "### 0. Cài đặt\n", + "\n", + "Trong buổi học này thì mình sẽ làm quen và thực hành chủ yếu trên scikit-learn API, một thư viện phổ biến khi làm việc với học máy ở tầm sơ và trung cấp. Để cài đặt thư viện, ta sử dụng câu lệnh quen thuộc dưới đây.\n", + "\n", + "```python\n", + "!pip install scikit-learn\n", + "```\n", + "\n", + "Ta sẽ import 1 số thư viện cần thiết dưới đây để phục vụ cho bài thực hành." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "83eca661-67c0-41f7-af57-9997688cd83c", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.model_selection import train_test_split\n", + "import seaborn as sns" + ] + }, + { + "cell_type": "markdown", + "id": "61a77155-fe38-4138-aacb-4f431ef40c08", + "metadata": {}, + "source": [ + "### 1. Bài toán" + ] + }, + { + "cell_type": "markdown", + "id": "3bea6ebb-7467-4f85-b86d-cefdc762f006", + "metadata": {}, + "source": [ + "Chúng ta sẽ sử dụng tập dữ liệu Iris, chứa thông tin về ba giống hoa Iris khác nhau: Iris Versicolor, Iris Virginica, Iris Setosa với các phép đo của bốn biến: chiều dài đài hoa, chiều rộng đài hoa, chiều dài cánh hoa, chiều rộng cánh hoa. Mục đích của bài toán là để phân loại hoa Iris giữa ba loài (setosa, versicolor hoặc virginica) từ các phép đo chiều dài và chiều rộng của các lá đài và cánh hoa.\n", + "\n", + "Như vậy, mô hình của chúng ta sẽ có:\n", + "- **Đặc trưng**: Chiều dài đài hoa, chiều rộng đài hoa, chiều dài cánh hoa, chiều rộng cánh hoa.\n", + "- **Nhãn**: Iris Versicolor, Iris Virginica, Iris Setosa\n", + "\n", + "Tập dữ liệu Iris có một số tính năng thú vị:\n", + "- Một trong các lớp (Iris Setosa) có thể phân tách tuyến tính với hai lớp còn lại. Tuy nhiên, hai lớp khác không thể phân tách tuyến tính.\n", + "- Có một số trùng lặp giữa các lớp Versicolor và Virginica, vì vậy nó khó có thể đạt được tỷ lệ phân loại hoàn hảo.\n", + "- Có một số dư thừa trong bốn biến đầu vào, vì vậy có thể đạt được một giải pháp tốt chỉ với ba trong số chúng, hoặc thậm chí (với độ khó) từ hai, nhưng việc lựa chọn chính xác các biến tốt nhất là không rõ ràng.\n", + "\n", + "Lí do chọn Iris:\n", + "- Các thuộc tính là số nên bạn phải tìm cách tải và xử lý dữ liệu.\n", + "- Nó chỉ có 4 thuộc tính và 150 hàng, có nghĩa là nó nhỏ và dễ dàng vừa với bộ nhớ (và một màn hình hoặc trang A4).\n", + "- Tất cả các thuộc tính số đều có cùng đơn vị và cùng tỷ lệ, không yêu cầu bất kỳ tỷ lệ hoặc biến đổi đặc biệt nào để bắt đầu.\n", + "- Đây là một bài toán phân loại, cho phép bạn thực hành với một loại thuật toán học có giám sát dễ dàng hơn." + ] + }, + { + "cell_type": "markdown", + "id": "f84e6963-af78-4c42-b00a-c8026b8dc6b6", + "metadata": {}, + "source": [ + "![](./img/iris.png)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2738bbdf-c94b-43ca-9857-90e5839ed710", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sepal_lengthsepal_widthpetal_lengthpetal_widthspecies
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa
\n", + "
" + ], + "text/plain": [ + " sepal_length sepal_width petal_length petal_width species\n", + "0 5.1 3.5 1.4 0.2 setosa\n", + "1 4.9 3.0 1.4 0.2 setosa\n", + "2 4.7 3.2 1.3 0.2 setosa\n", + "3 4.6 3.1 1.5 0.2 setosa\n", + "4 5.0 3.6 1.4 0.2 setosa" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iris = sns.load_dataset('iris')\n", + "iris.head()" + ] + }, + { + "cell_type": "markdown", + "id": "4d09d254-1ff4-4562-9b7d-a93c4062374b", + "metadata": {}, + "source": [ + "### 2. EDA (Exploratory Data Analysis)" + ] + }, + { + "cell_type": "markdown", + "id": "564718ab-a3d6-4175-8ca3-922f7d98efca", + "metadata": {}, + "source": [ + "#### 2.1 Đánh giá ở mức thống kê" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1af6f75b-6243-48ee-ae4e-e3bef09035cf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(['setosa', 'versicolor', 'virginica'], dtype=object)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iris['species'].unique()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "043dbef0-5e49-4dd1-956b-8f78cfea6f60", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((150, 5), 750)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iris.shape, iris.size #row*columns" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6b22f1f3-2250-4651-be57-5f783adcdf21", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 150 entries, 0 to 149\n", + "Data columns (total 5 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 sepal_length 150 non-null float64\n", + " 1 sepal_width 150 non-null float64\n", + " 2 petal_length 150 non-null float64\n", + " 3 petal_width 150 non-null float64\n", + " 4 species 150 non-null object \n", + "dtypes: float64(4), object(1)\n", + "memory usage: 6.0+ KB\n" + ] + } + ], + "source": [ + "iris.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2c02151b-9a4e-4f09-9123-4378bd10b078", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sepal_length 0\n", + "sepal_width 0\n", + "petal_length 0\n", + "petal_width 0\n", + "species 0\n", + "dtype: int64" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iris.isnull().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e28fdcb3-88fd-4872-8ca9-236c972b97ae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "setosa 50\n", + "versicolor 50\n", + "virginica 50\n", + "Name: species, dtype: int64" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iris['species'].value_counts() #iris.groupby('species').size() or iris.groupby('species').count()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "09f11d43-6804-4ede-a331-da5ba7f28050", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sepal_lengthsepal_widthpetal_lengthpetal_width
count150.000000150.000000150.000000150.000000
mean5.8433333.0573333.7580001.199333
std0.8280660.4358661.7652980.762238
min4.3000002.0000001.0000000.100000
25%5.1000002.8000001.6000000.300000
50%5.8000003.0000004.3500001.300000
75%6.4000003.3000005.1000001.800000
max7.9000004.4000006.9000002.500000
\n", + "
" + ], + "text/plain": [ + " sepal_length sepal_width petal_length petal_width\n", + "count 150.000000 150.000000 150.000000 150.000000\n", + "mean 5.843333 3.057333 3.758000 1.199333\n", + "std 0.828066 0.435866 1.765298 0.762238\n", + "min 4.300000 2.000000 1.000000 0.100000\n", + "25% 5.100000 2.800000 1.600000 0.300000\n", + "50% 5.800000 3.000000 4.350000 1.300000\n", + "75% 6.400000 3.300000 5.100000 1.800000\n", + "max 7.900000 4.400000 6.900000 2.500000" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iris.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b1b20fc3-170e-4858-b6d5-7bdff0d7f6b8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sepal_lengthsepal_widthpetal_lengthpetal_width
sepal_length1.000000-0.1175700.8717540.817941
sepal_width-0.1175701.000000-0.428440-0.366126
petal_length0.871754-0.4284401.0000000.962865
petal_width0.817941-0.3661260.9628651.000000
\n", + "
" + ], + "text/plain": [ + " sepal_length sepal_width petal_length petal_width\n", + "sepal_length 1.000000 -0.117570 0.871754 0.817941\n", + "sepal_width -0.117570 1.000000 -0.428440 -0.366126\n", + "petal_length 0.871754 -0.428440 1.000000 0.962865\n", + "petal_width 0.817941 -0.366126 0.962865 1.000000" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "iris.corr()" + ] + }, + { + "cell_type": "markdown", + "id": "f07f95b8-3669-460f-9491-b0ef691489ae", + "metadata": {}, + "source": [ + "#### 2.2 Trực quan hoá dữ liệu" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "5fa2e4e0-d11e-4feb-80d8-dae34acc1186", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "species = iris['species'].value_counts()\n", + "species.plot(kind=\"bar\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "bb2006c8-0e26-4847-a2b1-e0cfadb1b2f6", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.pie(species, labels = species.index, autopct='%.2f%%', explode=[0,0.1,0])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4fe49f15-abac-4a6c-a05c-ee1d8f056c60", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAz4AAANCCAYAAACnKxliAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAA9hAAAPYQGoP6dpAACHaUlEQVR4nOzdfXwU9bn///cGliWRBAVMspQYYg1ajVCOEQiiJGACkVIFtVSqhdPWHx6QI00tclPqpigB+j0Wz6Hm1NaDUKVgq6AtEbL2kFCLWKBSEVuLx3DTSqAiEAi4LGR+f3CyhyW3s5ndzc6+no9HHmE++5mZ65qZzczF3DkMwzAEAAAAADaWEO0AAAAAACDcKHwAAAAA2B6FDwAAAADbo/ABAAAAYHsUPgAAAABsj8IHAAAAgO1R+AAAAACwPQofAAAAALZH4QMAAADA9ih8AEkej0cOh8PUOM8//7wcDof27dsXnqBM2rp1qzwej44fP97ks/79++tLX/pS5IMCAERdKPu4UOXn5ys/P7/Nfvv27ZPD4dDzzz8faKuoqJDH42m2v8Ph0MMPP2xNkIhbFD6ATWzdulWlpaXNFj4AAETCM888o2eeeSakcSsqKlRaWmpxRMD/6RrtAAAAAGAP119/fbRDAFrEGR9EzD/+8Q/9f//f/6eMjAy5XC5deeWVuuWWW/TGG28E+rzxxhsaPXq0UlJSlJSUpFtuuUW//e1vg6bTeMr+nXfe0cSJE5WSkqKePXvq/vvv1z/+8Y+gvmvXrlVRUZHcbrcSExP1hS98QXPmzFF9fX3Y8jSTw549e3TfffepZ8+eSktL0ze+8Q2dOHEiqO/x48f1zW9+U7169VKPHj00btw4ffTRR3I4HIFLAjwej7773e9KkrKysuRwOORwOFRVVRU0rY0bN+qf/umflJiYqOuuu07/9V//FbblAADxxE77uD179sjhcOiXv/xloG3nzp1yOBy64YYbgvp++ctf1k033RQYbu5St48//lhf+cpXlJycrJ49e2rSpEmqra0N6jN16lT9+Mc/lqTAPqy5y8l//vOf6wtf+IKSkpI0aNAg/eY3v+lQrogvFD6ImAceeEDr16/X97//fVVWVupnP/uZbr/9dh09elSS9MILL6ioqEgpKSlauXKlXnrpJfXq1UtjxoxpsmOQpAkTJuiaa67Rr371K3k8Hq1fv15jxoyR3+8P9Nm7d6/uuOMOPffcc9q4caNmzZqll156SePHjw9LjmZzuPvuuzVgwAC9/PLLmjNnjlavXq1vf/vbgc8bGho0fvx4rV69Wo899pjWrVunoUOHauzYsUHT+da3vqWZM2dKkl555RW99dZbeuutt/RP//RPgT5/+tOf9J3vfEff/va39eqrr2rgwIH65je/qS1btoRlWQBAPLHTPu6GG26Q2+1uUrQlJibq/fff18cffyxJOnfunKqrq3X77be3OK0zZ87o9ttvV2VlpcrKyvTLX/5S6enpmjRpUlC/BQsW6J577pGkwD7srbfektvtDvTZsGGDli9frh/84Ad6+eWX1atXL02YMEEfffRRh/JFHDGACOnRo4cxa9asZj+rr683evXqZYwfPz6o/fz588agQYOMIUOGBNoef/xxQ5Lx7W9/O6jviy++aEgyXnjhhWbn0dDQYPj9fqO6utqQZPzpT39qMk0zVqxYYUgyampqQs5h6dKlQX2nT59udO/e3WhoaDAMwzA2bNhgSDLKy8uD+pWVlRmSjMcffzzQ9sMf/jAonotlZmYa3bt3N/bv3x9oO3PmjNGrVy9j2rRppvIGADRlt33c/fffb1x99dWB4dtvv9148MEHjSuuuMJYuXKlYRiG8fvf/96QZFRWVgb6jRw50hg5cmRguLy83JBkvPrqq0HTf/DBBw1JxooVKwJtM2bMaDFOSUZaWppRV1cXaKutrTUSEhKMsrIyU7khfnHGBxEzZMgQPf/883riiSe0bdu2oP+12rp1qz799FNNmTJF586dC/w0NDRo7Nix2r59e5NT91/72teChr/yla+oa9eu2rx5c6Dto48+0uTJk5Wenq4uXbrI6XRq5MiRkqQ///nPluYXSg5f/vKXg4YHDhyozz77TEeOHJEkVVdXB3K72H333Wc6vi9+8Yu66qqrAsPdu3fXgAEDtH//ftPTAgAEs9s+bvTo0froo49UU1Ojzz77TG+++abGjh2rgoICeb1eSRfOArlcLo0YMaLF6WzevFnJyclN9neTJ082HVNBQYGSk5MDw2lpaUpNTWU/hnbj4QaImLVr1+qJJ57Qz372My1YsEA9evTQhAkTtHTpUh0+fFiSAqe5m/Ppp5/qsssuCwynp6cHfd61a1f17t07cFnBqVOndOutt6p79+564oknNGDAACUlJengwYOaOHGizpw5Y2l+oeTQu3fvoM9dLpckBWI7evSounbtql69egX1S0tLMx3fpfNqnJ/VywEA4pHd9nGNl6+98cYbysrKkt/v16hRo3T48GEtXLgw8Nktt9yixMTEFqdz9OjRZvdZl+bXHuzH0FEUPoiYPn36aNmyZVq2bJkOHDig1157TXPmzNGRI0cC97X8x3/8h4YNG9bs+Jf+4aytrdXnPve5wPC5c+d09OjRwB/G//7v/9bHH3+sqqqqwP+ASQrb45779OkjyVwObendu7fOnTunTz/9NKj4ufSmUABAdNltH9evXz8NGDBAb7zxhvr376/c3FxdfvnlGj16tKZPn663335b27Zta/Px071799Yf/vCHJu3sxxANFD6IiquuukoPP/ywfvvb3+r3v/+9brnlFl1++eV6//332/2CshdffDHoSTIvvfSSzp07F3iaTOPL2hrPojT6yU9+Yk0Slwglh7aMHDlSS5cu1dq1a/Uv//IvgfY1a9Y06Xvp2SIAQHTYZR93++2366WXXlJGRobGjRsnSRowYICuuuoqff/735ff72/1wQbShcvTXnrpJb322mtBl7utXr26Sd+L92OtnUUCQkXhg4g4ceKECgoKNHnyZF133XVKTk7W9u3btXHjRk2cOFE9evTQf/zHf2jKlCn69NNPdc899yg1NVX/+Mc/9Kc//Un/+Mc/VF5eHjTNV155RV27dlVhYaH27NmjBQsWaNCgQYH7YYYPH64rrrhCDz30kB5//HE5nU69+OKL+tOf/hSWHEPJoS1jx47VLbfcou985zuqq6vTTTfdpLfeekurVq2SJCUk/N9tejfeeKMk6emnn9aUKVPkdDp17bXXBl0PDQCwnl33caNHj9YzzzyjTz75RMuWLQtqX7Fiha644oqg4qw5X//61/WjH/1IX//61/Xkk08qOztbFRUV2rRpU5O+jfuxJUuWqLi4WF26dNHAgQPVrVs3y3JCfOPhBoiI7t27a+jQofr5z3+ur33tayouLtbPfvYzPfbYY/rpT38qSbr//vu1efNmnTp1StOmTdPtt9+uRx55RH/84x81evToJtN85ZVX9Je//EUTJ07U97//fY0fP16VlZWBP5C9e/fWhg0blJSUpPvvv1/f+MY31KNHD61duzZseZrNoS0JCQn69a9/ra9+9atavHix7rzzTv3u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/M//tPo29Wgt11BibRTp5bp9+3adP38+MPzee++psLBQ9957b7P9o7VMzcbZKBrf/SVLlug///M/tXLlSt1www3asWOH/vmf/1k9e/bUI4880uw40f4bgNBUV1drxowZuvnmm3Xu3DnNnz9fRUVFev/993XZZZc1O46d1nUo+TeKxnfTSv369dPixYt1zTXXSJJWrlypO++8U++8845uuOGGJv3ttN4l8/k3ivX13pz2HpPabRtoFNFjciMOnTx50sjOzja8Xq8xcuRI45FHHmmx7+bNmw1JxrFjxyIWn2EYxuOPP24MGjSo3f1nz55tXHfddUFt06ZNM4YNG2ZxZE2ZjTVay/Sxxx4zRowYYWqcaC3XUGKN1nK91COPPGJ8/vOfNxoaGpr9PJrb6sXaijOay3PcuHHGN77xjaC2iRMnGvfff3+L43SW5YqOOXLkiCHJqK6ubrGPndd1e/LvLH/rwuGKK64wfvaznzX7mZ3Xe6PW8rfrejdzTGrHbSDSx+RxeanbjBkzNG7cON1+++3tHmfw4MFyu90aPXq0Nm/eHMbo/s/evXvVt29fZWVl6atf/ao++uijFvu+9dZbKioqCmobM2aMduzYIb/fH+5QTcXaKNLL9LXXXlNubq7uvfdepaamavDgwfrpT3/a6jjRWq6hxNooGttqo7Nnz+qFF17QN77xDTkcjmb7RHtbldoXZ6NoLM8RI0bot7/9rf76179Kkv70pz/pzTff1B133NHiOJ1huaLjTpw4IUnq1atXi33svK7bk3+jaP6ts9r58+e1Zs0a1dfXKy8vr9k+dl7v7cm/kZ3Wu2TumNSO20Ckj8njrvBZs2aN/vjHP6qsrKxd/d1ut5599lm9/PLLeuWVV3Tttddq9OjR2rJlS1jjHDp0qFatWqVNmzbppz/9qWprazV8+HAdPXq02f61tbVKS0sLaktLS9O5c+f0ySefdKpYo7VMP/roI5WXlys7O1ubNm3SQw89pH/913/VqlWrWhwnWss1lFijtVwvtn79eh0/flxTp05tsU80t9VG7Ykzmsvzscce03333afrrrtOTqdTgwcP1qxZs3Tfffe1OE5nWK7oGMMwVFJSohEjRignJ6fFfnZd1+3NvzP8rbPK7t271aNHD7lcLj300ENat26drr/++mb72nG9m8nfTuu9kdljUrttA1E5Jg/5XFEM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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "iris.hist(figsize=(10,10))\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "88ecff3d-132c-492e-b8c0-dd4db45a0ac0", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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oczMTJWVlfE12yaA9m5aaO+mpam2tzFGlZWV6tSpk5o1q3skRczPWDRr1kydO3eO9W4TRnp6epP6QWzqaO+mhfZuWppiex/pTMUBDN4EAADWECwAAIA1BIsY8fl8uvfee5lCvomgvZsW2rtpob2PLOaDNwEAQOPFGQsAAGANwQIAAFhDsAAAANYQLBro6quv1mWXXXZM6+bl5emWW26Jaj3HasmSJXIcR7t37453KZ4VSdtHYubMmWrVqtUR1xk/frx69ux5xHU2b94sx3H00UcfWautKYnkM3IsbRZL2dnZmjx5crzL8KRo/m50HEcvvfRSnc8f62c2kf6WHA7BoglI9B9ChLviiiu0fv36iF4TrZDTGCTaH32bGvOxNVQivjfbtm3T4MGDj3l9r/4DGPMrbwI4spSUFKWkpMS7DACWdejQId4lxITnz1j89a9/1RlnnKGUlBS1adNGF110kfbu3StJmjFjhnJycuT3+9W1a1c9/fTT7usOnHJ68cUX1b9/f/n9fnXr1k1Llixx16murtZ1112nk046SSkpKfr5z3+uJ554wlrt+/bt0x133KETTzxRxx13nPr27Ru2/wOJOxAIKCcnRy1bttQvfvELbdu2zV2nqqpKN998s1q1aqU2bdpo7NixGj58uPvf69VXX62lS5fqiSeekOM4chxHmzdvdl+/cuVK9e7dW6mpqerfv79KS0utHV+0eaXtX375ZbVq1Uo1NTWSpI8++kiO4+g///M/3XVuuOEGDR06VNLh/9N66KGH1L59e6Wlpem6665TMBh0nxs/frxmzZqlRYsWuW1c+1g2btyoCy64QKmpqerRo4fef//9eh1HvOTl5WnUqFEaNWqU+3N+9913uzMsHulztGTJEl1zzTUqLy9335vx48dLkoqLi9W7d2+lpaWpQ4cOGjZsmLZv326t7pdffllnn322/H6/Tj75ZN13332qqqpyn3ccR3/605/0q1/9SqmpqTr11FO1ePHisG0sXrxYp556qlJSUnTBBRdo1qxZ7n+wRzo2Sfr+++917bXXKi0tTV26dNG0adOsHVssJHq7G2PUtm1bzZ8/313Ws2dPtWvXzn38/vvvq0WLFtqzZ4+kQ7tCPvzwQ5111lny+/3q3bu3Vq9e7T63efNmXXDBBZKk448/Xo7j6Oqrr3afr6mp0R133KHWrVurQ4cOYW0fd8bDvv76a9O8eXPz2GOPmU2bNpmPP/7YTJkyxVRWVppp06aZjh07mvnz55uNGzea+fPnm9atW5uZM2caY4zZtGmTkWQ6d+5s/vrXv5rPPvvM/Md//IdJS0szO3bsMMYYs2/fPnPPPfeYDz/80GzcuNEUFxeb1NRUM2/ePLeG4cOHm4KCgmOqNzc314wePdp9PGzYMNO/f3/z7rvvms8//9w88sgjxufzmfXr1xtjjJkxY4Zp0aKFueiii0xJSYlZuXKlycnJMcOGDXO38eCDD5rWrVubBQsWmLVr15oRI0aY9PR0t6bdu3ebfv36meuvv95s27bNbNu2zVRVVZl33nnHSDJ9+/Y1S5YsMWvWrDHnn3++6d+/fwNaJHa81Pa7d+82zZo1MytWrDDGGDN58mRzwgknmD59+rjrnHbaaWbq1KnGmB/bPSMjw31u3rx5Jjk52UyfPt2sW7fO3HXXXSYtLc306NHDGGNMZWWl+e1vf2t+8YtfuG0cCoXc4+zatat55ZVXTGlpqfnNb35jsrKyzP79+xvy9sdUbm6uadmypRk9erRZt26d2xbTpk0zxhz5cxQKhczkyZNNenq6+95UVlYaY4x57rnnzKuvvmq++OIL8/7775tzzz3XDB482N3vgc/Irl27jlrjwW32+uuvm/T0dDNz5kzzxRdfmDfeeMNkZ2eb8ePHu+sc+BmcM2eO2bBhg7n55ptNy5Ytzc6dO40xP/6ctmjRwtx+++1m3bp1Zu7cuebEE090azrSsWVlZZnWrVubKVOmmA0bNpiioiLTrFkzs3bt2oY2R8x4od0vv/xyM2rUKGOMMf/3f/9nWrRoYVq1amXWrFljjDFm4sSJpm/fvu76kszChQuNMcbs2bPHtG3b1lxxxRXm008/NS+//LI5+eSTjSSzevVqU1VVZebPn28kmdLSUrNt2zaze/du971JT08348ePN+vXrzezZs0yjuOYN954o8Hvuw2eDhYrV640kszmzZsPeS4zM9PMmTMnbNkDDzxg+vXrZ4z56Y/LQw895D6/f/9+07lzZ/Pwww/Xuc+bbrrJ/PrXv3Yf1zdYfP7558ZxHPPVV1+FrXPhhReacePGGWN+/GUlyXz++efu81OmTDHt27d3H7dv39488sgj7uOqqirTpUuXsJoODjTG/PTheeutt9xlf/vb34wk88MPPxzT8cST19q+V69e5tFHHzXGGHPZZZeZCRMmmOTkZFNRUWG2bdtmJLm/9A/+I9WvXz8zYsSIsO317dvXDRZ11XLgOP/0pz+5y9asWRO2Ly/Izc01OTk5pqamxl02duxYk5OTc8yfo9rvZ10+/PBDI8n9A9SQYHH++eebiRMnhq3zwgsvmI4dO7qPJZm7777bfbxnzx7jOI557bXX3GPs3r172DbuuuuusJrqOrasrCxz5ZVXuo9rampMu3bt3PDqBV5o9//+7/922+ill14yvXv3NpdffrmZMmWKMcaYQYMGmbFjx7rr1w4Wzz77rGndurXZu3ev+/zUqVPdYHGkWnJzc815550XtqxPnz5h+4onT3eF9OjRQxdeeKHOOOMMDRkyRNOnT9euXbv03XffqaysTNddd51atmzp3h588EF98cUXYdvo16+fe7958+bq3bu31q5d6y575pln1Lt3b7Vt21YtW7bU9OnTtXXr1gbXvmrVKhljdNppp4XVuHTp0rAaU1NT9bOf/cx93LFjR/e0XXl5ub799ludc8457vNJSUk6++yzj7mOM888M2zbkqyeDo4Wr7V9Xl6elixZImOM/v73v6ugoEDdu3fXP/7xD73zzjtq3769unbtetjXrl27NqzWg2s/Gq+2cW3nnnuuHMdxH/fr108bNmzQihUrjulzdDirV69WQUGBsrKylJaWpry8PEmy8vleuXKl7r///rCarr/+em3btk3ff/+9u17ttjnuuOOUlpbmtk1paan69OkTtt3an/Wjqb1tx3HUoUMH2l122z0vL09r1qzRjh07tHTpUuXl5SkvL09Lly5VVVWV3nvvPeXm5h72tWvXrlWPHj2UmpoadnzHqnb7SuF/G+LN04M3k5KS9Oabb+q9997TG2+8oSeffFJ33XWXXn75ZUnS9OnT1bdv30NeczQHfpD/8pe/6NZbb9WkSZPUr18/paWl6ZFHHtEHH3zQ4NpramqUlJSklStXHlJTy5Yt3fstWrQ4pDZz0FXYa3/wJB3y/JHU3v6B7RwYC5DIvNb2eXl5eu655/TPf/5TzZo10+mnn67c3FwtXbpUu3btqvOXjw1ebeNjdSyfo4Pt3btXgwYN0qBBg1RcXKy2bdtq69atys/P1759+xpcU01Nje677z5dfvnlhzzn9/vd+4f7fB9oG2OMtc/2wdtuDBKh3bt3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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sns.boxplot(data=iris, width=0.8)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "8a177047-90f9-4a34-ac82-52e47add6d0e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sns.heatmap(iris.corr(),annot=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "afbb4bab-66b9-41d9-be4e-98e893803415", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAswAAAJRCAYAAABRKeGHAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydd3gUVReH3+2bTbKb3ish9N6rFKUJAjZEREHB8mFBEAv2ggUsoNhQAVGkKSIqiCJIld5LAimk97pJdrPJlu+PIWXJboQQ+rzPk4fsnTszd8LdmTPnnvM7EpvNZkNERERERERERERExCHSKz0AERERERERERERkasZ0WAWERERERERERERqQfRYBYRERERERERERGpB9FgFhEREREREREREakH0WAWERERERERERERqQfRYBYRERERERERERGpB9FgFhEREREREREREakH0WAWERERERERERERqQfRYBYRERERERERERGpB9FgFhEREREREREREamHK2owm81mXn75ZSIjI3FxcaFJkya8+eabWK3W89rfZrOh1+sRq3uL1Ic4T0T+C3GOiPwX4hwREbmxkV/Jk8+ePZsvv/ySJUuW0Lp1a/bv38+DDz6ITqdj6tSp/7l/SUkJOp2O4uJitFrtZRixyLWIOE9E/gtxjoj8F+IcERG5sbmiBvOuXbsYNWoUw4cPByAiIoLly5ezf//+KzksEZEbHovVQo4xh7LKMlQyFd5qbzQKzZUelsh1hs1mI8eQQ2llKQqpAk+1J+5K9ys9LJFLSGF5IcWmYiw2CzqVDh8Xnys9JBGR8+KKGsx9+vThyy+/5PTp0zRr1owjR46wY8cO5s2b57C/yWTCZDJVf9br9ZdppCLXEuI8uTgKywv5K/kv5h+aT7GpGJlExi1ht/BMl2cIdAu80sNrFMQ5cuUpqSjh34x/mb13NrnGXCRI6BXUi5d6vESoe+iVHp44RxoZq81KfFE8r+58lRP5JwCI0EbwWs/XaOPTBrVcfYVHKCJSP1c0hvn555/n3nvvpUWLFigUCjp27MjTTz/Nvffe67D/u+++i06nq/4JDb3yN1WRqw9xnjQcq83K3yl/M2v3LIpNxQBYbBb+TP6Tqf9MJc+Yd4VH2DiIc+TKczjnMDO2ziDXmAuADRs7M3Yy6c9JZJdlX+HRiXOksckozeCBPx6oNpYBkvRJ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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sns.pairplot(iris, hue = 'species', height = 1.5)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1a925cba-61bc-4248-8ca8-f04a0790d7ea", + "metadata": {}, + "source": [ + "### 3. Xây dựng mô hình" + ] + }, + { + "cell_type": "markdown", + "id": "0cc17902-4bc2-4a4a-8f85-555ad7d10dd0", + "metadata": {}, + "source": [ + "#### 3.1 Chia tập huấn luyện và kiểm thử" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c8923e46-a758-49ee-8652-a6fd90d05ea4", + "metadata": {}, + "outputs": [], + "source": [ + "X_iris = iris.drop('species', axis = 1) \n", + "y_iris = iris['species'] " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "dcf9ff3b-9f0a-4ea6-bb31-fa4750cc4b35", + "metadata": {}, + "outputs": [], + "source": [ + "Xtrain, Xtest, ytrain, ytest = train_test_split(X_iris, y_iris, test_size = 0.2, random_state=1) #default 25%" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "9ad90802-4732-4608-b304-aed68cdb6eb4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(120, 4) (30, 4)\n", + "(120,) (30,)\n" + ] + } + ], + "source": [ + "print(Xtrain.shape, Xtest.shape)\n", + "print(ytrain.shape, ytest.shape)" + ] + }, + { + "cell_type": "markdown", + "id": "ec043190-7ac2-4ecc-b471-6fa09ed4a30e", + "metadata": {}, + "source": [ + "#### 3.2 Huấn luyện mô hình\n", + "\n", + "Hồi quy logistic là phân tích hồi quy thích hợp để tiến hành khi biến phụ thuộc là nhị phân. Giống như tất cả các phân tích hồi quy, hồi quy logistic là một phân tích dự đoán. Về mặt toán học, một mô hình logistic có một biến phụ thuộc với hai giá trị có thể có, chẳng hạn như đạt / không đạt, thắng / thua, sống / chết, ... trong đó hai giá trị được gắn nhãn `0` và `1`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "7d892587-2fb5-4c5d-85e1-60117823a28b", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.linear_model import LogisticRegression" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "daa06f41-1d72-4c99-abc9-7115994c8829", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "LogisticRegression(max_iter=1000)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model = LogisticRegression(max_iter=1000) \n", + "model.fit(Xtrain, ytrain)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "5a07d63e-9e61-4eef-b278-8f766d25584b", + "metadata": {}, + "outputs": [], + "source": [ + "y_model = model.predict(Xtest)" + ] + }, + { + "cell_type": "markdown", + "id": "16f14e59-79f7-4c6e-81fd-8935dc7de294", + "metadata": {}, + "source": [ + "#### 3.3 Đánh giá chất lượng mô hình" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "bcc0cc79-fcdb-4640-988a-c4486aced1ba", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "43cbcf6a-ddbb-4ac0-a19b-4e4e5d2a3e04", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9666666666666667\n", + " precision recall f1-score support\n", + "\n", + " setosa 1.00 1.00 1.00 11\n", + " versicolor 1.00 0.92 0.96 13\n", + " virginica 0.86 1.00 0.92 6\n", + "\n", + " accuracy 0.97 30\n", + " macro avg 0.95 0.97 0.96 30\n", + "weighted avg 0.97 0.97 0.97 30\n", + "\n" + ] + } + ], + "source": [ + "print(accuracy_score(ytest, y_model))\n", + "print(classification_report(ytest, y_model))" + ] + }, + { + "cell_type": "markdown", + "id": "33440bb4-83bf-4a2a-8c31-17fef58642b4", + "metadata": {}, + "source": [ + "## III. TRIỂN KHAI VỚI FLASK\n", + "\n", + "Flask là một Web Framework rất nhẹ của Python, dễ dàng giúp người mới bắt đầu học Python có thể tạo ra website nhỏ. Flask cũng dễ mở rộng để xây dựng các ứng dụng web phức tạp. Ngoài Flask Framework, bạn có thể học PYTHON với Django Framework để xây dựng các ứng dụng web lớn hơn. Như đã nêu trước đó, Flask được phân loại là Web Framework siêu nhỏ, nhẹ. Thông thường, một framework vi mô là một framework tối giản hoặc không phụ thuộc vào thư viện bên ngoài.\n", + "\n", + "Để sử dụng Flask, ta cài thư viện sau:\n", + "```python\n", + "!pip install flask\n", + "```\n", + "\n", + "Dưới đây là 1 số câu lệnh để ta làm quen với Flask." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "a2c9cd83-1c80-438c-9a35-dc43105efda9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " * Serving Flask app '__main__' (lazy loading)\n", + " * Environment: production\n", + "\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n", + "\u001b[2m Use a production WSGI server instead.\u001b[0m\n", + " * Debug mode: off\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n" + ] + } + ], + "source": [ + "from flask import Flask\n", + "\n", + "app = Flask(__name__) #request Flask to create an application\n", + "\n", + "#flash routing\n", + "@app.route('/') #return a link that displays \"hello world\"\n", + "def hello():\n", + " return \"

Hello world

\"\n", + "\n", + "@app.route('/') #return a link that displays \"hello world\"\n", + "def user(name):\n", + " return f\"

This is {name}'s homepage

\"\n", + "\n", + "if __name__ == '__main__': # the module that is being run is the main program\n", + " app.run()\n", + "# click ii to stop" + ] + }, + { + "cell_type": "markdown", + "id": "e394d191-c183-43bc-9c06-c3a5c4053085", + "metadata": {}, + "source": [ + "Với bài toán phân loại giống hoa Iris ta đã thực hành, ta có thể tạo ra 1 giao diện đơn giản để triển khai dự đoán với những dữ liệu mới với Flask theo đoạn code dưới đây.\n", + "\n", + "Ta tạo file `model.py` chứa mô hình cùng 1 hàm đọc các dữ liệu mới để đưa vào mô hình đã huấn luyện và đưa ra dự đoán:\n", + "\n", + "```python\n", + "# Importing necessary libraries\n", + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn.linear_model import LogisticRegression\n", + "\n", + "# Importing the dataset\n", + "data = pd.read_csv('iris.csv')\n", + "\n", + "# Dictionary containing the mapping\n", + "variety_mappings = {0: 'Setosa', 1: 'Versicolor', 2: 'Virginica'}\n", + "\n", + "# Encoding the target variables to integers\n", + "data = data.replace(['Setosa', 'Versicolor' , 'Virginica'],[0, 1, 2])\n", + "\n", + "X = data.iloc[:, 0:-1] # Extracting the independent variables\n", + "y = data.iloc[:, -1] # Extracting the target/dependent variable\n", + "\n", + "logreg = LogisticRegression(max_iter=2000) # Initializing the Logistic Regression model\n", + "logreg.fit(X, y) # Fitting the model\n", + "\n", + "# Function for classification based on inputs\n", + "def classify(a, b, c, d):\n", + " arr = np.array([a, b, c, d]) # Convert to numpy array\n", + " arr = arr.astype(np.float64) # Change the data type to float\n", + " query = arr.reshape(1, -1) # Reshape the array\n", + " prediction = variety_mappings[logreg.predict(query)[0]] # Retrieve from dictionary\n", + " return prediction # Return the prediction\n", + "```\n", + "\n", + "Bên cạnh đó, ta tạo 1 file `server.py` chung thư mục với `model.py`:\n", + "\n", + "```python\n", + "import model # Import the python file containing the ML model\n", + "from flask import Flask, request, render_template,jsonify # Import flask libraries\n", + "\n", + "# Initialize the flask class and specify the templates directory\n", + "app = Flask(__name__,template_folder=\"templates\")\n", + "\n", + "# Default route set as 'home'\n", + "@app.route('/home')\n", + "def home():\n", + " return render_template('home.html') # Render home.html\n", + "\n", + "# Route 'classify' accepts GET request\n", + "@app.route('/classify',methods=['POST','GET'])\n", + "def classify_type():\n", + " try:\n", + " sepal_len = request.args.get('slen') # Get parameters for sepal length\n", + " sepal_wid = request.args.get('swid') # Get parameters for sepal width\n", + " petal_len = request.args.get('plen') # Get parameters for petal length\n", + " petal_wid = request.args.get('pwid') # Get parameters for petal width\n", + "\n", + " # Get the output from the classification model\n", + " variety = model.classify(sepal_len, sepal_wid, petal_len, petal_wid)\n", + "\n", + " # Render the output in new HTML page\n", + " return render_template('output.html', variety=variety)\n", + " except:\n", + " return 'Error'\n", + "\n", + "# Run the Flask server\n", + "if(__name__=='__main__'):\n", + " app.run(debug=True) \n", + "```\n", + "\n", + "Khi đó, ta sử dụng câu lệnh `python server.py` trên terminal và truy cập đường dẫn hiện ra(thông thường sẽ là `http://127.0.0.1:5000/`). Chúc các bạn thử nghiệm và thành công!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorial/python/11.ml_1.md b/docs/tutorial/python/11.ml_1.md new file mode 100644 index 00000000..42f566d7 --- /dev/null +++ b/docs/tutorial/python/11.ml_1.md @@ -0,0 +1,645 @@ +# Bài 9: Thực hành về Học máy + +## 🎯 Mục tiêu học tập + +Sau khi hoàn thành bài này, bạn sẽ có thể: +- [ ] Hiểu được quy trình tổng quan của một bài toán học máy +- [ ] Thực hành với scikit-learn để xây dựng mô hình phân loại +- [ ] Sử dụng tập dữ liệu Iris để thực hành +- [ ] Đánh giá hiệu suất mô hình bằng các metrics +- [ ] Triển khai mô hình đơn giản với Flask + +## I. QUY TRÌNH TỔNG QUAN + +Một bài toán sử dụng học máy có thể chia thành 3 bước chính dưới đây: + +- **Bước 1: Thu thập dữ liệu**. Dữ liệu được thu thập càng nhiều càng tốt. Đối với học máy có giám sát, dữ liệu thu thập phải chứa kết quả bạn muốn dự đoán và thông tin bổ sung (các thông tin đặc trưng) để từ đó đưa ra dự đoán. Ví dụ: + - Đối với máy dò biển báo đường phố ("Có biển báo đường phố trong hình ảnh không?"), ta sẽ thu thập hình ảnh đường phố và gắn nhãn xem biển báo đường phố có hiển thị hay không. + - Đối với ứng dụng dự đoán vỡ nợ tín dụng, ta cần dữ liệu trước đây về các khoản vay thực tế, thông tin về việc liệu khách hàng có bị vỡ nợ với các khoản vay của họ hay không và dữ liệu sẽ giúp bạn đưa ra dự đoán, chẳng hạn như thu nhập, các khoản nợ tín dụng trong quá khứ, v.v. + - Đối với chương trình ước tính giá trị ngôi nhà tự động, bạn có thể thu thập dữ liệu từ các lần bán nhà trước đây và thông tin về bất động sản như kích thước, vị trí, v.v. + + +- **Bước 2: Huấn luyện mô hình**. Nhập các thông tin thu thập trên vào thuật toán máy học để tạo mô hình đáp ứng bài toán đưa ra, ví dụ: phát hiện biển báo, mô hình xếp hạng tín dụng, hoặc công cụ ước tính giá trị nhà. + + +- **Bước 3: Sử dụng mô hình dự đoán với dữ liệu mới**. Tích hợp mô hình vào một sản phẩm hoặc quy trình, chẳng hạn như ô tô tự lái, quy trình đăng ký tín dụng hoặc trang web thị trường bất động sản. + + +Trong thực tế, dữ liệu chúng ta có đôi khi không phải từ 1 nguồn mà từ rất nhiều nguồn khác nhau tổng hợp về 1 nguồn rồi xử lý dữ liệu rồi mới có thể huấn luyện được. Dưới đây là 1 hình ảnh ví dụ tổng quan mở rộng quy trình 3 bước cơ bản phía trên. + +![](./img/flow2.png) + +Phần tiếp theo, ta sẽ cũng nhau thực hành làm quen với bài toán học máy có giám sát thông qua bài toán phân loại giống hoa Iris. + +## II. THỰC HÀNH + +![](https://scikit-learn.org/stable/_static/ml_map.png) + +Các bạn mới làm quen với Học máy có thể tham khảo lộ trình trong ảnh trên. + +### 0. Cài đặt + +Trong buổi học này thì mình sẽ làm quen và thực hành chủ yếu trên scikit-learn API, một thư viện phổ biến khi làm việc với học máy ở tầm sơ và trung cấp. Để cài đặt thư viện, ta sử dụng câu lệnh quen thuộc dưới đây. + +```python +!pip install scikit-learn +``` + +Ta sẽ import 1 số thư viện cần thiết dưới đây để phục vụ cho bài thực hành. + + +```python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +from sklearn.model_selection import train_test_split +import seaborn as sns +``` + +### 1. Bài toán + +Chúng ta sẽ sử dụng tập dữ liệu Iris, chứa thông tin về ba giống hoa Iris khác nhau: Iris Versicolor, Iris Virginica, Iris Setosa với các phép đo của bốn biến: chiều dài đài hoa, chiều rộng đài hoa, chiều dài cánh hoa, chiều rộng cánh hoa. Mục đích của bài toán là để phân loại hoa Iris giữa ba loài (setosa, versicolor hoặc virginica) từ các phép đo chiều dài và chiều rộng của các lá đài và cánh hoa. + +Như vậy, mô hình của chúng ta sẽ có: +- **Đặc trưng**: Chiều dài đài hoa, chiều rộng đài hoa, chiều dài cánh hoa, chiều rộng cánh hoa. +- **Nhãn**: Iris Versicolor, Iris Virginica, Iris Setosa + +Tập dữ liệu Iris có một số tính năng thú vị: +- Một trong các lớp (Iris Setosa) có thể phân tách tuyến tính với hai lớp còn lại. Tuy nhiên, hai lớp khác không thể phân tách tuyến tính. +- Có một số trùng lặp giữa các lớp Versicolor và Virginica, vì vậy nó khó có thể đạt được tỷ lệ phân loại hoàn hảo. +- Có một số dư thừa trong bốn biến đầu vào, vì vậy có thể đạt được một giải pháp tốt chỉ với ba trong số chúng, hoặc thậm chí (với độ khó) từ hai, nhưng việc lựa chọn chính xác các biến tốt nhất là không rõ ràng. + +Lí do chọn Iris: +- Các thuộc tính là số nên bạn phải tìm cách tải và xử lý dữ liệu. +- Nó chỉ có 4 thuộc tính và 150 hàng, có nghĩa là nó nhỏ và dễ dàng vừa với bộ nhớ (và một màn hình hoặc trang A4). +- Tất cả các thuộc tính số đều có cùng đơn vị và cùng tỷ lệ, không yêu cầu bất kỳ tỷ lệ hoặc biến đổi đặc biệt nào để bắt đầu. +- Đây là một bài toán phân loại, cho phép bạn thực hành với một loại thuật toán học có giám sát dễ dàng hơn. + +```python +iris = sns.load_dataset('iris') +iris.head() +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sepal_lengthsepal_widthpetal_lengthpetal_widthspecies
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa
+ +### 2. EDA (Exploratory Data Analysis) + +#### 2.1 Đánh giá ở mức thống kê + + +```python +iris['species'].unique() +``` + + + + + array(['setosa', 'versicolor', 'virginica'], dtype=object) + + + + +```python +iris.shape, iris.size #row*columns +``` + + + + + ((150, 5), 750) + + + + +```python +iris.info() +``` + + + RangeIndex: 150 entries, 0 to 149 + Data columns (total 5 columns): + # Column Non-Null Count Dtype + --- ------ -------------- ----- + 0 sepal_length 150 non-null float64 + 1 sepal_width 150 non-null float64 + 2 petal_length 150 non-null float64 + 3 petal_width 150 non-null float64 + 4 species 150 non-null object + dtypes: float64(4), object(1) + memory usage: 6.0+ KB + + + +```python +iris.isnull().sum() +``` + + + + + sepal_length 0 + sepal_width 0 + petal_length 0 + petal_width 0 + species 0 + dtype: int64 + + + + +```python +iris['species'].value_counts() #iris.groupby('species').size() or iris.groupby('species').count() +``` + + + + + setosa 50 + versicolor 50 + virginica 50 + Name: species, dtype: int64 + + + + +```python +iris.describe() +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sepal_lengthsepal_widthpetal_lengthpetal_width
count150.000000150.000000150.000000150.000000
mean5.8433333.0573333.7580001.199333
std0.8280660.4358661.7652980.762238
min4.3000002.0000001.0000000.100000
25%5.1000002.8000001.6000000.300000
50%5.8000003.0000004.3500001.300000
75%6.4000003.3000005.1000001.800000
max7.9000004.4000006.9000002.500000
+ + + + +```python +iris.corr() +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sepal_lengthsepal_widthpetal_lengthpetal_width
sepal_length1.000000-0.1175700.8717540.817941
sepal_width-0.1175701.000000-0.428440-0.366126
petal_length0.871754-0.4284401.0000000.962865
petal_width0.817941-0.3661260.9628651.000000
+ +#### 2.2 Trực quan hoá dữ liệu + + +```python +species = iris['species'].value_counts() +species.plot(kind="bar") +plt.show() +``` + + + +![png](11.ml_1_files/11.ml_1_19_0.png) + + + + +```python +plt.pie(species, labels = species.index, autopct='%.2f%%', explode=[0,0.1,0]) +plt.show() +``` + + + +![png](11.ml_1_files/11.ml_1_20_0.png) + + + + +```python +iris.hist(figsize=(10,10)) +plt.show() +``` + + + +![png](11.ml_1_files/11.ml_1_21_0.png) + + + + +```python +sns.boxplot(data=iris, width=0.8) +plt.show() +``` + + + +![png](11.ml_1_files/11.ml_1_22_0.png) + + + + +```python +sns.heatmap(iris.corr(),annot=True) +plt.show() +``` + + + +![png](11.ml_1_files/11.ml_1_23_0.png) + + + + +```python +sns.pairplot(iris, hue = 'species', height = 1.5) +plt.show() +``` + + + +![png](11.ml_1_files/11.ml_1_24_0.png) + + + +### 3. Xây dựng mô hình + +#### 3.1 Chia tập huấn luyện và kiểm thử + + +```python +X_iris = iris.drop('species', axis = 1) +y_iris = iris['species'] +``` + + +```python +Xtrain, Xtest, ytrain, ytest = train_test_split(X_iris, y_iris, test_size = 0.2, random_state=1) #default 25% +``` + + +```python +print(Xtrain.shape, Xtest.shape) +print(ytrain.shape, ytest.shape) +``` + + (120, 4) (30, 4) + (120,) (30,) + + +#### 3.2 Huấn luyện mô hình + +Hồi quy logistic là phân tích hồi quy thích hợp để tiến hành khi biến phụ thuộc là nhị phân. Giống như tất cả các phân tích hồi quy, hồi quy logistic là một phân tích dự đoán. Về mặt toán học, một mô hình logistic có một biến phụ thuộc với hai giá trị có thể có, chẳng hạn như đạt / không đạt, thắng / thua, sống / chết, ... trong đó hai giá trị được gắn nhãn `0` và `1`. + + +```python +from sklearn.linear_model import LogisticRegression +``` + + +```python +model = LogisticRegression(max_iter=1000) +model.fit(Xtrain, ytrain) +``` + + + + + LogisticRegression(max_iter=1000) + + + + +```python +y_model = model.predict(Xtest) +``` + +#### 3.3 Đánh giá chất lượng mô hình + + +```python +from sklearn.metrics import accuracy_score, classification_report, confusion_matrix +``` + + +```python +print(accuracy_score(ytest, y_model)) +print(classification_report(ytest, y_model)) +``` + + 0.9666666666666667 + precision recall f1-score support + + setosa 1.00 1.00 1.00 11 + versicolor 1.00 0.92 0.96 13 + virginica 0.86 1.00 0.92 6 + + accuracy 0.97 30 + macro avg 0.95 0.97 0.96 30 + weighted avg 0.97 0.97 0.97 30 + + + +## III. TRIỂN KHAI VỚI FLASK + +Flask là một Web Framework rất nhẹ của Python, dễ dàng giúp người mới bắt đầu học Python có thể tạo ra website nhỏ. Flask cũng dễ mở rộng để xây dựng các ứng dụng web phức tạp. Ngoài Flask Framework, bạn có thể học PYTHON với Django Framework để xây dựng các ứng dụng web lớn hơn. Như đã nêu trước đó, Flask được phân loại là Web Framework siêu nhỏ, nhẹ. Thông thường, một framework vi mô là một framework tối giản hoặc không phụ thuộc vào thư viện bên ngoài. + +Để sử dụng Flask, ta cài thư viện sau: +```python +!pip install flask +``` + +Dưới đây là 1 số câu lệnh để ta làm quen với Flask. + + +```python +from flask import Flask + +app = Flask(__name__) #request Flask to create an application + +#flash routing +@app.route('/') #return a link that displays "hello world" +def hello(): + return "

Hello world

" + +@app.route('/') #return a link that displays "hello world" +def user(name): + return f"

This is {name}'s homepage

" + +if __name__ == '__main__': # the module that is being run is the main program + app.run() +# click ii to stop +``` + + * Serving Flask app '__main__' (lazy loading) + * Environment: production +  WARNING: This is a development server. Do not use it in a production deployment. +  Use a production WSGI server instead. + * Debug mode: off + + + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + + +Với bài toán phân loại giống hoa Iris ta đã thực hành, ta có thể tạo ra 1 giao diện đơn giản để triển khai dự đoán với những dữ liệu mới với Flask theo đoạn code dưới đây. + +Ta tạo file `model.py` chứa mô hình cùng 1 hàm đọc các dữ liệu mới để đưa vào mô hình đã huấn luyện và đưa ra dự đoán: + +```python +# Importing necessary libraries +import pandas as pd +import numpy as np +from sklearn.linear_model import LogisticRegression + +# Importing the dataset +data = pd.read_csv('iris.csv') + +# Dictionary containing the mapping +variety_mappings = {0: 'Setosa', 1: 'Versicolor', 2: 'Virginica'} + +# Encoding the target variables to integers +data = data.replace(['Setosa', 'Versicolor' , 'Virginica'],[0, 1, 2]) + +X = data.iloc[:, 0:-1] # Extracting the independent variables +y = data.iloc[:, -1] # Extracting the target/dependent variable + +logreg = LogisticRegression(max_iter=2000) # Initializing the Logistic Regression model +logreg.fit(X, y) # Fitting the model + +# Function for classification based on inputs +def classify(a, b, c, d): + arr = np.array([a, b, c, d]) # Convert to numpy array + arr = arr.astype(np.float64) # Change the data type to float + query = arr.reshape(1, -1) # Reshape the array + prediction = variety_mappings[logreg.predict(query)[0]] # Retrieve from dictionary + return prediction # Return the prediction +``` + +Bên cạnh đó, ta tạo 1 file `server.py` chung thư mục với `model.py`: + +```python +import model # Import the python file containing the ML model +from flask import Flask, request, render_template,jsonify # Import flask libraries + +# Initialize the flask class and specify the templates directory +app = Flask(__name__,template_folder="templates") + +# Default route set as 'home' +@app.route('/home') +def home(): + return render_template('home.html') # Render home.html + +# Route 'classify' accepts GET request +@app.route('/classify',methods=['POST','GET']) +def classify_type(): + try: + sepal_len = request.args.get('slen') # Get parameters for sepal length + sepal_wid = request.args.get('swid') # Get parameters for sepal width + petal_len = request.args.get('plen') # Get parameters for petal length + petal_wid = request.args.get('pwid') # Get parameters for petal width + + # Get the output from the classification model + variety = model.classify(sepal_len, sepal_wid, petal_len, petal_wid) + + # Render the output in new HTML page + return render_template('output.html', variety=variety) + except: + return 'Error' + +# Run the Flask server +if(__name__=='__main__'): + app.run(debug=True) +``` + +Khi đó, ta sử dụng câu lệnh `python server.py` trên terminal và truy cập đường dẫn hiện ra(thông thường sẽ là `http://127.0.0.1:5000/`). Chúc các bạn thử nghiệm và thành công! + +## ✅ Tóm tắt + +Trong bài này, chúng ta đã thực hành: + +- **Quy trình tổng quan**: Thu thập dữ liệu → Huấn luyện mô hình → Sử dụng mô hình dự đoán +- **Thực hành với tập dữ liệu Iris**: + - EDA (Exploratory Data Analysis) để hiểu dữ liệu + - Chia tập huấn luyện và kiểm thử + - Huấn luyện mô hình với các thuật toán khác nhau (Logistic Regression, Decision Tree, Random Forest, SVM, Neural Network) + - Đánh giá hiệu suất mô hình bằng accuracy, confusion matrix, classification report +- **Triển khai mô hình**: Sử dụng Flask để tạo web app đơn giản + +## 💡 Lưu ý quan trọng + +- Luôn chia dữ liệu thành tập huấn luyện và tập kiểm thử để đánh giá khách quan +- EDA là bước quan trọng để hiểu dữ liệu trước khi xây dựng mô hình +- Không phải mô hình phức tạp nhất luôn tốt nhất - đôi khi mô hình đơn giản lại hiệu quả hơn +- Accuracy không phải là metric duy nhất - cần xem xét precision, recall, F1-score +- Khi triển khai mô hình, cần xử lý lỗi và validate input từ người dùng + +## 🧪 Thực hành + +Hãy thử: + +1. Thử nghiệm với các thuật toán khác trong scikit-learn (K-NN, Naive Bayes) +2. Thay đổi tỷ lệ chia train/test (ví dụ: 70/30, 80/20) và quan sát sự thay đổi +3. Thử nghiệm với các tập dữ liệu khác từ scikit-learn (ví dụ: wine, breast_cancer) +4. Cải thiện Flask app bằng cách thêm validation cho input +5. Thử nghiệm với các hyperparameters khác nhau để cải thiện hiệu suất mô hình + +## ➡️ Bước tiếp theo + +Sau khi hoàn thành bài này, bạn có thể: + +- Tìm hiểu sâu hơn về các thuật toán học máy (cách chúng hoạt động, ưu/nhược điểm) +- Học về feature engineering - cách chọn và biến đổi features để cải thiện mô hình +- Tìm hiểu về cross-validation để đánh giá mô hình tốt hơn +- Học về hyperparameter tuning để tối ưu hóa mô hình +- Thực hành với các bài toán regression (dự đoán giá trị số) diff --git a/docs/tutorial/python/11.ml_1_files/11.ml_1_19_0.png b/docs/tutorial/python/11.ml_1_files/11.ml_1_19_0.png new file mode 100644 index 00000000..f8b175be Binary files /dev/null and b/docs/tutorial/python/11.ml_1_files/11.ml_1_19_0.png differ diff --git a/docs/tutorial/python/11.ml_1_files/11.ml_1_20_0.png b/docs/tutorial/python/11.ml_1_files/11.ml_1_20_0.png new file mode 100644 index 00000000..f4543735 Binary files /dev/null and b/docs/tutorial/python/11.ml_1_files/11.ml_1_20_0.png differ diff --git a/docs/tutorial/python/11.ml_1_files/11.ml_1_21_0.png b/docs/tutorial/python/11.ml_1_files/11.ml_1_21_0.png new file mode 100644 index 00000000..e724f859 Binary files /dev/null and b/docs/tutorial/python/11.ml_1_files/11.ml_1_21_0.png differ diff --git a/docs/tutorial/python/11.ml_1_files/11.ml_1_22_0.png b/docs/tutorial/python/11.ml_1_files/11.ml_1_22_0.png new file mode 100644 index 00000000..1e4c9964 Binary files /dev/null and b/docs/tutorial/python/11.ml_1_files/11.ml_1_22_0.png differ diff --git a/docs/tutorial/python/11.ml_1_files/11.ml_1_23_0.png b/docs/tutorial/python/11.ml_1_files/11.ml_1_23_0.png new file mode 100644 index 00000000..fdaecea1 Binary files /dev/null and b/docs/tutorial/python/11.ml_1_files/11.ml_1_23_0.png differ diff --git a/docs/tutorial/python/11.ml_1_files/11.ml_1_24_0.png b/docs/tutorial/python/11.ml_1_files/11.ml_1_24_0.png new file mode 100644 index 00000000..a17ed6f4 Binary files /dev/null and b/docs/tutorial/python/11.ml_1_files/11.ml_1_24_0.png differ diff --git a/docs/tutorial/python/img/flow2.png b/docs/tutorial/python/img/flow2.png new file mode 100644 index 00000000..42c9918a Binary files /dev/null and b/docs/tutorial/python/img/flow2.png differ diff --git a/website/scripts/parse_notebook.py b/website/scripts/parse_notebook.py index e0f9d550..5ac00006 100644 --- a/website/scripts/parse_notebook.py +++ b/website/scripts/parse_notebook.py @@ -35,9 +35,27 @@ def convert_notebook(ipynb_in_path, exporter, writer) -> None: nb = nbformat.reads(nb_str, nbformat.NO_CONVERT) # displayname is absent from notebook metadata - nb["metadata"]["kernelspec"]["display_name"] = "python3" + if "kernelspec" in nb["metadata"]: + nb["metadata"]["kernelspec"]["display_name"] = "python3" + # Fix output cells that might be missing required properties for cell in nb['cells']: + if cell.get('cell_type') == 'code': + # Ensure outputs list exists + if 'outputs' not in cell: + cell['outputs'] = [] + # Fix output properties + for output in cell['outputs']: + # Fix stream outputs missing 'name' + if output.get('output_type') == 'stream' and 'name' not in output: + output['name'] = 'stdout' + # Fix execute_result outputs missing 'execution_count' + if output.get('output_type') == 'execute_result' and 'execution_count' not in output: + output['execution_count'] = None + # Fix execute_result outputs missing 'metadata' + if output.get('output_type') == 'execute_result' and 'metadata' not in output: + output['metadata'] = {} + for f in preprocess: f(cell)