NumPy学习3

继续学习NumPy,今天学习以下3个章节:

7,NumPy高级索引
8,NumPy广播机制
9,NumPy遍历数组
numpy_test3.py :
import numpy as np

'''
7, NumPy高级索引
NumPy 与 Python 的内置序列相比,它提供了更多的索引方式。
在 NumPy 中还可以使用高级索引方式,比如整数数组索引、布尔索引以及花式索引
高级索引返回的是数组的副本(深拷贝),而切片操作返回的是数组视图(浅拷贝)。

1) 整数数组索引
整数数组索引,它可以选择数组中的任意一个元素,比如,选择第几行第几列的某个元素,
'''
print("-----------整数数组索引-------------")
#创建二维数组
arr1 = np.array([[1,  3],  [5,  7],  [9,  6]])
print('arr1 :', arr1)
#[0,1,2]代表行索引;[1,1,0]代表列索引
arr2 = arr1[[0,1,2],[1,1,0]]
print('arr2 :', arr2)
'''
arr1 : [ [1 3]
         [5 7]
         [9 6] ]
arr2 : [3 7 9]
分析:将行、列索引组合会得到 (0,1)、(1,1) 和 (2,0) ,它们分别对应着输出结果在原数组中的索引位置。
'''
#获取了 4*4 数组中的四个角上元素,它们对应的行索引是 [0,0] 和 [3,3],列索引是 [0,3] 和 [0,3]
arr3 = np.array([[ 0, 1, 2, 3],
              [ 4, 5, 6, 7],
              [ 8, 9, 10, 11],
              [ 12, 13, 14, 15]])
print('arr3 :', arr3)
row = np.array([[0,0],[3,3]])
col = np.array([[0,3],[0,3]])
#获取四个角的元素
arr4 = arr3[row,col]
print('arr4 :', arr4)
'''
arr3 : [ [ 0  1  2  3]
         [ 4  5  6  7]
         [ 8  9 10 11]
         [12 13 14 15] ]
arr4 : [ [ 0  3]
         [12 15] ]
分析:将行、列索引组合会得到 (0,0)、(0,3) 和 (3,0)、(3,3) ,它们分别对应着输出结果在原数组中的索引位置。
'''

'''
也可以将切片所使用的:或省略号...与整数数组索引结合使用
'''
print("-----------将切片所使用的:或省略号...与整数数组索引结合使用-------------")
#对行列分别进行切片
arr5 = arr3[1:3,1:3]
print('arr5 :', arr5)
#行使用基础索引,对列使用高级索引
arr6 = arr3[1:3,[1,2]]
#显示切片后结果
print('arr6 :', arr6)
#对行使用省略号...
arr7 = arr3[...,2:]
print('arr7 :', arr7)
#对列使用省略号...
arr8 = arr3[2:,...]
print('arr8 :', arr8)
'''
arr3 : [ [ 0  1  2  3]
         [ 4  5  6  7]
         [ 8  9 10 11]
         [12 13 14 15] ]
arr5 : [ [ 5  6]
         [ 9 10] ]
arr6 : [ [ 5  6]
         [ 9 10] ]
arr7 : [ [ 2  3]
         [ 6  7]
         [10 11]
         [14 15] ]
arr8 : [ [ 8  9 10 11]
         [12 13 14 15] ]
'''

'''
2) 布尔数组索引
当输出的结果需要经过布尔运算(如比较运算)时,此时会使用到另一种高级索引方式,即布尔数组索引。
'''
print("-----------布尔数组索引-------------")
arr9 = np.array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]])
print('arr9 :', arr9)
# 返回所有大于5的数字组成的数组
print('arr9[arr9 > 5] :', arr9[arr9 > 5])
'''
arr9 : [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
arr9[arr9 > 5] : [ 6  7  8  9 10 11]
'''
#可以使用补码运算符来去除 NaN(即非数字元素)
arr10 = np.array([np.nan, 1, 2, np.nan, 3, np.nan, 5, 6, 7])
print('arr10 :', arr10)
print('arr10[~np.isnan(arr10)] :', arr10[~np.isnan(arr10)])
#删除数组中非复数元素
arr11 = np.array([1, 2+3j, 3, 5, 3.2+4j, 3.2])
print('arr11 :', arr11)
print('arr11[np.iscomplex(arr11)] :', arr11[np.iscomplex(arr11)])
'''
arr10 : [nan  1.  2. nan  3. nan  5.  6.  7.]
arr10[~np.isnan(arr10)] : [1. 2. 3. 5. 6. 7.]
arr11 : [1. +0.j 2. +3.j 3. +0.j 5. +0.j 3.2+4.j 3.2+0.j]
arr11[np.iscomplex(arr11)] : [2. +3.j 3.2+4.j]
'''

