1.2. ndarray#
NumPy 最核心的数据结构是多维数组(N-dimensional Array): ndarray
。 ndarray
是由同一数据类型的数据组成的数组列表。
import numpy as np
数组 array#
数组 array (或 ndarray
) 是 NumPy 最为核心的数据结构。array 里的数据类型是一致的,比如都是整数还是浮点数,数据类型被记录在了 dtype
里。
数组可以通过非负整数元组、布尔、另一个数组或整数进行索引。数组的秩 rank
是维数。数组的形状 shape
给出了数组在每个维度上的大小。
生成一个 NumPy array 最简单的一种方法是从 Python 列表(list)开始。
# 数组 array
arr1 = np.array([1, 2, 3, 4])
arr1
array([1, 2, 3, 4])
arr2 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
arr2
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
我们可以使用方括号来查找数组中的元素,查找元素的过程又被称为索引(Indexing)。 NumPy 中的索引从 0 开始:如果你想索引数组中的第一个元素,那就用 0 作为索引。
print(arr2[0])
[1 2 3 4]
array 可以是一个数组,也可以是数组的数组,即高维数组。数组中的维数和项数由其形状 shape
决定。数组的形状 shape
是一组非负整数,指定每个维度的大小。
在 NumPy 中,维度被称为轴线 axes
。
例如,这有一个二维数组:
[[0., 0., 0.],
[1., 1., 1.]]
这个数组有两个轴 axes
。第一轴的长度为 2 ,第二轴的长度为 3 。
创建 ndarray
#
array 的底层数据结构是 ndarray
。
array()
函数接收来自原生 Python 的各类数据,如列表、元组等,并转化为 ndarray
;array()
函数也可以接收一个 NumPy 的 ndarray
。数组中的数据类型必须是一致的。
# 列表 list
ar1 = np.array([1,2,3,4])
ar1
array([1, 2, 3, 4])
# 元组 tuple
ar2 = np.array((1,2,3,4))
ar2
array([1, 2, 3, 4])
数据类型:dtype
#
在进一步深入了解各种创建 ndarray
的方式之前,我们需要了解一下数据类型的基础知识。计算机无法直接表征数值,其底层基于二进制表示数值,整数和浮点数(其实就是小数,计算机科学中一般称小数为浮点数)基于科学计数法,字符串是一个从整数到字符的映射。 NumPy 提供了不同的数据类型 dtype
,不同的 dtype
所能表示的区间范围不同。比如,同为整数,就有以下几种类型,其所表示的数值区间差异较大。
dtype |
区间 |
---|---|
np.int8 |
-128 ~ 127 |
np.int16 |
-32768 ~ 32767 |
np.int32 |
\(-2.1*10^9 \sim 2.1*10 ^9\) |
NumPy 所支持的数据类型主要有:
np.bool_
:布尔类型np.number
:又细分为整数(比如np.int8
等)和浮点类型(np.float32
等)。np.datetime64
:表示日期的数据类型np.character
:字符串类型
这些数据类型往往都带有一个数字,比如 np.int8
,数字表示这个数据类型占用了多少个比特(bit)的存储空间。1 个 bit 为一个 0/1 二进制。8 bit = 1 byte,即 8 比特位等于 1 个字节。
根据上表,整数占用的存储空间越大,所能表示的数值范围越大。当我们编写简单的程序时,数值范围一般不会出现问题,但是当我们编写复杂的科学计算程序时,数值范围决定了计算的精度。例如,np.int8
只能表示 -128 ~ 127 范围的数值,超过这个数值的数字,如何被 np.int8
表示,存在较大不确定性,进而造成程序运行不准确。
占用的 bit 越多,数据越精准,但也会使得对内存消耗越大。选择合适的数据类型有助于节省内存。刚刚接触数据类型的朋友,对于如何选择合适数据类型并不熟悉。大概可以按照如下准则:
整数类型默认是
np.int64
,但很多应用np.32
就足够了。浮点数类型一般科学计算应用都使用
np.float64
,深度学习类应用使用np.float32
或者甚至更小的数据类型也足够了。
创建特定的 ndarray
#
arange() 与 linspace()#
arange()
和 linspace()
函数可用于生成一个数值数组。数值从区间为 \([start,stop)\) 中选择,一般从 start
开始,到 stop
结束,在这个区间内生成一系列值。
arange()
函数的常见形式:
arange(stop)
arange(start, stop)
arange(start, stop, step)
其中,step
用于指定数值之间的间隔,默认为 1。
以下两种方式是等效的。
np.arange(10)
np.arange(stop=10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
linspace()
函数生成 num
个均匀间隔的数值,也就是创建一个等差数列。常见形式:linspace(start, stop, num)
。
生成在 1 和 5 之间 5 个均匀间隔的数组:
a = np.linspace(1,5,5)
a
array([1., 2., 3., 4., 5.])
ones() 与 ones_like()#
np.ones(shape)
生成 shape
大小的、全为 1 数组。例如,生成 \(3 \times 3 \times 6\) 的高维数组:
np.ones((2,3,4), dtype=np.float32)
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.]]], dtype=float32)
如果已经有一个多维数组 a
,我们想根据这个数组 a
的形状生成一个全为 1 的数组:
np.ones_like(a)
array([1., 1., 1., 1., 1.])
zeros() 与 zeros_like()#
np.zeros()
与 np.ones()
类似。np.zeros(shape)
生成 shape
大小的、全为 0 的数组。
np.zeros((2,3,4))
array([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
如果已经有一个多维数组 a
,我们想根据这个数组 a
的形状生成一个全为 0 的数组:
np.zeros_like(a)
array([0., 0., 0., 0., 0.])
full() 与 full_like()#
np.full(shape,val)
生成 shape
大小的、全为 val
的数组,即数组中每个元素值都是 val
。
np.full((2,3,4), 6)
array([[[6, 6, 6, 6],
[6, 6, 6, 6],
[6, 6, 6, 6]],
[[6, 6, 6, 6],
[6, 6, 6, 6],
[6, 6, 6, 6]]])
根据数组 a
的形状生成一个数组,元素值全为 val
。这里生成一个全为 6 的数组:
np.full_like(a, 6)
array([6., 6., 6., 6., 6.])
eye()#
np.eye(n)
创建一个 \(n \times n\) 单位矩阵,对角线为 1,其余为 0。
np.eye(3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
常用属性#
直接打印一个 ndarray
可以看到它的值、dtype
等属性。
ar = np.array([[1,1,2,3,],
[4,5,6,7],
[8,9,10,11]])
ar
array([[ 1, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
或者打印某些具体的属性。
ndim:多维数组的秩,或者说轴的数量,或者说数组有多少维度
ar.ndim
2
数组的尺度
ar.shape
(3, 4)
数组中元素的个数
ar.size
12
元素的数据类型
ar.dtype
dtype('int64')
每个元素的大小,以字节(Byte)为单位
ar.itemsize
8
数组维度变换#
我们经常要对数据进行一些变换。
不改变原数组#
reshape()
函数
reshape(shape)
函数,不改变数组的元素,根据 shape
形状,生成一个新的数组。
ar.reshape((2,6))
array([[ 1, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
flatten()
函数
flatten()
函数,对数组进行降维,将高维数组压缩成一个一维数组。
ar.flatten()
array([ 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
改变原数组#
resize()
函数
resize(shape)
函数,返回一个 shape
形状的数组,功能与 reshape()
函数一致,但是修改原数组。
Note
当新数组可容纳数据少于原数据,按照原数据选择前 shape
个数据;如果多于,则新数组会按照原数组中的数据顺序进行填补。
ar_new = np.resize(ar, (2, 5))
ar_new
array([[1, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
ar_new = np.resize(ar, (3, 5))
ar_new
array([[ 1, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 1, 1, 2]])
数据类型变换#
astype()
#
astype(new_type)
函数,对数组的数据类型进行类型变化,基于对原始数据的拷贝创建一个新的数组。比如,将 np.int
类型元素修改为 np.float64
类型:
new_ar = ar.astype(np.float64)
new_ar
array([[ 1., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
合并函数#
np.concatenate()
将两个或者多个数组合并成一个新数组。
b = np.linspace(1,9,7)
c = np.concatenate((a,b)) #将 a,b 两个数组合并成为 c
c
array([1. , 2. , 3. , 4. , 5. ,
1. , 2.33333333, 3.66666667, 5. , 6.33333333,
7.66666667, 9. ])
ndarray
与 Python 列表的转换#
列表作为 Python 中原始的数据类型,运算速度慢于 NumPy。如果需要与原生 Python 语言相适配,需做转换:
ar.tolist()
[[1, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]