• 基础
  • 创建数组
  • 用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

数组中元素的类型,如numpy.int32numpy.int16numpy.float64

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]])

注意,不管是hstackvstack还是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]],方便一些基于维度的操作。