🎯 Mục tiêu bài học
Sau bài học này, bạn sẽ:
✅ Hiểu tại sao NumPy nhanh hơn Python list hàng trăm lần
✅ Tạo, thao tác và biến đổi ndarray thành thạo
✅ Áp dụng Broadcasting để tính toán hiệu quả
✅ Sử dụng các hàm toán học, thống kê, và random trong NumPy
✅ Chuẩn bị nền tảng cho Pandas, Matplotlib, Scikit-learn
Thời gian: 3 giờ | Độ khó: Beginner → Intermediate | Yêu cầu: Python cơ bản (Bài 1-3)
📖 Bảng Thuật Ngữ Quan Trọng
| Thuật ngữ | Tiếng Việt | Mô tả |
|---|---|---|
| ndarray | Mảng N chiều | Cấu trúc dữ liệu cốt lõi của NumPy |
| Vectorization | Véc-tơ hóa | Thao tác trên toàn bộ mảng thay vì từng phần tử |
| Broadcasting | Phát sóng | Tự động mở rộng shape để thực hiện phép tính |
| Shape | Kích thước | Tuple mô tả số phần tử mỗi chiều |
| dtype | Kiểu dữ liệu | Kiểu phần tử trong mảng (int32, float64...) |
| Indexing | Chỉ mục | Truy cập phần tử theo vị trí |
| Slicing | Cắt lát | Trích xuất một phần mảng |
| Axis | Trục | Chiều tính toán (0=dọc, 1=ngang) |
| Reshape | Biến đổi hình dạng | Thay đổi shape mà không đổi dữ liệu |
| Universal Function (ufunc) | Hàm toàn cục | Hàm áp dụng element-wise lên mảng |
Checkpoint
Bạn có thể giải thích sự khác biệt giữa Vectorization và Broadcasting không? Đây là 2 khái niệm quan trọng nhất!
📚 Tại sao cần NumPy?
Vấn đề với Python List
Python list linh hoạt nhưng chậm khi xử lý dữ liệu lớn:
1import time23# Python list: cộng 1 triệu phần tử4py_list = list(range(1_000_000))5start = time.time()6result = [x * 2 for x in py_list]7py_time = time.time() - start89# NumPy: cộng 1 triệu phần tử10import numpy as np11np_arr = np.arange(1_000_000)12start = time.time()13result = np_arr * 214np_time = time.time() - start1516print(f"Python list: {py_time:.4f}s")17print(f"NumPy array: {np_time:.4f}s")18print(f"NumPy nhanh hơn: {py_time/np_time:.0f}x")19# NumPy nhanh hơn: ~50-100xTại sao NumPy nhanh?
3 lý do NumPy nhanh hơn Python list:
- Contiguous Memory: Dữ liệu lưu liên tục trong bộ nhớ → cache-friendly
- Fixed dtype: Tất cả phần tử cùng kiểu → không cần type checking
- C Backend: Vòng lặp chạy bằng C, không phải Python interpreter
NumPy trong Hệ sinh thái Data Science
NumPy trong Hệ sinh thái Data Science
Checkpoint
Bạn có hiểu tại sao np.array([1,2,3]) * 2 nhanh hơn [x*2 for x in [1,2,3]] không?
🧱 Tạo và Khởi tạo Array
Từ Python list
1import numpy as np23# 1D array4a = np.array([1, 2, 3, 4, 5])5print(a, type(a)) # [1 2 3 4 5] <class 'numpy.ndarray'>67# 2D array (ma trận)8b = np.array([[1, 2, 3], 9 [4, 5, 6]])10print(b.shape) # (2, 3) — 2 hàng, 3 cột11print(b.dtype) # int6412print(b.ndim) # 2 chiều13print(b.size) # 6 phần tửHàm khởi tạo thường dùng
1# Mảng 0 và 12zeros = np.zeros((3, 4)) # Ma trận 3x4 toàn 03ones = np.ones((2, 3)) # Ma trận 2x3 toàn 14full = np.full((2, 2), 7) # Ma trận 2x2 toàn 756# Dãy số7arange = np.arange(0, 10, 2) # [0, 2, 4, 6, 8] — giống range()8linspace = np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1.0] — chia đều910# Ma trận đơn vị11eye = np.eye(3) # Ma trận đơn vị 3x31213# Random14rand = np.random.rand(3, 3) # Uniform [0, 1)15randn = np.random.randn(3, 3) # Normal (mean=0, std=1)16randint = np.random.randint(1, 100, (3, 3)) # Integer [1, 100)Chọn dtype phù hợp để tiết kiệm bộ nhớ:
np.float32thay vìfloat64→ tiết kiệm 50% RAMnp.int16cho số nhỏ (dưới 32768)np.bool_cho mảng True/False
1# Chỉ định dtype2a = np.array([1, 2, 3], dtype=np.float32)3print(a.nbytes) # 12 bytes (thay vì 24 với float64)Checkpoint
Tạo ma trận 5x5 với giá trị random từ 0-100 (integer). Gợi ý: np.random.randint()
🔍 Indexing và Slicing
Indexing là gì? Là cách truy cập phần tử cụ thể trong mảng theo vị trí. Slicing là cách cắt một phần của mảng.
Tại sao quan trọng? Trong Data Science, bạn thường cần: lấy 100 dòng đầu tiên, chọn cột cụ thể, lọc dữ liệu theo điều kiện — tất cả đều là indexing/slicing.
Basic Indexing (1D)
1a = np.array([10, 20, 30, 40, 50])23print(a[0]) # 10 — phần tử đầu4print(a[-1]) # 50 — phần tử cuối5print(a[1:4]) # [20, 30, 40] — slice6print(a[::2]) # [10, 30, 50] — step=22D Indexing
1matrix = np.array([[1, 2, 3],2 [4, 5, 6],3 [7, 8, 9]])45print(matrix[0, 1]) # 2 — hàng 0, cột 16print(matrix[1]) # [4, 5, 6] — toàn bộ hàng 17print(matrix[:, 0]) # [1, 4, 7] — toàn bộ cột 08print(matrix[0:2, 1:]) # [[2, 3], [5, 6]] — sub-matrixBoolean Indexing (Rất quan trọng!)
1scores = np.array([85, 92, 78, 95, 68, 88, 73])23# Mask: mảng True/False4passed = scores >= 805print(passed) # [True True False True False True False]67# Lọc bằng boolean mask8print(scores[passed]) # [85 92 95 88]910# Thay đổi giá trị theo điều kiện11scores[scores < 80] = 80 # Nâng điểm "sàn" lên 8012print(scores) # [85 92 80 95 80 88 80]Fancy Indexing
1a = np.array([10, 20, 30, 40, 50])23# Chọn nhiều phần tử bằng list index4print(a[[0, 2, 4]]) # [10, 30, 50]5print(a[[3, 1, 4]]) # [40, 20, 50] — thứ tự tùy ý67# np.where — tìm vị trí thỏa điều kiện8idx = np.where(a > 25)9print(idx) # (array([2, 3, 4]),)10print(a[idx]) # [30, 40, 50]View vs Copy: Slicing trả về view (tham chiếu), không phải copy!
1a = np.array([1, 2, 3, 4, 5])2b = a[1:4] # b là view3b[0] = 994print(a) # [1, 99, 3, 4, 5] — a cũng bị thay đổi!56# Muốn copy? Dùng .copy()7c = a[1:4].copy()8c[0] = 09print(a) # [1, 99, 3, 4, 5] — a không đổiCheckpoint
Cho arr = np.arange(20).reshape(4,5). Lấy ra tất cả phần tử chẵn ở cột cuối cùng.
🔄 Reshape và Manipulation
Reshape là gì? Là thay đổi hình dạng mảng mà không thay đổi dữ liệu. Ví dụ: mảng 12 phần tử có thể thành 3x4, 4x3, 2x6...
Tại sao cần reshape? Nhiều model ML yêu cầu input có shape cụ thể. Ví dụ: ảnh 28x28 pixel cần reshape thành 784 phần tử để đưa vào Neural Network, hoặc ngược lại.
Reshape — Thay đổi hình dạng
1a = np.arange(12)2print(a) # [ 0 1 2 3 4 5 6 7 8 9 10 11]34# Reshape sang 3x45b = a.reshape(3, 4)6print(b)7# [[ 0 1 2 3]8# [ 4 5 6 7]9# [ 8 9 10 11]]1011# -1: tự tính chiều còn lại12c = a.reshape(2, -1) # 2 hàng, NumPy tự tính 6 cột13print(c.shape) # (2, 6)1415# Flatten: về 1D16print(b.ravel()) # [ 0 1 2 ... 11] — view17print(b.flatten()) # [ 0 1 2 ... 11] — copyNối và Tách mảng
1a = np.array([1, 2, 3])2b = np.array([4, 5, 6])34# Nối5print(np.concatenate([a, b])) # [1 2 3 4 5 6]67# Stack8x = np.array([[1, 2], [3, 4]])9y = np.array([[5, 6], [7, 8]])1011print(np.vstack([x, y])) # Nối dọc → shape (4, 2)12print(np.hstack([x, y])) # Nối ngang → shape (2, 4)1314# Tách15arr = np.arange(12).reshape(3, 4)16a, b, c = np.split(arr, 3, axis=0) # Tách theo hàngTranspose
1m = np.array([[1, 2, 3],2 [4, 5, 6]])3print(m.T) # Chuyển vị4# [[1, 4],5# [2, 5],6# [3, 6]]7print(m.T.shape) # (3, 2)Checkpoint
Tạo mảng 1D gồm 24 phần tử, reshape thành (2, 3, 4). In shape và tổng theo từng axis.
📡 Broadcasting
Quy tắc Broadcasting
Khi thực hiện phép tính giữa 2 mảng khác shape, NumPy tự mở rộng mảng nhỏ:
3 quy tắc Broadcasting:
- Nếu 2 mảng khác số chiều → thêm chiều 1 ở bên trái mảng nhỏ
- Nếu kích thước ở 1 chiều khác nhau → mảng có size=1 được stretch theo chiều đó
- Nếu kích thước khác nhau mà KHÔNG có size=1 → LỖI
Ví dụ minh họa
1# Scalar + Array (phổ biến nhất)2a = np.array([1, 2, 3])3print(a + 10) # [11, 12, 13]4print(a * 2) # [2, 4, 6]56# Vector + Matrix7matrix = np.array([[1, 2, 3],8 [4, 5, 6],9 [7, 8, 9]])10row = np.array([10, 20, 30])1112print(matrix + row)13# [[11, 22, 33],14# [14, 25, 36],15# [17, 28, 39]]1617# Column vector + Matrix18col = np.array([[100], [200], [300]])19print(matrix + col)20# [[101, 102, 103],21# [204, 205, 206],22# [307, 308, 309]]Ứng dụng: Chuẩn hóa dữ liệu
1# Z-score normalization (rất quan trọng trong ML!)2data = np.array([[170, 60], # Chiều cao (cm), Cân nặng (kg)3 [180, 75],4 [165, 55],5 [175, 68]])67mean = data.mean(axis=0) # [172.5, 64.5]8std = data.std(axis=0) # [5.59, 7.63]910normalized = (data - mean) / std # Broadcasting!11print(normalized.round(2))12# [[-0.45, -0.59],13# [ 1.34, 1.38],14# [-1.34, -1.25],15# [ 0.45, 0.46]]Mẹo debug broadcasting: Dùng np.broadcast_shapes() để kiểm tra:
1np.broadcast_shapes((3, 1), (1, 4)) # → (3, 4) ✅2np.broadcast_shapes((3,), (4,)) # → Error ❌Checkpoint
Giải thích tại sao np.ones((3,1)) + np.ones((1,4)) cho kết quả shape (3,4)?
📊 Hàm Toán học và Thống kê
Tại sao cần hàm thống kê? Khi phân tích dữ liệu, bạn luôn cần biết: dữ liệu trung bình bao nhiêu? (mean), giá trị nào xuất hiện giữa? (median), dữ liệu phân tán ra sao? (std), giá trị cực đại/tiểu? (min, max). NumPy tính các giá trị này nhanh gấp trăm lần so với Python thuần.
Toán học cơ bản (Element-wise)
1a = np.array([1, 4, 9, 16, 25])23print(np.sqrt(a)) # [1. 2. 3. 4. 5.]4print(np.square(a)) # [1 16 81 256 625]5print(np.log(a)) # [0. 1.39 2.20 2.77 3.22]6print(np.exp([1,2])) # [2.718 7.389]7print(np.abs([-3,2])) # [3 2]Thống kê
1scores = np.array([85, 92, 78, 95, 68, 88, 73, 91, 82, 96])23print(f"Mean: {np.mean(scores):.1f}") # 84.84print(f"Median: {np.median(scores):.1f}") # 86.55print(f"Std: {np.std(scores):.1f}") # 8.86print(f"Var: {np.var(scores):.1f}") # 78.07print(f"Min: {np.min(scores)}") # 688print(f"Max: {np.max(scores)}") # 969print(f"Sum: {np.sum(scores)}") # 8481011# Vị trí min/max12print(f"ArgMin: {np.argmin(scores)}") # 4 (index của 68)13print(f"ArgMax: {np.argmax(scores)}") # 9 (index của 96)1415# Percentile16print(f"P25: {np.percentile(scores, 25):.1f}") # 77.017print(f"P75: {np.percentile(scores, 75):.1f}") # 91.75Thống kê theo Axis
Axis là gì? Khi có dữ liệu 2D (bảng), axis=0 tính theo cột (dọc xuống — mỗi môn học), axis=1 tính theo hàng (ngang qua — mỗi sinh viên). Đây là khái niệm bạn sẽ dùng hàng ngày trong Pandas!
1data = np.array([[85, 90, 78], # Student 12 [92, 88, 95], # Student 23 [76, 85, 80]]) # Student 34# Columns: Math, Physics, Chemistry56# Trung bình theo hàng (mỗi sinh viên)7print(np.mean(data, axis=1)) # [84.33 91.67 80.33]89# Trung bình theo cột (mỗi môn)10print(np.mean(data, axis=0)) # [84.33 87.67 84.33]Đại số tuyến tính
Tại sao cần? Nhiều thuật toán ML (Linear Regression, PCA, Neural Networks) đều dựa trên phép nhân ma trận. Bạn không cần thạo mối lắm về đại số tuyến tính — chỉ cần biết các hàm cơ bản:
1# Dot product2a = np.array([1, 2, 3])3b = np.array([4, 5, 6])4print(np.dot(a, b)) # 32 = 1*4 + 2*5 + 3*656# Matrix multiplication7A = np.array([[1, 2], [3, 4]])8B = np.array([[5, 6], [7, 8]])9print(A @ B) # [[19 22], [43 50]] — toán tử @10print(np.matmul(A, B)) # Tương đương1112# Determinant và Inverse13print(np.linalg.det(A)) # -2.014print(np.linalg.inv(A)) # [[-2, 1], [1.5, -0.5]]Checkpoint
Cho ma trận điểm 100 sinh viên × 5 môn (random 0-100). Tính: điểm TB mỗi môn, sinh viên điểm cao nhất, và số sinh viên dưới trung bình.
🎲 Random & Reproducibility
np.random — Sinh số ngẫu nhiên
1# Seed: đảm bảo kết quả lặp lại2np.random.seed(42)34# Uniform distribution [0, 1)5print(np.random.rand(3)) # [0.374 0.950 0.731]67# Normal distribution (Gaussian)8print(np.random.randn(3)) # [-0.256 0.840 -0.264]910# Custom range11print(np.random.uniform(10, 20, 5)) # 5 số từ 10-2012print(np.random.normal(100, 15, 5)) # 5 số, mean=100, std=1513print(np.random.randint(1, 7, 10)) # 10 lần tung xúc xắc1415# Random choice16items = ["A", "B", "C", "D"]17print(np.random.choice(items, 3, replace=True)) # Có lặp18print(np.random.choice(items, 3, replace=False)) # Không lặpModern Random (Recommended)
1# Cách mới: dùng Generator (NumPy ≥ 1.17)2rng = np.random.default_rng(seed=42)34print(rng.random(5)) # 5 số uniform5print(rng.normal(0, 1, 5)) # 5 số normal6print(rng.integers(1, 100, 5)) # 5 số integer78# Shuffle9arr = np.arange(10)10rng.shuffle(arr)11print(arr) # Xáo trộn ngẫu nhiênLuôn set seed khi viết code Data Science/ML để kết quả reproducible:
1np.random.seed(42)2# hoặc3rng = np.random.default_rng(42)Checkpoint
Mô phỏng tung 2 xúc xắc 10,000 lần. Đếm số lần tổng = 7. Xác suất lý thuyết là 6/36 = 16.67%.
📝 Tổng Kết
Tóm tắt kiến thức
| Chủ đề | Hàm/Khái niệm chính |
|---|---|
| Tạo array | np.array(), zeros(), ones(), arange(), linspace() |
| Indexing | a[0], a[1:4], a[mask], np.where() |
| Reshape | reshape(), ravel(), flatten(), T |
| Broadcasting | Scalar ↔ Array, Vector ↔ Matrix |
| Math/Stats | mean(), std(), sum(), median(), percentile() |
| Linear Algebra | dot(), @, linalg.inv(), linalg.det() |
| Random | seed(), rand(), randn(), choice(), default_rng() |
Quick Reference
1import numpy as np23# Tạo4a = np.array([1, 2, 3])5m = np.zeros((3, 4))67# Thuộc tính8a.shape, a.dtype, a.ndim, a.size910# Thao tác11a.reshape(1, -1) # → (1, 3)12a[a > 1] # Boolean indexing13np.concatenate() # Nối14np.vstack/hstack() # Stack1516# Toán17np.mean(a), np.std(a), np.sum(a)18a @ b # Matrix multiplyBài tiếp theo: Thực hành NumPy với các bài tập từ cơ bản đến nâng cao! 🎯
Câu hỏi tự kiểm tra
- NumPy array khác gì so với Python list? Tại sao NumPy nhanh hơn nhiều lần?
- Broadcasting trong NumPy là gì? Cho ví dụ khi cộng một scalar với một array.
- Boolean indexing hoạt động như thế nào? Viết cách lọc các phần tử lớn hơn 5 trong array.
- Tại sao cần set
np.random.seed()khi viết code Data Science/ML?
🎉 Tuyệt vời! Bạn đã hoàn thành bài học NumPy!
Tiếp theo: Thực hành NumPy với các bài tập từ cơ bản đến nâng cao để củng cố kiến thức!
