Bỏ qua đến nội dung chính
Phân tích dữ liệu
Logo NumPy cạnh cửa sổ code trên nền xanh

NumPy là gì và vì sao pandas cần NumPy

AIDev TeamTham vấn chuyên môn: Nguyễn Việt Chung

Đăng 11 phút đọc

Mục lục · 18

NumPy là gì? Đó là library Python lưu số trong mảng cùng kiểu và tính trên cả mảng một lúc, nên nhanh hơn list nhiều lần. Bài so list với mảng NumPy bằng số đo thật ở bốn cỡ dữ liệu, tính doanh thu tám chi nhánh bằng vài dòng code và chỉ ra chỗ pandas dùng NumPy.

NumPy là library (thư viện code viết sẵn) của Python để lưu và tính toán trên mảng số. Khác với list, mảng NumPy chỉ chứa một kiểu dữ liệu và mọi phép tính chạy trên cả mảng một lúc bằng code đã biên dịch sẵn, nên nhanh hơn vòng for của Python hàng chục lần. pandas lưu các cột số bằng mảng NumPy, vì vậy hiểu NumPy giúp bạn hiểu vì sao pandas nhanh và vì sao nó báo lỗi kiểu dữ liệu.

Mấy điều cần nắm trước khi đọc tiếp:

  • Đối tượng chính của NumPy là ndarray, mảng n chiều. Mảng một chiều giống một dãy số, mảng hai chiều giống một bảng.

  • Trên máy ảo AIDev Cloud, phép nhân 1 triệu số với 2 bằng NumPy mất 0,76 mili giây, bằng list mất 53,3 mili giây.

  • Cùng 1 triệu số nguyên, list chiếm 36,0 MB RAM, mảng NumPy chiếm 8,0 MB.

  • Lệnh pip install pandas tự cài NumPy. Bản mới nhất trên PyPI ngày 08/11/2026 là NumPy 2.5.4.

NumPy là gì và ra đời từ đâu?

Theo trang giới thiệu của NumPy (đọc ngày 08/11/2026), NumPy ra đời năm 2005, xây tiếp trên hai library cũ hơn là Numeric và Numarray, người sáng lập là Travis Oliphant. NumPy là mã nguồn mở theo giấy phép BSD bản sửa đổi, ai cũng dùng miễn phí được.

Tài liệu chính thức gọi NumPy là package nền tảng cho tính toán khoa học trong Python. Trong thực tế, phần lớn library dữ liệu và AI phổ biến đều nhận hoặc trả về mảng NumPy: pandas, scikit-learn, matplotlib, và cả PyTorch khi chuyển dữ liệu qua lại. Bạn hiếm khi viết cả một project chỉ bằng NumPy, nhưng gần như project dữ liệu nào cũng có NumPy chạy bên dưới.

Mảng NumPy đầu tiên trông thế nào?

Dataset của khóa Phân tích dữ liệu với Python (tải ở lab.aidev.edu.vn/k2) có file doanh_thu_chi_nhanh.csv: doanh thu 12 tháng năm 2025 của tám chi nhánh trong một chuỗi cửa hàng mô phỏng, đơn vị triệu đồng. Dòng đầu file là tên tám chi nhánh, skiprows=1 bỏ qua dòng đó. File không có cột tháng, dòng thứ i là tháng thứ i, nên đọc thẳng thành một mảng 12 × 8:

python
import numpy as np

dt = np.loadtxt("doanh_thu_chi_nhanh.csv", delimiter=",", skiprows=1)
print(type(dt).__name__, dt.shape, dt.dtype, dt.ndim)
print(dt[:3])
print("tổng theo chi nhánh (axis=0):", dt.sum(axis=0))
print("tổng theo tháng (axis=1):", dt.sum(axis=1))
print("tháng cao nhất của mỗi chi nhánh:", dt.argmax(axis=0) + 1)
print("số ô > 1000 triệu:", (dt > 1000).sum())
print("tăng trưởng tháng 12 so tháng 1 (%):", ((dt[11] / dt[0] - 1) * 100).round(1))
Terminal chạy python co_ban.py: mảng ndarray 12 x 8 kiểu float64 hai chiều, ba dòng đầu, tổng theo chi nhánh từ 5190 đến 9036, tổng theo tháng từ 3263 đến 5874, tháng cao nhất của mỗi chi nhánh là 12 trừ chi nhánh thứ năm là 1, tăng trưởng tháng 12 so tháng 1 từ -1,4 đến 26,6 phần trăm

Dòng đầu in ba thuộc tính của mảng:

  • shape là (12, 8): 12 dòng, 8 cột.

  • dtype là float64: mọi ô đều là số thực 64 bit, vì loadtxt mặc định đọc ra số thực.

  • ndim là 2: mảng hai chiều.

