- 基础
- 创建数组
- 用Python中的list或者tuple来创建
- 用填充函数创建数组
- 创建序列组成的数组(一维)
- 基础方法
- 通用方法
- 索引、切片和迭代
- 一维数组
- 多维数组
- 形状
- 改变形状
- 堆叠
- newaxis
基础
NumPy的主要对象是同质(元素为相同类型)的多维数组,其索引为一个由非负整数组成的tuple。在NumPy中,“维”由axis(轴,复数为axes)表示,“维度”由rank表示。
如:[[1,2,3],[4,5,6]]的维度为2,第一维长度为2,第二维长度为3。可以从外层括号开始数,第一层括号表示第一维,第二层括号表示第二维,等等。
NumPy数组的类是ndarray
。注:numpy.array和Python里面的array.array是不同的,array.array只处理一维数组且功能较少。
ndarray的一些重要属性:
attibutes | description |
ndarray.ndim | 数组的维数,即rank |
ndarray.shape | 数组各个维的大小,由一个tuple表示。易知,这个tuple的长度即为rank。 |
ndarray.size | 数组中元素的总数。易知,该值等于shape中所有元素的乘机。 |
ndarray.dtype | 数组中元素的类型,如 |
ndarray.itemsize | 各个元素占的空间大小(单位为byte),等同于ndarray.dtype.itemsize |
ndarray.data | 包含数组的实际元素的缓冲区,不常用。 |
创建数组
用Python中的list或者tuple来创建
用np.array()
创建。如果原来为整型则创建后类型为int64;对应的,浮点型为float64;如果既有整型又有浮点型,程序不会报错,而是都转成float64;有字符串的话都转成字符串类型
用填充函数创建数组
当创建一个数组时数组中的内容经常是未知的,而数组的shape已知。这种情况下可以使用np.zeros()函数和np.ones()函数创建一个由零、或者由1填充的数组。这种方法创建的数组类型默认为np.float64,类型可指定。
function | description |
np.zeros((d0,d1,…,dn)) | 全零,shape为(d0,d1,…,dn) |
np.ones((d0,d1,…,dn)) | 全一 |
np.empty((d0,d1,…,dn)) | 初始值随机,且取决于内存状态 |
np.zeros_like(arr) | 接收一个np.array作为参数,与其有相同的shape,但元素都为零 |
np.ones_like(arr) | 同上 |
np.empty_like(arr) | 同上 |
np.random.rand(d0,d1,…,dn) | 填充服从[0,1)均匀分布的随机数,shape为(d0,…,dn) |
np.random.randn(d0,d1,…,dn) | 填充服从标准正态分布N(0,1)的随机数。 |
如果要让随机数服从N(mu,segma^2),可以通过公式 n1 = segma*n0 + mu 获得,n0为标准正态分布的数组。
np.zeros((3,4)) #数组的shape为(3,4),元素都为0
np.ones((2,3,4),dtype=np.int16) #数组的shape为(2,3,4),元素都为0,类型为np.int16
创建序列组成的数组(一维)
类似Python中的range,可以使用np.arrange(p,q,a)
来创建一个首项为p,等差为a,末项小于q的数组。该方法接收float类型的参数。
另外,可以用np.linspace(p,q,n)
来创建首项为p,末项为q,总数为n的等差数列数组。数组类型为float。
基础方法
数组的数学运算都是基于每个元素的(element-by-element),运算会创建一个新的数组。不过和其他语言的矩阵运算有所不同,*运算是指两个矩阵的对应元素相乘,而不是矩阵乘法,矩阵乘法要用dot
方法,如:
C = A.dot(B)
C = np.dot(A,B) #两种方法皆可
ndarray支持的基本数学运算有:+, -, , +=, -=, =, **等。
+=和*=等运算是在原来的array上进行修改而不是创建一个新的array。
注意类型转换,一般两个不同类型的矩阵相乘时结果会向上转型。但是结果赋值给一个变量时两者的类型要相同,否则会报错。
ndarray支持许多一元操作,如sum、min、max等。
a = np.random.randn(2,3)
a.sum()
a.min()
a.max()
a.cumsum() #累计求和
这些操作可以沿某一维:
a.sum(axis=1)
可以根据上面的ndarray建立一个二维坐标系,那么axis=1就沿x轴计算,数据投影到y轴上形成一列;
如果是三维的ndarray可能更容易理解。假设一个ndarray的形状为(a, b, c),那么从左到右,axis=0表示z轴,axis=1表示y轴,axis=2表示x轴(因为axis是从外往里数),这个时候执行sum的时候指定axis=0就表示沿z轴求和,所有数据投影到xy平面上求和。因为xy面的形状是(b, c),所以求和得到的结果的形状也是(b, c)。
通用方法
function | description |
all | 测试是否所有元素值为真 |
any | 测试是否存在元素值为真 |
argmax | 返回值最大的元素的下标 |
argmin | 返回值最小的元素的下标 |
argsort | 间接排序,返回的数组是原数组排序后的下标序列。 |
average | 平均值 |
bincount | 计算非负整型数组中每个数出现的次数。res[5]表示数5出现的次数。 |
ceil | 向上取整 |
floor | 向下取整 |
exp(n) | e的n次方 |
索引、切片和迭代
ndarray的索引、切片和迭代的方式与普通python的sequence(tuple,list等)相似,不过一维数组和多维数组需要分开来看。
一维数组
一维数组就和sequence相同,比如:
a = np.arange(10)**3
a[2]
a[2:5]
a[:6:2] = -1000
a[::-1]
a[::-1]
会返回这个数组的倒序。这里很有意思,虽然第一个参数默认是0,但是如果明确指定为0的话返回结果又会不一样:
In [13]: a[::-1]
Out[13]: array([729, 512, 343, 216, 125, 64, 27, 8, 1, 0], dtype=int32)
In [14]: a[0::-1]
Out[14]: array([0], dtype=int32)
Python Tutorial中给出的关于slice的解释是,所有index都不是刚好对应一个数值,而是在数值之间,如:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
所以说a[0:3]
实际上是指索引0和索引3之间的内容,也就是’P’, ‘y’, ‘t’;而index是取单个元素,也就是取这个index后面接着的那个元素,比如a[0]
实际取的就是’P’。
a[begin index:end index:step]
这样解释也许能行得通。python在处理slice的时候,根据是否传入前两个参数有两套不同的方案。如果指定这两个参数(或者其中之一),那么begin index能取到,而end index取不到,两者指向同一个元素的话则是取不到;
如果不指定begin index,在step为正值的时候默认为0,当step为负值的时候默认为length-1(length为string或list的长度);如果不指定end index,在step为正值的时候默认为length,在step为负值的时候默认为-1**(不过这是看不到的-1,跟直接用index取到的-1不同,只是为了能包括到0)。**工作方式跟也许上图描述的更直观些,也就是step为-1的时候是取6到0之间的元素,但是和“index取的是与其相邻的下一个数”原则相违背,因为取不到index=0的元素,所以会采取这种方式。
多维数组
多维数组也很好理解,可以取到各个维度指定部分的重叠部分。
比如a[:,1]
表示取“所有行,第一列”,a[0:5,1]
表示取“0、1、2、3、4行,第一列”,a[1:3,:]
表示取“1、2行,所有列”。
另外,可以使用...
来省略掉一部分(全选的)内容。比如:
-
x[1,2,...]
与x[1,2,:,:,:]
相同, -
x[...,3]
与x[:,:,:,:,3]
相同, -
x[4,...,5,:]
与x[4,:,:,5,:]
相同。
迭代的话会直接迭代第一维(按括号从外向内数)的内容。比如[[1,2,3],[4,5,6]]
就会依次迭代[1,2,3]、[4,5,6]。当然也可以使用a.flat
迭代这个二维数组里的所有单个的元素:
In [41]: a = np.array([[1,2,3],[4,5,6]])
In [42]: for i in a.flat:
...: print(i)
...:
1
2
3
4
5
6
另外,a.ravel()
也可以获得这个二维数组(或多维)转化成一维的数组,不过这个函数返回的是np.ndarray,而上面的a.flat
是np.flatiter类型的值。
形状
改变形状
数组可以通过这些函数来改变自身的shape,并返回一个ndarray:
- 上面提到的
a.ravel()
,返回扁平后的一维数组, -
a.reshape(args)
返回reshape成特定形状的数组, -
a.T
返回这个数组的转置
堆叠
多个数组可以沿着某一维堆叠起来,比如:
In [14]: a
Out[14]:
array([[ 3., 0.],
[ 7., 4.]])
In [15]: b
Out[15]:
array([[ 9., 9.],
[ 9., 0.]])
In [17]: np.vstack((a,b))
Out[17]:
array([[ 3., 0.],
[ 7., 4.],
[ 9., 9.],
[ 9., 0.]])
In [18]: np.hstack((a,b))
Out[18]:
array([[ 3., 0., 9., 9.],
[ 7., 4., 9., 0.]])
column_stack
在处理二维数组的时候和hstack
相同,但是在处理一维数组时不同。column_stack
会把一维数组看作一列,然后按水平方向堆叠起来(如果两个数组长度都是3,则得到的结果就是3*2的数组),而hstack
则是直接把他们按水平方向堆叠(如果两个数组长度为3,则得到的结果是1*6的数组),当然,也可以使hstack
得到与column_stack
相同的结果,那就是把a改成a[:,np.newaxis]
,b也同理。
In [2]: a = np.array([1,2,3])
In [3]: b = np.array([4,5,6])
In [7]: np.column_stack((a,b))
Out[7]:
array([[1, 4],
[2, 5],
[3, 6]])
In [21]: np.hstack((a,b))
Out[21]: array([1, 2, 3, 4, 5, 6])
#使用newaxis来使结果和column_stack一致
In [22]: np.hstack((a[:,np.newaxis],b[:,np.newaxis]))
Out[22]:
array([[1, 4],
[2, 5],
[3, 6]])
注意,不管是hstack
、vstack
还是column_stack
,接收的参数都只有一个元组(包含多个数组),而不是多个单独的数组。
newaxis
Each newaxis object in the selection tuple serves to expand the dimensions of the resulting selection by one
unit-length dimension. The added dimension is the position of the newaxis object in the selection tuple.
newaxis的作用是在指定位置建立一个新的维,使这个位置之后的维数加一。使用np.newaxis和使用None的作用是相同的。
比如:
In [9]: a
Out[9]:
array([[1, 2, 3],
[4, 5, 6]])
这个时候第一维的内容有[1,2,3]
和[4,5,6]
,第二维的内容就分别是1,2,3和4,5,6这些独立的数字,a[1,1]
可以得到5。
如果使用a[None]
(或者a[np.newaxis]
),则表示使用当前内容生成第一维,也就是在原来的所有内容外面加一对括号,然后原来的第一维变成第二维,第二维变成第三维。a[0,1,1]
可以得到5:
In [12]: a = a[None]
Out[12]:
array([[[1, 2, 3],
[4, 5, 6]]])
如果使用a[:,None]
,则表示第一维不变,第二维是将原来的第二维括起来生成的新的维,而原来的第二维变成第三维。a[1,0,1]
可以得到5:
In [14]: a = a[:,np.newaxis]
Out[14]:
array([[[1, 2, 3]],
[[4, 5, 6]]])
使用这个方法的一个作用是,可以将原来一维的[1,2,3]拆分成二维的[[1],[2],[3]],方便一些基于维度的操作。