1.3.1.6 副本和视图
切片操作创建原数组的一个视图,这只是访问数组数据一种方式。因此,原始的数组并不是在内存中复制。你可以用 np.may_share_memory()
来确认两个数组是否共享相同的内存块。但是请注意,这种方式使用启发式,可能产生漏报。
**当修改视图时,原始数据也被修改:
In [32]:
a = np.arange(10)
a
Out[32]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [33]:
b = a[::2]
b
Out[33]:
array([0, 2, 4, 6, 8])
In [34]:
np.may_share_memory(a, b)
Out[34]:
True
In [36]:
b[0] = 12
b
Out[36]:
array([12, 2, 4, 6, 8])
In [37]:
a # (!)
Out[37]:
array([12, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [38]:
a = np.arange(10)
c = a[::2].copy() # 强制复制
c[0] = 12
a
Out[38]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [39]:
np.may_share_memory(a, c)
Out[39]:
False
乍看之下这种行为可能有些奇怪,但是这样做节省了内存和时间。
实例:素数筛选
用筛选法计算0-99之间的素数
- 构建一个名为
_prime
形状是 (100,) 的布尔数组,在初始将值都设为True:
In [40]:
is_prime = np.ones((100,), dtype=bool)
- 将不属于素数的0,1去掉
In [41]:
is_prime[:2] = 0
对于从2开始的整数 j
,化掉它的倍数:
In [42]:
N_max = int(np.sqrt(len(is_prime)))
for j in range(2, N_max):
is_prime[2*j::j] = False
- 看一眼
help(np.nonzero)
,然后打印素数 - 接下来:
- 将上面的代码放入名为
prime_sieve.py
的脚本文件 - 运行检查一下时候有效
- 使用埃拉托斯特尼筛法的优化建议
- 跳过已知不是素数的
j
- 第一个应该被划掉的数是$j^2$
- 跳过已知不是素数的
- 将上面的代码放入名为
1.3.1.7象征索引
Numpy数组可以用切片索引,也可以用布尔或整形数组(面具)。这个方法也被称为象征索引。它创建一个副本而不是视图。
1.3.1.7.1使用布尔面具
In [44]:
np.random.seed(3)
a = np.random.random_integers(0, 20, 15)
a
Out[44]:
array([10, 3, 8, 0, 19, 10, 11, 9, 10, 6, 0, 20, 12, 7, 14])
In [45]:
(a % 3 == 0)
Out[45]:
array([False, True, False, True, False, False, False, True, False,
True, True, False, True, False, False], dtype=bool)
In [47]:
mask = (a % 3 == 0)
extract_from_a = a[mask] # 或, a[a%3==0]
extract_from_a # 用面具抽取一个子数组
Out[47]:
array([ 3, 0, 9, 6, 0, 12])
赋值给子数组时,用面具索引非常有用:
In [48]:
a[a % 3 == 0] = -1
a
Out[48]:
array([10, -1, 8, -1, 19, 10, 11, -1, 10, -1, -1, 20, -1, 7, 14])
1.3.1.7.2 用整型数组索引
In [49]:
a = np.arange(0, 100, 10)
a
Out[49]:
array([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90])
索引可以用整型数组完成,其中相同的索引重复了几次:
In [50]:
a[[2, 3, 2, 4, 2]] # 注:[2, 3, 2, 4, 2] 是Python列表
Out[50]:
array([20, 30, 20, 40, 20])
用这种类型的索引可以分配新值:
In [51]:
a[[9, 7]] = -100
a
Out[51]:
array([ 0, 10, 20, 30, 40, 50, 60, -100, 80, -100])
当一个新数组用整型数组索引创建时,新数组有相同的形状,而不是整数数组:
In [52]:
a = np.arange(10)
idx = np.array([[3, 4], [9, 7]])
idx.shape
Out[52]:
(2, 2)
In [53]:
a[idx]
Out[53]:
array([[3, 4],
[9, 7]])
下图展示了多种象征索引的应用
练习:象征索引
- 同样,重新生成上图中所示的象征索引
- 用左侧的象征索引和右侧的数组创建在为一个数组赋值,例如,设置上图数组的一部分为0。