Tham số axis là chỗ người mới hay nhầm. axis=0 tính dọc theo các dòng, nên ra một số cho mỗi cột (mỗi chi nhánh). axis=1 tính ngang theo các cột, nên ra một số cho mỗi dòng (mỗi tháng). Chi nhánh Bến Thành (cột thứ năm trong file) có tổng cao nhất, 9.036 triệu đồng. argmax(axis=0) trả về vị trí của giá trị lớn nhất trong mỗi cột, cộng thêm 1 để ra số tháng. Bảy chi nhánh đạt đỉnh vào tháng 12, riêng Bến Thành đạt đỉnh vào tháng 1 và là chi nhánh duy nhất có tháng 12 thấp hơn tháng 1 (giảm 1,4%).

Dòng (dt > 1000).sum() cho thấy một cách viết quen thuộc trong NumPy: phép so sánh tạo ra một mảng True/False cùng cỡ, rồi sum() đếm số ô True. Ở đây không ô nào vượt 1.000 triệu đồng.

Mảng NumPy khác gì list trong Python?

Cùng một phép toán, list và mảng NumPy cho ra kết quả khác hẳn nhau. Đây là chỗ đáng chạy thử trước khi đọc tiếp:

python
import numpy as np

a, b = [1, 2, 3], [10, 20, 30]
print("list + list:", a + b)
print("list * 2   :", a * 2)
x, y = np.array(a), np.array(b)
print("array + array:", x + y)
print("array * 2    :", x * 2)
Terminal chạy python list_khac_array.py: list cộng list nối thành 1, 2, 3, 10, 20, 30; list nhân 2 lặp thành 1, 2, 3, 1, 2, 3; array cộng array ra 11 22 33; array nhân 2 ra 2 4 6; trộn kiểu thành mảng chữ U32; int8 tràn số ra -126; cuối cùng ValueError operands could not be broadcast together with shapes (3,) (2,)

Với list, dấu + nối hai list, dấu * 2 lặp list hai lần. Với mảng NumPy, hai phép đó tính trên từng phần tử. Bảng dưới gom các điểm khác nhau mình thấy khi chạy:

Điểm so sánh

list Python

mảng NumPy

Kiểu phần tử

trộn được số, chữ, list con

một kiểu cho cả mảng (int64, float64…)

+ và * 2

nối, lặp list

cộng, nhân từng phần tử

Thêm phần tử

append, cỡ tự tăng

cỡ cố định, np.append tạo mảng mới

1 triệu số nguyên

36,0 MB

8,0 MB

Nhân 1 triệu số với 2

53,3 ms

0,76 ms

Script trong ảnh chạy thêm ba lệnh ngoài đoạn code trên: hai lệnh in ra dòng trộn kiểu và int8 tràn số, lệnh cuối gây lỗi broadcast (nói ở mục lỗi bên dưới). Hai dòng đầu là hai cái bẫy. np.array([1, "2", 3.5]) không báo lỗi mà đổi cả mảng sang chữ (kiểu <U32), nên phép cộng sau đó sẽ hỏng. Kiểu int8 chỉ chứa được số từ -128 đến 127, và 120 + 10 ra -126 mà không có cảnh báo nào. Với kiểu int64 mặc định thì phải tới số rất lớn mới tràn, nhưng khi bạn chủ động chọn kiểu nhỏ để tiết kiệm RAM thì nên nhớ giới hạn này.

NumPy nhanh hơn list bao nhiêu?

Mình đo hai phép tính ở bốn cỡ dữ liệu: cộng tất cả phần tử (sum) và nhân mọi phần tử với 2. Mỗi phép chạy năm lần và lấy trung vị (giá trị đứng giữa khi xếp từ nhỏ đến lớn). Hai bên chứa cùng các số: list tạo bằng list(range(n)), mảng tạo bằng np.arange(n). Đoạn chính của script, trong đó do là hàm chạy một phép năm lần rồi trả về trung vị:

python
t_sum_l, t_sum_a = do(lambda: sum(ds)), do(lambda: mang.sum())
t_nhan_l, t_nhan_a = do(lambda: [x * 2 for x in ds]), do(lambda: mang * 2)
Terminal chạy python list_vs_numpy.py với NumPy 2.5.4 và Python 3.12.3: ở 1.000 số sum nhanh hơn 5 lần, nhân 2 nhanh hơn 6,7 lần; 100.000 số 31 và 61,4 lần; 1 triệu số 33 và 70,5 lần; 10 triệu số 15,3 và 23,2 lần; bộ nhớ 1 triệu số list 36,0 MB, NumPy 8,0 MB

Số phần tử

sum: list / NumPy

Nhanh hơn

Nhân 2: list / NumPy

Nhanh hơn

1.000

0,008 / 0,002 ms

