NumPy (Numerical Python) là một thư viện cốt lõi trong hệ sinh thái Python, cung cấp các công cụ mạnh mẽ để thực hiện các phép tính khoa học và phân tích dữ liệu hiệu quả. Nó là nền tảng cho nhiều thư viện phân tích dữ liệu khác như Pandas, SciPy và scikit-learn.
Mặc dù Python là một ngôn ngữ đa năng, nhưng đôi khi có thể gặp hạn chế về độ chính xác hoặc hiệu suất khi xử lý các phép tính số học phức tạp hoặc lượng lớn dữ liệu. Ví dụ:
# Python có thể không luôn thể hiện độ chính xác tuyệt đối với số thập phân lớn
so_lon = 524552524.532523
chuoi_so = str(so_lon)
print(chuoi_so)
# Kết quả có thể được rút gọn, ví dụ: 524552524.5
Để giải quyết vấn đề này và cho phép Python thực hiện các tác vụ đòi hỏi độ chính xác cao như Trí tuệ Nhân tạo hay phân tích định lượng, các thư viện chuyên biệt như NumPy đã ra đời.
Đặc điểm nổi bật của NumPy:
- Cung cấp cấu trúc dữ liệu mảng đa chiều hiệu quả (
ndarray). - Hỗ trợ các hàm toán học phong phú để thực hiện các phép toán trên mảng.
- Tăng cường hiệu suất tính toán so với Python thuần túy.
Cài đặt và Sử dụng NumPy
Để cài đặt thư viện NumPy, bạn có thể sử dụng pip hoặc conda.
Sử dụng pip:
pip install numpy
Sử dụng conda (dành cho người dùng Anaconda):
conda install numpy
Sau khi cài đặt, bạn có thể nhập thư viện vào chương trình của mình. Thông thường, NumPy được nhập với biệt danh np để tiện lợi khi sử dụng:
import numpy as np
So sánh hiệu suất: Python thuần và NumPy
Để minh họa sự khác biệt về hiệu suất giữa Python thuần và NumPy, chúng ta sẽ thực hiện một ví dụ tính chỉ số BMI (Body Mass Index) cho một tập dữ liệu lớn.
Chuẩn bị dữ liệu mẫu
Chúng ta sẽ tạo 10 triệu bản ghi giả định về chiều cao và cân nặng.
import random
import time
# Tạo danh sách rỗng để lưu trữ chiều cao và cân nặng
ds_chieu_cao = [] # Đơn vị cm
ds_can_nang = [] # Đơn vị kg
# Tạo dữ liệu giả định
for i in range(10000000):
# Chiều cao ngẫu nhiên từ 153 đến 180 cm
ds_chieu_cao.append(random.randint(153, 180))
# Cân nặng ngẫu nhiên từ 51.0 đến 88.0 kg
ds_can_nang.append(random.uniform(51.0, 88.0))
Phương pháp 1: Tính BMI bằng Python thuần
Chỉ số BMI được tính theo công thức: Cân nặng (kg) / (Chiều cao (m))^2
start_time_py = time.perf_counter() # Sử dụng perf_counter để đo thời gian chính xác hơn
for i in range(len(ds_chieu_cao)):
can_nang_kg = ds_can_nang[i]
chieu_cao_m = ds_chieu_cao[i] / 100 # Chuyển cm sang mét
bmi = can_nang_kg / (chieu_cao_m ** 2)
end_time_py = time.perf_counter() - start_time_py
print(f"Thời gian tính bằng Python thuần: {end_time_py:.4f} giây")
Phương pháp 2: Tính BMI bằng NumPy
NumPy cho phép thực hiện các phép toán trên toàn bộ mảng một cách hiệu quả, không cần vòng lặp tường minh.
import numpy as np
# Chuyển đổi danh sách Python sang mảng NumPy
mang_chieu_cao = np.array(ds_chieu_cao)
mang_can_nang = np.array(ds_can_nang)
# Thực hiện phép tính BMI bằng NumPy
start_time_np = time.perf_counter()
# Chuyển chiều cao từ cm sang mét, sau đó tính bình phương
bmi_numpy = mang_can_nang / ( (mang_chieu_cao / 100) ** 2 )
end_time_np = time.perf_counter() - start_time_np
print(f"Thời gian tính bằng NumPy: {end_time_np:.4f} giây")
Kết luận về hiệu suất
Qua ví dụ trên, chúng ta có thể thấy rõ rằng NumPy mang lại hiệu suất vượt trội đáng kể so với Python thuần túy khi xử lý các phép toán trên tập dữ liệu lớn. Điều này là do NumPy được tối ưu hóa bằng C và Fortran ở cấp độ thấp, giúp thực hiện các phép tính số học nhanh hơn rất nhiều.
Mảng đa chiều (ndarray)
Cấu trúc dữ liệu chính của NumPy là ndarray (N-dimensional array), một đối tượng mảng đa chiều có cùng kiểu dữ liệu. Tất cả các phần tử trong một mảng NumPy phải có cùng một kiểu dữ liệu duy nhất.
Khi thực hiện các phép toán trên mảng NumPy, các thao tác được áp dụng cho từng phần tử tương ứng trong mảng một cách đồng thời (vectorized operations), miễn là các mảng có kích thước tương thích. Điều này giúp loại bỏ nhu cầu sử dụng vòng lặp Python tường minh, tăng tốc độ xử lý.
Mảng một chiều (Vector)
import numpy as np
mang_mot_chieu = np.array([10, 20, 30, 40, 50])
print(mang_mot_chieu)
Mảng hai chiều (Matrix)
mang_hai_chieu = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
print(mang_hai_chieu)
Kiểu dữ liệu trong NumPy
NumPy hỗ trợ nhiều kiểu dữ liệu số học chuyên biệt để tối ưu hóa việc lưu trữ và tính toán. Các kiểu dữ liệu này chủ yếu là số vì NumPy được thiết kế cho tính toán khoa học.
- Boolean:
bool_ - Số nguyên (Integer):
int_,int8,int16,int32,int64,uint8,uint16,uint32,uint64(số nguyên không dấu).Ví dụ,
int32có thể biểu diễn các giá trị trong khoảng từ -2^31 đến 2^31 - 1, vì nó sử dụng 32 bit. - Số thực (Floating-point):
float_,float16,float32,float64 - Số phức (Complex):
complex_,complex64,complex128
Việc sử dụng dấu gạch dưới (_) trong bool_, int_, float_, complex_ nhằm phân biệt chúng với các từ khóa kiểu dữ liệu sẵn có của Python, mặc dù chúng thường ánh xạ tới các kiểu dữ liệu mặc định của hệ thống (ví dụ: int_ thường là int64).
Các thuộc tính cơ bản của Mảng NumPy
Các mảng NumPy có một số thuộc tính hữu ích để kiểm tra cấu trúc và đặc điểm của chúng:
.T(Transpose): Trả về ma trận chuyển vị của mảng (đổi hàng thành cột, cột thành hàng).
matran_goc = np.array([
[1, 2, 3],
[4, 5, 6]
])
print("Ma trận gốc:\n", matran_goc)
print("Ma trận chuyển vị (matran_goc.T):\n", matran_goc.T)
.dtype: Cho biết kiểu dữ liệu của các phần tử trong mảng.print("Kiểu dữ liệu của matran_goc:", matran_goc.dtype) # Ví dụ: int64
.size: Trả về tổng số phần tử trong mảng.print("Tổng số phần tử trong matran_goc:", matran_goc.size) # Ví dụ: 6
.ndim: Trả về số chiều của mảng.print("Số chiều của matran_goc:", matran_goc.ndim) # Ví dụ: 2
.shape: Trả về một tuple biểu thị kích thước của từng chiều (số hàng, số cột, ...).print("Kích thước của matran_goc (shape):", matran_goc.shape) # Ví dụ: (2, 3)
Các phương thức tạo mảng phổ biến
Để tìm hiểu cách sử dụng chi tiết của bất kỳ phương thức nào trong Python/Jupyter Notebook, bạn có thể gõ tên phương thức và thêm dấu chấm hỏi (?) ở cuối rồi chạy ô, hoặc nhấn Shift + Tab sau khi gõ tên phương thức và mở dấu ngoặc đơn.
np.array(object, dtype=None): Chuyển đổi một đối tượng (như list) thành mảng NumPy.
list_du_lieu = [[10, 20, 30], [40, 50, 60]]
mang_tu_list = np.array(list_du_lieu)
print(mang_tu_list)
np.arange(start, stop, step, dtype=None): Tương tự như range() của Python nhưng hỗ trợ số thực và trả về mảng NumPy. (Giá trị stop không được bao gồm).mang_le = np.arange(1, 10, 2) # [1, 3, 5, 7, 9]
mang_float = np.arange(1.5, 12.0, 0.5) # [1.5, 2.0, ..., 11.5]
print("Dãy số lẻ:", mang_le)
print("Dãy số float:", mang_float)
np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): Tạo một dãy số cách đều nhau giữa start và stop (bao gồm cả stop).day_so_deu = np.linspace(0, 10, num=5) # 5 số từ 0 đến 10, bao gồm cả 0 và 10
print("Dãy số cách đều:", day_so_deu) # Output: [ 0. 2.5 5. 7.5 10. ]
# So sánh với arange, linspace bao gồm cả điểm kết thúc và đảm bảo số lượng phần tử.
np.zeros(shape, dtype=float): Tạo một mảng với tất cả các phần tử là 0.mang_khong = np.zeros((2, 3)) # Mảng 2 hàng, 3 cột toàn số 0
print("Mảng toàn số 0:\n", mang_khong)
np.ones(shape, dtype=float): Tạo một mảng với tất cả các phần tử là 1.mang_mot = np.ones((3, 2), dtype=int) # Mảng 3 hàng, 2 cột toàn số 1 (kiểu int)
print("Mảng toàn số 1:\n", mang_mot)
np.empty(shape, dtype=float): Tạo một mảng "rỗng" với kích thước đã cho. Các phần tử sẽ chứa các giá trị ngẫu nhiên (tùy thuộc vào bộ nhớ).mang_rong = np.empty(4) # Mảng 1 chiều với 4 phần tử ngẫu nhiên
print("Mảng rỗng (giá trị ngẫu nhiên):\n", mang_rong)
np.eye(N, M=None, k=0, dtype=float): Tạo một ma trận đơn vị (identity matrix) với các phần tử trên đường chéo chính là 1 và các phần tử còn lại là 0.ma_tran_don_vi = np.eye(3) # Ma trận đơn vị 3x3
print("Ma trận đơn vị 3x3:\n", ma_tran_don_vi)
Truy cập và Cắt lát Mảng NumPy
Truy cập và cắt lát mảng NumPy tương tự như với danh sách Python, nhưng có thêm các tùy chọn mạnh mẽ cho mảng đa chiều.
Mảng một chiều
Đối với mảng một chiều, cơ chế truy cập và cắt lát hoạt động giống hệt như danh sách Python.
mang_du_lieu = np.array([10, 12, 14, 16, 18, 20, 22, 24])
print("Phần tử tại chỉ số 3:", mang_du_lieu[3]) # Output: 16
print("Cắt lát từ chỉ số 2 đến 5 (không bao gồm 5):", mang_du_lieu[2:5]) # Output: [14, 16, 18]
Fancy Indexing (Chỉ mục phức tạp)
Cho phép chọn các phần tử không liên tiếp bằng cách truyền một danh sách hoặc mảng các chỉ số.
mang_chu_so = np.array([100, 200, 300, 400, 500, 600, 700, 800])
# Chọn các phần tử tại chỉ số 0, 2 và 5
chon_theo_chi_so = mang_chu_so[[0, 2, 5]]
print("Chọn theo chỉ số:", chon_theo_chi_so) # Output: [100 300 600]
Boolean Indexing (Chỉ mục logic)
Sử dụng một mảng boolean có cùng kích thước với mảng gốc để chọn các phần tử mà điều kiện là True.
mang_gia_tri = np.array([5, 12, 3, 25, 8, 30, 1])
# Chọn các phần tử lớn hơn 10
loc_lon_hon_10 = mang_gia_tri[mang_gia_tri > 10]
print("Các phần tử lớn hơn 10:", loc_lon_hon_10) # Output: [12 25 30]
Truy cập và Cắt lát Mảng hai chiều
Cú pháp chung: mang[chỉ_số_hàng, chỉ_số_cột]. Bạn có thể sử dụng chỉ số đơn, cắt lát, fancy indexing hoặc boolean indexing cho cả hàng và cột.
Sử dụng dấu hai chấm (:) để chọn tất cả các hàng hoặc tất cả các cột.
ma_tran = np.array([
[10, 11, 12, 13],
[20, 21, 22, 23],
[30, 31, 32, 33],
[40, 41, 42, 43],
[50, 51, 52, 53]
])
print("Phần tử tại hàng 3, cột 3 (chỉ số 0):", ma_tran[3, 3]) # Output: 43
print("Toàn bộ hàng thứ 3:", ma_tran[3, :]) # Output: [40 41 42 43]
print("Toàn bộ cột thứ 1:", ma_tran[:, 1]) # Output: [11 21 31 41 51]
print("Cắt lát hàng từ 2 đến 4 (không bao gồm 4), cột từ 1 đến 3 (không bao gồm 3):\n", ma_tran[2:4, 1:3])
# Output:
# [[31 32]
# [41 42]]
Các toán tử trên Mảng NumPy
NumPy hỗ trợ các toán tử số học và so sánh quen thuộc, áp dụng theo từng phần tử (element-wise) trên các mảng có kích thước tương thích.
Toán tử số học
Các toán tử +, -, *, /, // (chia nguyên), ** (lũy thừa) thực hiện phép toán tương ứng giữa các phần tử của hai mảng hoặc giữa một mảng và một số vô hướng.
mang_A = np.array([1, 3, 5, 7])
mang_B = np.array([4, 6, 5, 8])
print("mang_A + mang_B:", mang_A + mang_B) # [ 5 9 10 15]
print("mang_A - mang_B:", mang_A - mang_B) # [-3 -3 0 -1]
print("mang_A * mang_B:", mang_A * mang_B) # [ 4 18 25 56]
print("mang_A / mang_B:", mang_A / mang_B) # [0.25 0.5 1. 0.875 ]
print("mang_A // mang_B:", mang_A // mang_B) # [0 0 1 0]
print("mang_A ** 2:", mang_A ** 2) # [ 1 9 25 49]
Toán tử so sánh
Các toán tử so sánh (>, >=, <, <=, ==, !=) trả về một mảng boolean với kết quả của phép so sánh từng phần tử.
>hoặcnp.greater(mang_1, mang_2): Kiểm tra nếu phần tử củamang_1lớn hơn phần tử tương ứng củamang_2.
mang_X = np.array([10, 90, 50, 700])
mang_Y = np.array([40, 70, 50, 80])
print("mang_X > mang_Y:", mang_X > mang_Y) # [False True False True]
print("np.greater(mang_X, mang_Y):", np.greater(mang_X, mang_Y)) # [False True False True]
>= hoặc np.greater_equal(mang_1, mang_2)< hoặc np.less(mang_1, mang_2)<= hoặc np.less_equal(mang_1, mang_2)== hoặc np.equal(mang_1, mang_2)!= hoặc np.not_equal(mang_1, mang_2)Các hàm trong NumPy
NumPy cung cấp một bộ sưu tập lớn các hàm toán học và thống kê có thể áp dụng hiệu quả trên mảng.
Hàm toán học phổ biến
np.round(arr, decimals=0): Làm tròn các phần tử trong mảng.
gia_tri_thap_phan = np.array([1.2345, 1.6434, 7.05])
lam_tron = np.round(gia_tri_thap_phan, decimals=2) # Làm tròn 2 chữ số thập phân
print("Mảng đã làm tròn:", lam_tron) # Output: [1.23 1.64 7.05]
np.sqrt(arr): Tính căn bậc hai của từng phần tử.np.square(arr): Tính bình phương của từng phần tử.np.exp(arr): Tính e mũ (mũ tự nhiên) của từng phần tử.np.power(arr, exponent): Tính lũy thừa của từng phần tử với số mũ cho trước.mang_co_so = np.array([2, 3, 4])
ket_qua_luy_thua = np.power(mang_co_so, 3) # Mỗi phần tử mũ 3
print("Kết quả lũy thừa:", ket_qua_luy_thua) # Output: [ 8 27 64]
np.log2(arr): Tính logarit cơ số 2 của từng phần tử.np.log10(arr): Tính logarit cơ số 10 của từng phần tử.np.log(arr): Tính logarit tự nhiên (cơ số e) của từng phần tử.Hàm thống kê phổ biến (rất quan trọng)
Các hàm thống kê thường có tham số axis để chỉ định chiều mà phép tính sẽ được thực hiện:
axis=0: Tính toán theo cột (áp dụng cho tất cả các hàng trong mỗi cột).axis=1: Tính toán theo hàng (áp dụng cho tất cả các cột trong mỗi hàng).
np.min(arr, axis=None): Giá trị nhỏ nhất.np.max(arr, axis=None): Giá trị lớn nhất.np.mean(arr, axis=None): Giá trị trung bình.np.median(arr, axis=None): Giá trị trung vị.np.sum(arr, axis=None): Tổng các phần tử.np.std(arr, axis=None): Độ lệch chuẩn.np.var(arr, axis=None): Phương sai.
Ví dụ về hàm thống kê với axis
Chuẩn bị dữ liệu mẫu:
bang_diem = np.array([
[80.5, 60.0, 40.1, 20.0, 90.7],
[10.5, 30.0, 50.4, 70.3, 90.0],
[35.2, 35.0, 39.8, 39.0, 31.0],
[91.2, 83.4, 85.6, 67.8, 99.0]
])
print("Bảng điểm:\n", bang_diem)
Tính tổng điểm của từng hàng (từng học sinh):
# Cách 1: Sử dụng phương thức của mảng
tong_diem_moi_hang_1 = bang_diem.sum(axis=1)
print("Tổng điểm từng hàng (phương thức):", tong_diem_moi_hang_1)
# Cách 2: Sử dụng hàm NumPy
tong_diem_moi_hang_2 = np.sum(bang_diem, axis=1)
print("Tổng điểm từng hàng (hàm NumPy):", tong_diem_moi_hang_2)
Tính điểm trung bình của từng cột (từng môn học):
# Cách 1: Sử dụng phương thức của mảng
trung_binh_moi_cot_1 = bang_diem.mean(axis=0)
print("Điểm trung bình từng cột (phương thức):\n", trung_binh_moi_cot_1)
# Cách 2: Sử dụng hàm NumPy
trung_binh_moi_cot_2 = np.mean(bang_diem, axis=0)
print("Điểm trung bình từng cột (hàm NumPy):\n", trung_binh_moi_cot_2)
Tạo số ngẫu nhiên với NumPy
NumPy cung cấp mô-đun numpy.random để tạo các số ngẫu nhiên với nhiều phân phối khác nhau.
np.random.rand(d0, d1, ..., dn): Tạo mảng các số thực ngẫu nhiên có phân phối đều trong khoảng [0.0, 1.0).
mang_rand = np.random.rand(2, 3) # Mảng 2 hàng, 3 cột các số ngẫu nhiên từ 0 đến 1
print("Mảng rand:\n", mang_rand)
np.random.randint(low, high=None, size=None, dtype=int): Tạo mảng các số nguyên ngẫu nhiên trong khoảng [low, high).mang_randint = np.random.randint(10, 50, size=(3, 2)) # Số nguyên từ 10 đến 49, kích thước 3x2
print("Mảng randint:\n", mang_randint)
np.random.choice(a, size=None, replace=True, p=None): Lấy mẫu ngẫu nhiên từ một mảng 1 chiều đã cho.# Chọn 3 số ngẫu nhiên từ 0 đến 4 (tức là từ mảng [0, 1, 2, 3, 4])
chon_ngau_nhien_1 = np.random.choice(5, 3)
print("Chọn ngẫu nhiên (không thay thế):", chon_ngau_nhien_1) # Ví dụ: [2 0 4] (mặc định replace=True)
# Chọn 3 số với xác suất được chỉ định
chon_ngau_nhien_2 = np.random.choice(5, 3, p=[0.1, 0.2, 0.3, 0.2, 0.2])
print("Chọn ngẫu nhiên (có xác suất):", chon_ngau_nhien_2) # Ví dụ: [2 0 3]
np.random.shuffle(x): Xáo trộn thứ tự các phần tử của một mảng 1 chiều tại chỗ.day_so = np.arange(7) # [0 1 2 3 4 5 6]
print("Dãy số ban đầu:", day_so)
np.random.shuffle(day_so)
print("Dãy số đã xáo trộn:", day_so)
np.random.uniform(low=0.0, high=1.0, size=None): Tạo mảng các số thực ngẫu nhiên có phân phối đều trong khoảng [low, high).mang_uniform = np.random.uniform(1.0, 5.0, 4) # 4 số thực ngẫu nhiên từ 1.0 đến 5.0
print("Mảng uniform:", mang_uniform)
np.random.normal(loc=0.0, scale=1.0, size=None): Tạo mảng các số thực ngẫu nhiên có phân phối chuẩn (normal/Gaussian distribution).
loc(float): Giá trị trung bình (mean) của phân phối (trung tâm của đường cong).scale(float): Độ lệch chuẩn (standard deviation) của phân phối (độ rộng của đường cong).size(int hoặc tuple of ints): Kích thước của mảng đầu ra.
mang_normal = np.random.normal(loc=10, scale=2, size=(2, 2)) # Phân phối chuẩn, mean=10, std_dev=2, kích thước 2x2
print("Mảng normal (phân phối chuẩn):\n", mang_normal)
Thay đổi hình dạng mảng (reshape())
Phương thức reshape() cho phép thay đổi hình dạng (kích thước các chiều) của một mảng NumPy mà không làm thay đổi dữ liệu bên trong. Số lượng phần tử trong mảng phải không đổi.
import numpy as np
mang_goc = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
print("Mảng gốc (1 chiều):\n", mang_goc)
# Chuyển đổi thành mảng 2 chiều (3 hàng, 4 cột)
mang_2d = mang_goc.reshape((3, 4))
print("Mảng 2 chiều (3x4):\n", mang_2d)
# Chuyển đổi thành mảng 3 chiều (2 khối, mỗi khối 2 hàng, 3 cột)
mang_3d = mang_goc.reshape((2, 2, 3))
print("Mảng 3 chiều (2x2x3):\n", mang_3d)
# Sử dụng -1 để NumPy tự động tính toán kích thước chiều còn lại
# Ví dụ: Chuyển thành mảng có 1 hàng và NumPy tự tính số cột
mang_hang_don = mang_goc.reshape((1, -1))
print("Mảng 1 hàng, số cột tự động:\n", mang_hang_don)
# Ví dụ: Chuyển thành mảng có 2 cột và NumPy tự tính số hàng
mang_2_cot = mang_goc.reshape((-1, 2))
print("Mảng 2 cột, số hàng tự động:\n", mang_2_cot)