'''
3) 花式索引(拓展知识)
花式索引也可以理解为整数数组索引,但是它们之间又略有不同,花式索引也会生成一个新的副本。

如果原数组是二维数组,那么索引数组也需要是二维的,索引数组的元素值与被索引数组的每一行相对应
'''
print("-----------花式索引-------------")
arr12 = np.arange(32).reshape((8, 4))
print('arr12 :', arr12)
# 分别对应 第3行数据、第2行数据、第1行数据、第6行数据项
arr13 = arr12[[3, 2, 1, 6]]
print('arr13 :', arr13)
# 也可以使用倒序索引数组
arr14 = arr12[[-3, -2, -1, -6]]
print('arr14 :', arr14)
# 还可以同时使用多个索引数组,但这种情况下需要添加np.ix_
arr15 = arr12[np.ix_([1, 3, 4, 2], [0, 3, 1, 2])]
print('arr15 :', arr15)
'''
arr12 : [[ 0  1  2  3]
         [ 4  5  6  7]
         [ 8  9 10 11]
         [12 13 14 15]
         [16 17 18 19]
         [20 21 22 23]
         [24 25 26 27]
         [28 29 30 31]]
         
arr13 : [[12 13 14 15]
         [ 8  9 10 11]
         [ 4  5  6  7]
         [24 25 26 27]]
         
arr14 : [[20 21 22 23]
         [24 25 26 27]
         [28 29 30 31]
         [ 8  9 10 11]]
         
arr15 : [[ 4  7  5  6]
         [12 15 13 14]
         [16 19 17 18]
         [ 8 11  9 10]]

其中 [1,3,4,2] 代表行索引,而 [0,3,1,2] 表示与行索引相对应的列索引值,也就是行中的元素值会按照列索引值排序。
比如,第一行元素,未排序前的顺序是 [4,5,6,7],经过列索引排序后变成了 [4,7,5,6]。
'''

'''
8,NumPy广播机制
NumPy 中的广播机制(Broadcast)旨在解决不同形状数组之间的算术运算问题。
我们知道,如果进行运算的两个数组形状完全相同,它们直接可以做相应的运算。
'''
print("-----------NumPy广播机制-------------")
a1 = np.array([0.1, 0.2, 0.3, 0.4])
b1 = np.array([10, 20, 30, 40])
c1 = a1 * b1
print('c1 :', c1)
# c1 : [ 1.  4.  9. 16.]
'''
但如果两个形状不同的数组呢?它们之间就不能做算术运算了吗?当然不是!为了保持数组形状相同,NumPy 设计了一种广播机制,
这种机制的核心是对形状较小的数组,在横向或纵向上进行一定次数的重复,使其与形状较大的数组拥有相同的维度。

当进行运算的两个数组形状不同,Numpy 会自动触发广播机制。
'''
a2 = np.array([[0, 0, 0], [5, 10, 20], [10, 20, 30]])
# b数组与a数组形状不同
b2 = np.array([1, 2, 3])
c2 = a2 * b2
print('c2 :', c2)
'''
c2 : [[ 0  0  0]
 [ 5 20 60]
 [10 40 90]]

3x3 的二维 a2 数组 与 1x3 的一维 b2 数组相乘,本质上可以理解为 b2 数组在纵向上向下拓展 3 次(将第一行重复 3 次),
b2 变成了 [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
从而生成与 a2 数组相同形状的数组,之后再与 a2 数组进行运算。
'''

'''
9, NumPy遍历数组
NumPy 提供了一个 nditer 迭代器对象,它可以配合 for 循环完成对数组元素的遍历。
'''
print("-----------NumPy遍历数组-------------")
arr16 = np.arange(0, 45, 5)
print('arr16 :', arr16)
arr17 = arr16.reshape(3, 3)
print('arr17 :', arr17)
# 使用nditer迭代器,并使用for进行遍历
for x in np.nditer(arr17):
    print(x, end=",")
print("\n------------------------")
'''
arr16 : [ 0  5 10 15 20 25 30 35 40]
arr17 : [[ 0  5 10]
 [15 20 25]
 [30 35 40]]
0,5,10,15,20,25,30,35,40,
'''