5,0 lần

0,037 / 0,005 ms

6,7 lần

100.000

0,868 / 0,028 ms

31,0 lần

4,727 / 0,077 ms

61,4 lần

1.000.000

8,668 / 0,262 ms

33,0 lần

53,289 / 0,756 ms

70,5 lần

10.000.000

88,734 / 5,789 ms

15,3 lần

601,778 / 25,930 ms

23,2 lần

Với 1.000 số, cả hai cách đều xong dưới 0,04 mili giây, chênh lệch không đáng để ý. Từ 100.000 số trở lên, NumPy nhanh hơn vài chục lần. Ở 10 triệu số, tỉ lệ giảm còn khoảng 15 đến 23 lần. Mình đoán nguyên nhân là mảng 80 MB không còn nằm gọn trong bộ nhớ đệm của CPU, nên NumPy cũng phải chờ RAM, nhưng mình chưa đo riêng để khẳng định.

Biểu đồ số lần NumPy nhanh hơn list khi nhân mọi phần tử với 2: 1.000 số 6,7 lần, 100.000 số 61,4 lần, 1 triệu số 70,5 lần, 10 triệu số 23,2 lần, đo trên máy ảo AIDev Cloud ngày 26/10/2026

List tốn RAM hơn vì mỗi phần tử là một đối tượng Python riêng (28 byte với số nguyên nhỏ) cộng thêm 8 byte cho con trỏ (địa chỉ trỏ tới đối tượng đó) nằm trong list. Mảng int64 chỉ cần đúng 8 byte cho mỗi số, xếp liền nhau trong bộ nhớ.

Vì sao pandas cần NumPy?

pandas lưu mỗi cột số của DataFrame (bảng dữ liệu của pandas) bằng một mảng NumPy. Mình kiểm tra trên 6.000 đơn hàng của cửa hàng mẫu Tiệm Mây (file don_hang_sach.csv trong cùng dataset):

python
import numpy as np
import pandas as pd

df = pd.read_csv("don_hang_sach.csv")
dt = df["doanh_thu"]
arr = dt.to_numpy()
print("cột doanh_thu:", dt.dtype, "->", type(arr).__name__, arr.dtype)
print("cột kenh     :", df["kenh"].dtype, "->", type(df["kenh"].array).__name__)
print("dt.sum() =", dt.sum(), "| np.sum(arr) =", np.sum(arr))
print("np.log10 trên Series ->", type(np.log10(dt)).__name__)
Terminal chạy python pandas_dung_numpy.py với pandas 3.1.0 và NumPy 2.5.4: cột doanh_thu int64 chuyển ra ndarray int64, cột kenh kiểu str nằm trong StringArray, tổng bằng pandas và bằng np.sum đều là 17.753.491.000, np.log10 trên Series trả về Series, so_luong nhân don_gia bằng doanh_thu ở mọi dòng

Kết quả cho thấy ba điều:

  • Cột doanh_thu kiểu int64 lấy ra bằng to_numpy() là một ndarray cùng kiểu. dt.sum() và np.sum(arr) cho cùng con số 17.753.491.000.

  • Hàm của NumPy chạy thẳng trên Series (một cột của DataFrame): np.log10(dt) trả về một Series, giữ nguyên index (nhãn của từng dòng).

  • Cột chữ thì khác. Ở pandas 3, cột kenh có kiểu str và nằm trong StringArray, một kiểu mảng riêng của pandas (máy mình chưa cài pyarrow; có pyarrow thì pandas dùng ArrowStringArray). Đây là một trong các thay đổi lớn của pandas 3, bài pandas là gì có so trực tiếp với pandas 2.

Vì cột số là mảng NumPy, phép df["so_luong"] * df["don_gia"] nhân 6.000 cặp số trong một lần gọi, không có vòng for nào của Python. Mình kiểm tra lại: tích hai cột bằng cột doanh_thu ở cả 6.000 dòng.

Cài NumPy thế nào?

Nếu đã cài pandas thì bạn đã có NumPy. Muốn cài riêng, chạy pip install numpy trong virtual environment (môi trường Python riêng cho từng thư mục làm việc). NumPy 2.5.4 cần Python từ 3.12 trở lên theo PyPI. Kiểm tra phiên bản bằng:

bash
python -c "import numpy; print(numpy.__version__)"

Khi chạy pip install pandas trên máy ảo Ubuntu 24.04 của AIDev Cloud, pip kéo theo NumPy 2.5.4 (file tải về 16,7 MB). Nếu gặp lỗi No module named numpy, nguyên nhân thường giống hệt lỗi với pandas: package đã vào một bản Python khác. Bài cài pandas và sửa lỗi No module named pandas có cách kiểm tra code đang chạy bằng bản Python nào.

Lỗi mình gặp khi dùng NumPy

