1.3.1.6 副本和视图

切片操作创建原数组的一个视图,这只是访问数组数据一种方式。因此,原始的数组并不是在内存中复制。你可以用 np.may_share_memory() 来确认两个数组是否共享相同的内存块。但是请注意,这种方式使用启发式,可能产生漏报。

**当修改视图时,原始数据也被修改:

In [32]:

  1. a = np.arange(10)
  2. a

Out[32]:

  1. array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [33]:

  1. b = a[::2]
  2. b

Out[33]:

  1. array([0, 2, 4, 6, 8])

In [34]:

  1. np.may_share_memory(a, b)

Out[34]:

  1. True

In [36]:

  1. b[0] = 12
  2. b

Out[36]:

  1. array([12, 2, 4, 6, 8])

In [37]:

  1. a # (!)

Out[37]:

  1. array([12, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [38]:

  1. a = np.arange(10)
  2. c = a[::2].copy() # 强制复制
  3. c[0] = 12
  4. a

Out[38]:

  1. array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [39]:

  1. np.may_share_memory(a, c)

Out[39]:

  1. False

乍看之下这种行为可能有些奇怪,但是这样做节省了内存和时间。

实例:素数筛选

prime

用筛选法计算0-99之间的素数

  • 构建一个名为 _prime 形状是 (100,) 的布尔数组,在初始将值都设为True:

In [40]:

  1. is_prime = np.ones((100,), dtype=bool)
  • 将不属于素数的0,1去掉

In [41]:

  1. is_prime[:2] = 0

对于从2开始的整数 j ,化掉它的倍数:

In [42]:

  1. N_max = int(np.sqrt(len(is_prime)))
  2. for j in range(2, N_max):
  3. 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]:

  1. np.random.seed(3)
  2. a = np.random.random_integers(0, 20, 15)
  3. a

Out[44]:

  1. array([10, 3, 8, 0, 19, 10, 11, 9, 10, 6, 0, 20, 12, 7, 14])

In [45]:

  1. (a % 3 == 0)

Out[45]:

  1. array([False, True, False, True, False, False, False, True, False,
  2. True, True, False, True, False, False], dtype=bool)

In [47]:

  1. mask = (a % 3 == 0)
  2. extract_from_a = a[mask] # 或, a[a%3==0]
  3. extract_from_a # 用面具抽取一个子数组

Out[47]:

  1. array([ 3, 0, 9, 6, 0, 12])

赋值给子数组时,用面具索引非常有用:

In [48]:

  1. a[a % 3 == 0] = -1
  2. a

Out[48]:

  1. array([10, -1, 8, -1, 19, 10, 11, -1, 10, -1, -1, 20, -1, 7, 14])

1.3.1.7.2 用整型数组索引

In [49]:

  1. a = np.arange(0, 100, 10)
  2. a

Out[49]:

  1. array([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90])

索引可以用整型数组完成,其中相同的索引重复了几次:

In [50]:

  1. a[[2, 3, 2, 4, 2]] # 注:[2, 3, 2, 4, 2] 是Python列表

Out[50]:

  1. array([20, 30, 20, 40, 20])

用这种类型的索引可以分配新值:

In [51]:

  1. a[[9, 7]] = -100
  2. a

Out[51]:

  1. array([ 0, 10, 20, 30, 40, 50, 60, -100, 80, -100])

当一个新数组用整型数组索引创建时,新数组有相同的形状,而不是整数数组:

In [52]:

  1. a = np.arange(10)
  2. idx = np.array([[3, 4], [9, 7]])
  3. idx.shape

Out[52]:

  1. (2, 2)

In [53]:

  1. a[idx]

Out[53]:

  1. array([[3, 4],
  2. [9, 7]])

下图展示了多种象征索引的应用 numpy_fancy_indexing

练习:象征索引

  • 同样,重新生成上图中所示的象征索引
  • 用左侧的象征索引和右侧的数组创建在为一个数组赋值,例如,设置上图数组的一部分为0。