'''
1) 遍历顺序
在内存中,Numpy 数组提供了两种存储数据的方式,分别是 C-order(行优先顺序)与 Fortrant-order(列优先顺序)。
那么 nditer 迭代器又是如何处理具有特定存储顺序的数组呢?其实它选择了一种与数组内存布局一致的顺序,
之所以这样做,是为了提升数据的访问效率。

在默认情况下,当我们遍历数组中元素的时候,不需要考虑数组的存储顺序,这一点可以通过遍历上述数组的转置数组来验证。
'''
print("-----------遍历顺序-------------")
# 转置数组
arr18 = arr17.T
print('arr18 :', arr18)
for x in np.nditer(arr18):
   print(x, end=",")
print("\n------------------------")
'''
arr18 : [[ 0 15 30]
 [ 5 20 35]
 [10 25 40]]
0,5,10,15,20,25,30,35,40,
可以看出,输出结果可以看出,arr17 和 arr17.T 转置数组,的遍历顺序是一样的,
也就是说,它们在内存中的存储顺序是一样的。
'''
# 转置数组,并copy方法生成数组副本,存储方式变更
arr19 = arr17.T.copy(order='C')
print('arr19 :', arr19)
for x in np.nditer(arr19):
    print(x, end=",")
print("\n------------------------")
'''
arr19 : [[ 0 15 30]
 [ 5 20 35]
 [10 25 40]]
0,15,30,5,20,35,10,25,40,
'''

'''
2) 指定遍历顺序
您可以通过 nditer 对象的order参数来指定数组的遍历的顺序。
'''
print("-----------指定遍历顺序-------------")
print(" C-order(行优先顺序)")
for x in np.nditer(arr17, order='C'):
    print(x, end=",")
print("\n------------------------")
print(" F-order(列优先顺序)")
for x in np.nditer(arr17, order='F'):
    print(x, end=",")
print("\n------------------------")
'''
 C-order(行优先顺序)
0,5,10,15,20,25,30,35,40,
------------------------
 F-order(列优先顺序)
0,15,30,5,20,35,10,25,40,
'''

'''
3) 修改数组元素值
nditer 对象提供了一个可选参数op_flags,它表示能否在遍历数组时对元素进行修改。它提供了三种模式,如下所示:
(1) read-only
只读模式,在这种模式下,遍历时不能修改数组中的元素。
(2) read-write
读写模式,遍历时可以修改元素值。
(3) write-only
只写模式,在遍历时可以修改元素值。
'''
print("-----------遍历过程,修改数组元素值-------------")
arr20 = arr17.copy()
print("原数组是:", arr20)
for x in np.nditer(arr20, op_flags=['readwrite']):
    x[...] = 2*x    # 每个元素都乘2
print('修改后的数组是:', arr20)
'''
原数组是: [[ 0  5 10]
 [15 20 25]
 [30 35 40]]
修改后的数组是: [[ 0 10 20]
 [30 40 50]
 [60 70 80]]
'''

'''
4) 外部循环使用
nditer 对象的构造函数有一个“flags”参数,它可以接受以下参数值:

flags参数说明
参数值    描述说明
c_index    可以跟踪 C 顺序的索引。
f_index    可以跟踪 Fortran 顺序的索引。
multi_index    每次迭代都会跟踪一种索引类型。
external_loop    返回的遍历结果是具有多个值的一维数组。
'''
print("-----------外部循环使用-------------")
arr20 = arr17.copy()
print("原数组是:", arr20)
#修改后数组
print("修改后的一维数组")
for x in np.nditer(arr20, flags=['external_loop'], order='F'):
    print(x)
print("\n------------------------")
'''
原数组是: [[ 0  5 10]
 [15 20 25]
 [30 35 40]]
修改后的一维数组
[ 0 15 30]
[ 5 20 35]
[10 25 40]
'''

'''
5) 迭代多个数组
如果两个数组都能够被广播,那么 nditer 对象就可以同时对它们迭代。

假设数组 a3 的维度是 3*3,另一个数组 b3 的维度是 1*3 (即维度较小的数组 b3 可以被广播到数组 a3 中)
'''
print("-----------迭代多个数组,广播迭代-------------")
a3 = np.arange(0, 45, 5).reshape(3, 3)
print("a3 : ", a3)
b3 = np.array([1, 2, 3], dtype=int)
print("b3 : ", b3)
# 广播迭代
for x,y in np.nditer([a3, b3]):
    print("%d:%d" % (x, y), end="; ")
print("\n------------------------")
'''
a3 :  [[ 0  5 10]
 [15 20 25]
 [30 35 40]]
b3 :  [1 2 3]
0:1; 5:2; 10:3; 15:1; 20:2; 25:3; 30:1; 35:2; 40:3;  
'''

 

 
 
posted @ 2023-05-25 19:25  PandaCode辉  阅读(11)  评论(0编辑  收藏  举报