本节主要介绍numpy中在数组上的一些常规操作,在数组级别上包括数组迭代,数组拼接、数组分割,在元素级别包括元素迭代、元素增加、元素删除等。
本节的内容比较重要,同时也比较基础,是养成良好的编程习惯的重要的一个环节,因为每一个方法都可以通过最笨拙的索引方法去实现,但是这对于代码的可读性和程序的运行速度都是有影响的。

1. 迭代操作

迭代操作是最体现代码水平的,因为总是可以通过索引实现,这里介绍几种迭代的形式。

1.1 for i in a 进行数组的第一维度迭代

例如:

import numpy as np 
a = np.arange(16)
a.shape=(4,2,2)
print('a 数据为:',a)
b = np.array([1,2,3,4])
print('b 数组为:',b)

for i in a:
    print(i)

事实上,

for i in a:
    print(i)

可以改写为,

for i in range(a.shape[0]):
    print(a[i,:,:])

他们的结果是一样的。这种方式只能按照数组的第一维度进行迭代,返回的是数组第一维度的值,可能是数组也可能是元素(元素实际上是0维数组)。

1.2 使用numpy提供的迭代器nditer进行迭代。

nditer和python标准的迭代器iter一样,但是它既可以用来迭代数组也可以用来迭代元素,有可选的参数来控制,同时还能调整迭代的顺序,是nnumpy中比较实用的工具。
1) 迭代元素
此时按照默认的存储顺序返回元素:

import numpy as np 
a = np.arange(16)
a.shape=(4,4)
print('a 数据为:',a)
b = np.array([1,2,3,4])
print('b 数组为:',b)

for i in np.nditer(a):
    print(i)

输出:

a 数据为: [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
b 数组为: [1 2 3 4]
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

而此时,代码块:

for i in np.nditer(a):
    print(i)

相当于:

for i in range(a.shape[0]):
    for j in range(a.shape[1]):
        print(a[i,j])

至于那种写法比较好,一目了然。
上面是以行优先(第一维度)迭代数据,想要以列优先迭代数据,事实上以’F’顺序迭代就可以:

for i in np.nditer(a, order='F'):
    print(i)

同样等价于:

for i in range(a.shape[0]):
    for j in range(a.shape[1]):
        print(a[j,i])

默认创建数组的顺序为’c’,默认迭代元素顺序也是’C‘,通过显示的使用order='F'可以改变迭代顺序。

2) 数组迭代
默认迭代返回的是单个元素,使用参数flags = ['external_loop']将返回值变为数组,order='C'时,返回值压缩为一维数组,order='F',将每一列打包一个数组返回。

import numpy as np 
a = np.arange(16)
a.shape=(4,2,2)
print('a 数据为:',a)
# b = np.array([1,2,3,4])
# print('b 数组为:',b)

for i in np.nditer(a, flags =  ['external_loop'], order='C'):
    print('c顺序')
    print(i)

for i in np.nditer(a, flags =  ['external_loop'], order='F'):
    print('F顺序')
    print(i)

输出:

a 数据为: [[[ 0  1]
  [ 2  3]]

 [[ 4  5]
  [ 6  7]]

 [[ 8  9]
  [10 11]]

 [[12 13]
  [14 15]]]
c顺序
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]
F顺序
[ 0  4  8 12]
F顺序
[ 2  6 10 14]
F顺序
[ 1  5  9 13]
F顺序
[ 3  7 11 15]

可以看出,当参数为order='F'每次也是按行迭代,只是迭代了4次。说明nditer是按照内存储存的顺序来迭代的。

1.3 广播迭代

nditer也可以对多个数组同时迭代,当这些数组的维度大小不一样的时候,遵循numpy的广播机制。
import numpy as np 
a = np.arange(16)
a.shape=(4,4)
print('a 数据为:',a)
b = np.array([1,2,3,4])
print('b 数组为:',b)

for i,j in np.nditer([a,b]):
    print('iter value is %d:%d'%(i,j))

输出:

thonFiles\PythonTools\visualstudio_py_launcher.py' 'e:\home\Python\machineLearning' '14039' '34806ad9-833a-4524-8cd6-18ca4aa74f14' 'RedirectOutput,RedirectOutput' 'e:\home\Python\machineLearning\test.py'
a 数据为: [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
b 数组为: [1 2 3 4]
iter value is 0:1
iter value is 1:2
iter value is 2:3
iter value is 3:4
iter value is 4:1
iter value is 5:2
iter value is 6:3
iter value is 7:4
iter value is 8:1
iter value is 9:2
iter value is 10:3
iter value is 11:4
iter value is 12:1
iter value is 13:2
iter value is 14:3
iter value is 15:4

1.4 展开为一维数组迭代

迭代后结果为一维数组的迭代器有两个:flatten和ravel,同样两者都可以接受参数order=F,order:’C’ — 按行,’F’ — 按列,’A’ — 原顺序,’k’ — 元素在内存中的出现顺序

import numpy as np 
a = np.arange(10).reshape(2,5)
print('数组a为:\n',a)

b = a.flatten()
# 经过flatten迭代器迭代后的结果
print('flat 迭代后的结果:\n', b)
# 与其他迭代器一样,接收参数order,order='F'
c = a.flatten(order='F')
print('order=F迭代结果\n',c)
d = a.ravel()
# 经过ravel迭代
print('ravel 迭代后的结果:\n',d)

输出结果:

数组a为:
 [[0 1 2 3 4]
 [5 6 7 8 9]]
flat 迭代后的结果:
 [0 1 2 3 4 5 6 7 8 9]
order=F迭代结果
 [0 5 1 6 2 7 3 8 4 9]
ravel 迭代后的结果:
 [0 1 2 3 4 5 6 7 8 9]

可以看出,两者在功能上是完全一样的,但是两个还是有一个致命的区别:flatten返回的是拷贝,也就是从重新开辟了一块儿内存,与原数组无关。ravel返回的是原始数组的一个视图,结果与原数组是共内存的,修改结果会改变原数组的值,因而在使用上是需要注意的。

2. 数组拼接

在numpy的数组拼接中,常用的以下函数:

函数

参数

功能

numpy.concatenate((a1, a2, …), axis)

a1, a2, …:相同类型的数组序列,axis:沿着它连接数组的轴,默认为 0

沿着现存的轴连接数据序列,连接后新数组的维度不变

numpy.stack(arrays, axis)

arrays:相同形状的数组序列,axis:返回数组中的轴,输入数组沿着它来堆叠

沿着新轴连接数组序列,新数组的维度增加1

2.1 numpy.concatenate: 数组的连接是指元素上的连接。 此函数用于沿指定轴连接相同形状的两个或多个数组。连接的数组维度必须一样,连接轴的维度大小必须一样。

import numpy as np 
a = np.arange(16)
a.shape=(4,4)
print('a 数据为:',a)
b = np.array([[1,2,3,4]])
print('b 数组为:',b)

c = np.concatenate((a,b))
print(c)

输出:

a 数据为: [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
b 数组为: [[1 2 3 4]]
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [ 1  2  3  4]]

很显然,这是第一维度(行)拼接。下面是第二维度(列)拼接。

import numpy as np 
a = np.arange(16)
a.shape=(4,4)
print('a 数据为:',a)
b = np.array([[1],[2],[3],[4]])
print('b 数组为:',b)

c = np.concatenate((a,b),axis=1)
print(c)

输出:

a 数据为: [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
b 数组为: [[1]
 [2]
 [3]
 [4]]
[[ 0  1  2  3  1]
 [ 4  5  6  7  2]
 [ 8  9 10 11  3]
 [12 13 14 15  4]]

注意这两个例子中b数组的维度,拼接轴的维度大小一定要一样。

2.2 numpy.stack: 此函数沿新轴连接数组序列. 新数组的维度比原数组的维度大1。被拼接的数组的维度以及每个维度大小必须一样。

import numpy as np 
a = np.arange(4)
a.shape=(2,2)
print('a 数据为:',a)
b = np.array([[5,6],[7,8]])
print('b 数组为:',b)

c = np.stack((a,b),axis=0)
print(c)

输出:

a 数据为: [[0 1]
 [2 3]]
b 数组为: [[5 6]
 [7 8]]
[[[0 1]
  [2 3]]

 [[5 6]
  [7 8]]]

原数组是2维,新数组是3维。
c = np.stack((a,b),axis=0)改为:c = np.stack((a,b),axis=1)
输出:

a 数据为: [[0 1]
 [2 3]]
b 数组为: [[5 6]
 [7 8]]
[[[0 1]
  [5 6]]

 [[2 3]
  [7 8]]]

stack有两个扩充变体:
hstack:水平堆叠序列中的数组(列方向) 与函数np.concatenate((a,b),axis=1)等价
vstack :竖直堆叠序列中的数组(行方向) 与函数np.concatenate((a,b),axis=0)等价
注意这两个函数与stack的不同

3. 数组分割

数组分割常用到以下函数:

函数

参数

功能

numpy.split(ary, indices_or_sections, axis)

ary:被分割的输入数组,indices_or_sections:可以是整数,表明要从输入数组创建的,等大小的子数组的数量。 如果此参数是一维数组,则其元素表明要创建新子数组的点,axis:分割轴,默认为 0

该函数沿特定的轴将数组分割为子数组

import numpy as np 
a = np.arange(24)
a.shape=(6,4)
print('a 数据为:',a)
# b = np.array([[5,6],[7,8]])
# print('b 数组为:',b)

c = np.split(a,3)
print(c)

输出:

a 数据为: [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]
[array([[0, 1, 2, 3],
       [4, 5, 6, 7]]), array([[ 8,  9, 10, 11],
       [12, 13, 14, 15]]), array([[16, 17, 18, 19],
       [20, 21, 22, 23]])]

分割后元素的维度不变。上面是数组按轴等分分割,也可以标明分割位置:

c = np.split(a,[1,3,4])

输出:

a 数据为: 
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]
[array([[0, 1, 2, 3]]), array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]]), array([[12, 13, 14, 15]]), array([[16, 17, 18, 19],
       [20, 21, 22, 23]])]

注意分割[1,3,4]是分割位置,故产生了4个数组。

同样,他有两个变体:
numpy.hsplit是split()函数的特例,其中轴为 1 表示水平分割,无论输入数组的维度是什么。
numpy.vsplit是split()函数的特例,其中轴为 0 表示竖直分割,无论输入数组的维度是什么