NUMPY
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.

Tạo mảng cơ bản với Numpy

Trong bài này ta sẽ đào sâu và tìm hiểu kỹ về cách tạo mảng cơ bản trong NumPy, cũng như sự hiệu quả của việc dùng NumPy so với List trong việc lưu trữ và thao tác với mảng.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Python là một ngôn ngữ động (dynamic language), do vậy việc khai báo biến trên Python cũng vô cùng đơn giản, ta xét đoạn code sau:

Trong C
int a = 2;
if (a % 2 == 0) {
  printf("%d la so chan", a);
}
Python
a = 2
if a % 2 == 0:
    print(a, “la so chan”)

Vì C là ngôn ngữ tĩnh (static language) nên các biến phải khai báo rõ ràng. Bạn có thể thấy trước biến a cần có tiền tố int (để khai báo rằng biến a thuộc kiểu dữ liệu integer) trong khi Python thì không cần điều này.

Chẳng hạn:

Bài viết này được đăng tại [free tuts .net]

Python
b = 5
b = “nam”
C/C++
int b = 5;
b = “nam” // Lỗi

Việc này khiến cho việc sử dụng Python trở nên tiện dụng hơn nhiều so với C, dù phải đánh đổi về hiệu năng. Ta không thể làm việc nhanh chóng mà phải để ý đến việc khai báo biến này thuộc kiểu dữ liệu này, kiểu dữ liệu kia, chưa kể sẽ xuất hiện hàng tá lỗi nếu set dữ liệu không cùng kiểu.

Đó cũng là lý do mà Python trở thành ngôn ngữ phổ biến nhất cho Data Science - đơn giản và tiện dụng. Trong các phần tiếp, ta sẽ tìm hiểu sâu về cơ chế hình thành nên 1 biến trong Python, đây là một khía cạnh quan trọng mà nhiều người hay bỏ qua, hiểu được những vấn đề cốt lõi này sẽ giúp việc phân tích dữ liệu một cách hiệu quả hơn.

1. Một biến trong Python được hình thành như thế nào?

Python vốn dĩ được viết trên C, do vậy hiển nhiên tất cả các biến của Python mà ta khai báo sẽ được khai báo trên C, mà ở đây chính là kiểu cấu trúc (struct).

Khi ta khai báo một số nguyên trên Python, chẳng hạn x = 100, thì nó không phải là số nguyên “thuần”, mà nó bản chất là một con trỏ và trỏ đến một struct trong C. Nếu tìm trong mã nguồn của Python 3 (CPython), một biến số nguyên được định nghĩa như thế này:

struct _longobject {
  long ob_refcnt;
  PyTypeObject *ob_type;
  size_t ob_size;
  long ob_digit[1];
};

Dễ thấy một biến số nguyên của Python gồm 4 phần:

  • ob_refcnt: tham chiếu cho Python cấp phát và giải phóng bộ nhớ
  • ob_type: mã hoá loại kiểu dữ liệu
  • ob_size: chỉ định kích thước của các dữ liệu thành viên
  • ob_digit: đây chính là nơi lưu giá trị số nguyên mà ta khai báo ban đầu

Ảnh dưới mô tả cách mà số nguyên trong C và Python được lưu trong bộ nhớ:

1 png

* Ghi chú: PyObject_HEAD chính là nơi chứa tất cả các tham chiếu, kiểu dữ liệu, kích thước,... đã đề cập ở trên (ob_refcnt, ob_type,...)

Từ đó, ta có thể thấy rõ được sự khác biệt của việc khai báo dữ liệu số nguyên trên Python so với C:

  • Một số nguyên trong C cơ bản là một nhãn (label) cho một vị trị trong bộ nhớ mà các byte đã mã hoá giá trị số nguyên, điều này khiến cho một biến đã khai báo kiểu dữ liệu trong C thì không thể set với kiểu khác được.
  • Còn trong Python, một số nguyên là một con trỏ chỉ đến một vị trí trong bộ nhớ nơi chứa một object đã để cập ở trên. Do vậy, ta có thể set kiểu dữ liệu khác mà không lo bị lỗi.

2. Cơ chế của List trong Python và sự hạn chế