Mảng NumPy không có append

Quen tay với list, mình gọi append trên mảng và nhận lỗi:

text
AttributeError: 'numpy.ndarray' object has no attribute 'append'
Terminal chạy loi_append.py báo AttributeError: 'numpy.ndarray' object has no attribute 'append'; sau đó them_phan_tu.py dùng np.append in ra mảng 619000 929000 2989000 658000 kiểu int64

Mảng NumPy có cỡ cố định. np.append(gia, 658000) không sửa gia mà tạo một mảng mới, nên phải gán lại: gia = np.append(gia, 658000). Nếu cần thêm phần tử nhiều lần trong vòng lặp, cách nhanh hơn là gom vào list trước, cuối cùng mới đổi sang mảng bằng np.array(ds) một lần.

Hai mảng khác cỡ: lỗi broadcast

Cộng mảng ba phần tử với mảng hai phần tử thì NumPy dừng ngay:

text
ValueError: operands could not be broadcast together with shapes (3,) (2,)

Broadcast là quy tắc NumPy dùng để tính trên hai mảng khác cỡ, ví dụ cộng một số vào cả mảng, hay cộng một dòng 8 số vào từng dòng của bảng 12 × 8. Quy tắc này chỉ chạy khi các chiều khớp nhau hoặc một bên có cỡ 1. Khi gặp lỗi này, hãy in .shape của hai mảng ra trước. Thường thì một mảng đã bị lọc mất vài phần tử ở bước trước đó.

Khi nào không nên dùng NumPy?

  • Dữ liệu ít, vài trăm phần tử, lại hay thêm bớt phần tử: list đơn giản hơn và chênh lệch tốc độ không đáng kể (với 1.000 số, cả hai đều dưới 0,04 mili giây).

  • Bảng có tên cột, cột chữ, ngày tháng: dùng pandas, đừng tự quản lý mảng NumPy theo số thứ tự cột.

  • Dữ liệu trộn nhiều kiểu trong cùng một dãy, như bản ghi khách hàng: NumPy sẽ ép tất cả về một kiểu, thường là chữ.

Câu hỏi thường gặp

NumPy có phải là một ngôn ngữ riêng không?

Không. NumPy là một library của Python, bạn viết code Python bình thường và gọi các hàm của NumPy. Phần tính toán bên trong chạy bằng code đã biên dịch sẵn (chủ yếu viết bằng C), nên nhanh hơn vòng lặp Python.

Học NumPy trước hay pandas trước?

Biết NumPy là gì ở mức như bài này là đủ để bắt đầu. Nếu mục tiêu là phân tích dữ liệu dạng bảng, bạn có thể học pandas trước và học NumPy song song khi cần. Tuy vậy, nắm shape, dtype, axis và cách tính trên cả mảng giúp bạn đọc lỗi của pandas dễ hơn nhiều. Nếu đi tiếp sang machine learning, NumPy là bắt buộc vì dữ liệu đưa vào model đều là mảng.

np là gì trong code Python?

np là tên viết tắt khi nạp NumPy bằng import numpy as np. Đây chỉ là quy ước, nhưng gần như mọi tài liệu đều dùng, nên bạn nên viết theo để code dễ đọc.

ndarray là gì?

ndarray (n-dimensional array) là kiểu mảng n chiều của NumPy. np.array(...), np.arange(...) hay np.loadtxt(...) đều trả về một ndarray.

Đọc tiếp

Chương 1 của ebook Phân tích dữ liệu với Python là NumPy và tư duy vector, dùng chính file doanh thu tám chi nhánh trong bài này, trước khi sang pandas ở các chương sau. Sách có 89 bài tập tự chấm. Nếu bạn học dữ liệu để đi tiếp sang AI, lộ trình AI Engineer từ số 0 đi từ Python, qua phân tích dữ liệu, đến machine learning.

Code chạy ngày 26/10/2026 trên máy ảo AIDev Cloud: Ubuntu 24.04.4, Intel Xeon E5-2696 v4 25 vCPU, 54 GiB RAM, Python 3.12.3, NumPy 2.5.4, pandas 3.1.0. Dữ liệu doanh thu chi nhánh và Tiệm Mây là dữ liệu mô phỏng của AIDev (giấy phép MIT).

Tham vấn chuyên môn

Nguyễn Việt Chung

Nhà sáng lập AIDev và VietPlatform · Thạc sĩ AI MSE

  • Tốt nghiệp thủ khoa xuất sắc Thạc sĩ AI MSE.
  • Đồng sáng lập, CTO E-University, SVOnline: số hóa 10+ đại học, 40.000+ sinh viên.
  • Xây hệ thống AI cho doanh nghiệp tại VietPlatform.
Biên soạn: AIDev Team · Rà soát · Quy trình biên tập