Sau khi đã hiểu về cấu trúc của một biến trong Python, ta sẽ nhắc qua về List trong Python để nói về sự hạn chế của nó.

Chúng ta có thể tạo một mảng số nguyên trên Python như sau:

In [1]:
A = list(range(5))
A
Out [1]:
[0, 1, 2, 3, 4]
In[2]:
type(A[0])

Out[2]:
int

Hoặc một mảng với nhiều kiểu dữ liệu:

In[3]:
A1 = [True, "Freetuts", 1, 2.5]
[type(i) for i in A1]

Out[3]:
[bool, str, int, float]

Việc khai báo một mảng với nhiều kiểu dữ liệu đem tới nhiều sự thuận lợi, tuy nhiên ta có thể thấy rõ một vấn đề sau: Nếu trong mảng đều có chung kiểu dữ liệu thì sẽ tồn tại rất nhiều thông tin thừa (tham chiếu, kiểu dữ liệu,... trong PyObject_HEAD).

Do đó, List không thực sự tốt nếu ta cần xử lý các mảng dữ liệu nếu tất cả cùng chung một kiểu dữ liệu (mà hầu hết khi xử lý dữ liệu trong Data Science, mỗi mảng sẽ chỉ có một kiểu dữ liệu duy nhất). Vì vậy, sẽ hiệu quả hơn nhiều nếu như ta cố định toàn bộ kiểu dữ liệu vào trong một mảng nếu mảng đó chung kiểu dữ liệu (fixed-type arrays). Dù phải đánh đổi sự tiện lợi nhưng nó sẽ giúp thao tác và lưu trữ hiệu quả hơn, và đó chính là cách mà NumPy làm việc.

3. Tạo mảng với Numpy

Tạo từ List

Chúng ta có thể dùng nhiều cách để tạo fixed-type arrays trong Python, chẳng hạn từ Python 3.3 đi kèm với thư viện array:

In[4]:
import array
A = list(range(10))
A1 = array.array('i', L)
A1
Out[4]:
array('i', [0, 1, 2, 3, 4])

Note: “i” chính là viết tắt cho việc mảng chứa kiểu dữ liệu integers

Đây là một thư viện khá hữu ích, tuy nhiên nó chỉ mới cung cấp khả năng lưu trữ. Với ndarray - một object cốt lõi của NumPy thì ngoài lưu trữ thì nó còn có khả năng thao tác với dữ liệu (ta sẽ nói ở các bài sau).

Có rất nhiều cách để tạo mảng với NumPy, đầu tiên ta sẽ import NumPy vào notebook:

In[5]:
import numpy as np

Đầu tiên, ta có thể tạo mảng từ List bằng cách dùng np.array:

In[6]:
np.array([1, 6, 9, 12])

Out[6]:
array([ 1, 6, 9, 12])

Vì mảng NumPy bắt buộc phải cùng kiểu dữ liệu, nên nếu khác thì nó sẽ cố ép sao cho toàn bộ mảng cùng kiểu, chẳng hạn như:

In[7]:
np.array([6.99, 1, 2, 3])

Out[7]:
array([6.99, 1. , 2. , 3. ])

Ta thấy toàn bộ kiểu dữ liệu đã chuyển sang số thực để đồng bộ. Ngoài ra ta có thể khai báo trước kiểu dữ liệu của mảng:

In[8]:
np.array([1, 2, 3, 4], dtype='float32')

Out[8]:
array([1., 2., 3., 4.], dtype=float32)

Quan trọng nhất, mảng NumPy có thể đa chiều, không giống như List chỉ có thể lưu trữ dữ liệu 1 chiều, ví dụ:

In[9]:
np.array([range(i, i + 5) for i in [1, 2, 3, 4]])

Out[9]:
array([[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8]])

Tạo từ các hàm có sẵn

NumPy hỗ trợ rất nhiều hàm có sẵn để tạo mảng, tiện lợi hơn nhiều so với tạo từ List. Ta sẽ xem xét 1 số ví dụ:

Tạo mảng có 5 phần tử mà mọi giá trị đều bằng 0
In[10]:
# Tạo mảng có 5 phần tử mà mọi giá trị đều bằng 0
np.zeros(5, dtype=int)

Out[10]:
array([0, 0, 0, 0, 0])
Tạo mảng đa chiều kích thước 5x5 mà mọi giá trị đều = 1
In[11]:
# Tạo mảng đa chiều kích thước 5x5 mà mọi giá trị đều = 1
np.ones((5, 5), dtype=float)

Out[11]:
array([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]])
Tạo mảng đa chiều kích thước 3x3 mà mọi giá trị đều = 100
In[12]:
# Tạo mảng đa chiều kích thước 3x3 mà mọi giá trị đều = 100
np.full((3, 3), 100)

Out[12]:
array([[100, 100, 100],
[100, 100, 100],
[100, 100, 100]])

Ta sẽ đến với một số ví dụ nâng cao hơn, chẳng hạn:

Dùng np.arange để tạo mảng tuyến tính, bắt đầu bằng 0, kết thúc bằng 10, bước nhảy là 2:
In[13]:
np.arange(0, 10, 2)

Out[13]:
array([0, 2, 4, 6, 8])
Dùng np.linspace để tạo mảng tuyến tính với 5 phần tử và các phần tử có giá trị từ 0 đến 2:
In[14]:
np.linspace(0, 2, 5)

Out[14]:
array([0. , 0.5, 1. , 1.5, 2. ])
Tạo mảng 3x3 phân bố đều với các phần tử có giá trị ngẫu nhiên giữa 0 và 1:
In[15]:
np.random.random((3, 3))

Out[15]:
array([[0.74096074, 0.52767225, 0.48543925],
[0.10406574, 0.6248593 , 0.40501661],
[0.52078079, 0.82908192, 0.85961909]])
Tạo một mảng 3x3 với các phần tử ngẫu nhiên được phân phối chuẩn với giá trị trung bình là 0 và độ lệch chuẩn 1
In[16]:
np.random.normal(0, 1, (3, 3))

Out[16]:
array([[ 0.58069184, 0.00102128, 0.66747731],
[ 0.92574049, -0.24678111, 0.6781257 ],
[-0.60611321, -0.54344727, 0.67134354]])
Tạo một mảng 3x3 các số nguyên ngẫu nhiên trong khoảng [0, 10)
In[17]:
np.random.randint(0, 10, (3, 3))

Out[17]:
array([[6, 2, 4],
[9, 8, 8],
[9, 1, 5]])
Tạo ma trận đơn vị kích thước 3x3:
In[18]:
np.eye(3)

Out[18]:
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
Tạo mảng rỗng gồm 5 phần tử, giá trị của 5 phần tử này có thể là bất cứ giá trị nào đã lưu trên bộ nhớ trước đó:
In[19]:
np.empty(5)

Out[19]:
array([0. , 0.5, 1. , 1.5, 2. ])

4. Kiểu dữ liệu trong NumPy

Trong NumPy có nhiều kiểu dữ liệu khác nhau, trong bảng dưới là danh sách các kiểu dữ liệu hỗ trợ bởi NumPy (sẽ không lạ lắm với những người đã từng sử dụng C)

Các kiểu dữ liệu cơ bản của NumPy:

Kiểu dữ liệu Chú thích
bool_ Kiểu Boolean, giá trị True hoặc False
int_ Kiểu số nguyên mặc định (giống C long; thường là int64 hoặc int32
intc Giống hệt với int C (thường là int32 hoặc int64)
intp Số nguyên được sử dụng để lập chỉ mục (giống như C ssize_t; thông thường là int32 hoặc int64)
int8 Byte (–128 to 127)
int16 Số nguyên (–32768 đến 32767)
int32 Số nguyên (–2147483648 đến 2147483647)
int64 Số nguyên (–9223372036854775808 đến 9223372036854775807)
uint8 Số nguyên không dấu (0 đến 255)
uint16 Số nguyên không dấu (0 đến 65535)
uint32 Số nguyên không dấu (0 đến 4294967295)
uint64 Số nguyên không dấu (0 đến 18446744073709551615)
float_ Viết tắt cho float64
float16 Half-precision float: sign bit, 5 bits exponent, 10 bits mantissa
float32 Single-precision float: sign bit, 8 bits exponent, 23 bits mantissa
float64 Double-precision float: sign bit, 11 bits exponent, 52 bits mantissa
complex_ Viết tắt cho complex128
complex64 Số phức, được biểu diễn bởi 32 bit floats
complex128 Số phức, được biểu diễn bởi 64 bit floats

* Lưu ý: Ta có thể định dạng kiểu dữ liệu của mảng bằng 2 cách:

np.zeros(10, dtype='int16')

Hoặc

np.zeros(10, dtypenp.int16)

5. Tổng kết

Qua bài trên, ta đã tìm hiểu được cơ bản về NumPy, về cách tạo mảng, kiểu dữ liệu, cũng như hiểu về cách thức mà một biến trong Python được hình thành.

Đây là một bài rất quan trọng, các bạn nên thử trên notebook tất cả các kiểu tạo mảng trên để có thể nắm bắt được các phương thức mà NumPy hỗ trợ. Trong bài tiếp theo, ta sẽ cùng nhau khám phá các thao tác xử lý mảng với NumPy. Hẹn gặp các bạn ở bài tiếp theo nhé.

Cùng chuyên mục:

Cách thêm Progress Bar trong Python với chỉ một dòng Code

Cách thêm Progress Bar trong Python với chỉ một dòng Code

Toán tử Walrus Operator- Tính năng mới trong Python 3.8

Toán tử Walrus Operator- Tính năng mới trong Python 3.8

Cách nạp dữ liệu Machine Learning từ File trong Python

Cách nạp dữ liệu Machine Learning từ File trong Python

Hướng dẫn sử dụng Google Sheets API với Python

Hướng dẫn sử dụng Google Sheets API với Python

Xây dựng  web Python tự động hóa Twitter | Flask, Heroku, Twitter API & Google Sheets API

Xây dựng web Python tự động hóa Twitter | Flask, Heroku, Twitter API & Google Sheets API

Xây dựng Web Machine Learning đẹp mắt với Streamlit và Scikit-learn trong Python

Xây dựng Web Machine Learning đẹp mắt với Streamlit và Scikit-learn trong Python

Hướng dẫn tạo Chatbot đơn giản bằng PyTorch

Hướng dẫn tạo Chatbot đơn giản bằng PyTorch

11 mẹo và thủ thuật để viết Code Python hiệu quả hơn

11 mẹo và thủ thuật để viết Code Python hiệu quả hơn

Hướng dẫn làm ứng dụng TODO với Flask dành cho người mới bắt đầu trong Python

Hướng dẫn làm ứng dụng TODO với Flask dành cho người mới bắt đầu trong Python

Hướng dẫn viết Snake Game bằng Python

Hướng dẫn viết Snake Game bằng Python

Cách sử dụng chế độ interactive trong Python

Cách sử dụng chế độ interactive trong Python

Cách sử dụng Python Debugger với hàm breakpoint()

Cách sử dụng Python Debugger với hàm breakpoint()

Xây dựng ứng dụng Web Style Transfer với PyTorch và Streamlit

Xây dựng ứng dụng Web Style Transfer với PyTorch và Streamlit

Cách cài đặt Jupyter Notebook trong môi trường Conda và thêm Kernel

Cách cài đặt Jupyter Notebook trong môi trường Conda và thêm Kernel

Hướng dẫn xây dựng ứng dụng dự đoán giá cổ phiếu bằng Python

Hướng dẫn xây dựng ứng dụng dự đoán giá cổ phiếu bằng Python

Hướng dẫn tạo ứng dụng AI hội thoại với NVIDIA Jarvis trong Python

Hướng dẫn tạo ứng dụng AI hội thoại với NVIDIA Jarvis trong Python

Hỗ trợ Async trong Django 3.1

Hỗ trợ Async trong Django 3.1

8 mẹo tái cấu trúc Python giúp mã sạch hơn và Pythonic

8 mẹo tái cấu trúc Python giúp mã sạch hơn và Pythonic

Ý nghĩa của if __name__ ==

Ý nghĩa của if __name__ == "__main__" trong Python

Cách xóa phần tử trong danh sách Python

Cách xóa phần tử trong danh sách Python

Top