Numpy学习笔记(五)

常用概念

  • 副本和视图的区别,其中着重讲解两者返回值的不同点。
  • NumPy函数的很多事务(transaction)隐式使用的广播机制(broadcasting)

对象的副本或视图

你可能已经注意到, NumPy中,尤其是在做数组运算或数组操作时,返回结果不是数组的副本就是视图。 NumPy中,所有赋值运算不会为数组和数组中的任何元素创建副本

>>> a=np.array([1,2,3,4])
>>> b=a
>>> b
array([1, 2, 3, 4])
>>> a[2]=0
>>> b
array([1, 2, 0, 4])
>>> 

把数组a赋给数组b,实际上不是为a创建副本,b只不过是调用数组a的另外一种方式。
事实上,修改a的第三个元素,同样会修改b的第三个元素。
数组切片操作返回的对象只是原数组的视图

>>> c=a[0:2]
>>> c
array([1, 2])
>>> a[0]=0
>>> c
array([0, 2])
>>> 

如上所见,即使是切片操作得到的结果,实际上仍指向相同的对象。
如果想为原数组生成一份完整的副本,从而得到一个不同的数组,使用copy()函数即可。

>>> a=np.array([1,2,3,4])
>>> c=a.copy()
>>> c
array([1, 2, 3, 4])
>>> a[0]=0
>>> c
array([1, 2, 3, 4])
>>> 

上面的例子中,即使改动数组a的元素,数组c仍保持不变。

向量化

向量化和广播这两个概念是 NumPy内部实现的基础。有了向量化,编写代码时无需使用显式循环。这些循环实际上不能省略,只不过是在内部实现,被代码中的其他结构代替。向量化的应用使得代码更简洁,可读性更强,你可以说使用了向量化方法的代码看上去更“ Pythonic".向量化使得很多运算看上去更像是数学表达式,例如, NumPy中两个数组相乘可以表示为:

a×b

甚至两个矩阵相乘也可以这么表示

A×B

其他语言的上述运算要用到多重for结构。
例如,计算数组相乘:

for(i=0;i<rows;i++){
	c[i]=a[i]*b[i];
	}

计算矩阵相乘

for(i=0;i<rows;i++){
	for(j=0;j<columns;j++){
		c[i][j]=a[i][j]*b[i][j]
		}
	}

由上可见,使用 NumPy时,代码的可读性更强,其表达式更像是数学表达式。

广播机制

广播机制这一操作实现了对两个或以上数组进行运算或用函数处理,即使这些数组形状并不完全相同。
并不是所有的维度都要彼此兼容才符合广播机制的要求,但它们必须要满足一定的条件。

前面讲过,在 NumPy中,如何通过用表示数组各维度长度的元组(也就是数组的型)把数组
转换成多维数组。

因此,若两个数组的各维度兼容,也就是两个数组的每一维等长,或其中一个数组为一维那么广播机制就适用。如果这两个条件都不能满足, NumPy就会抛出异常,说两个数组不兼容。

>>> A=np.arange(16).reshape(4,4)
>>> b=np.arange(4)
>>> A
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
>>> b
array([0, 1, 2, 3])
>>> 

执行完上述代码后。我们就得到两个数组:
4×4
4
广播机制有两条规则。
第一条是为缺失的维度补上个1.如果这时满足兼容性条件,就可以应用广播机制,再来看第二条规则。
4×4
4×1
兼容性规则满足之后,再来看一下广播机制的第二条规则。这一规则解释的是如何扩展最小的数组,使得它跟最大的数组大小相同,以便使用元素级的函数或运算符。
第二条规则假定缺失元素(一维)都用已有值进行了填充
Numpy学习笔记(五)_第1张图片
既然两个数组维度相同,.它们里面的值就可以相加。

>>> A+b
array([[ 0,  2,  4,  6],
       [ 4,  6,  8, 10],
       [ 8, 10, 12, 14],
       [12, 14, 16, 18]])
>>> 

上例这种情况比较简单,一个数组较另一个小。还有更复杂的情况,即两个数组形状不同维度不同、互有长短。

>>> m=np.arange(6).reshape(3,1,2)
>>> n=np.arange(6).reshape(3,2,1)
>>> m
array([[[0, 1]],

       [[2, 3]],

       [[4, 5]]])
>>> n
array([[[0],
        [1]],

       [[2],
        [3]],

       [[4],
        [5]]])
>>> 

即使是这种复杂情况,分析两个数组的形状,你也会发现它们相互兼容,因此广播规则仍然适用。
3×1×2
3×2×1
这种情况下,两个数组都要扩展维度(进行广播)
Numpy学习笔记(五)_第2张图片
然后,就可以对两个数组进行诸如加法这样的元素级运算。

>>> m+n
array([[[ 0,  1],
        [ 1,  2]],

       [[ 4,  5],
        [ 5,  6]],

       [[ 8,  9],
        [ 9, 10]]])
>>> 

结构化数组

通过前面几节的多个例子,我们讲了一维数组和二维数组。在 NumPy中,不仅可以创建规模更为复杂的数组,还可以创建结构更为复杂的数组,后者叫作结构化数组( structured array),它包含的是结构或记录而不是独立的元素。

例如,你可以创建一个简单的结构化数组,其中元素为结构体。你可以用 dtype选项,指定系列用逗号隔开的说明符,指明组成结构体的元素及它们的数据类型和顺序。
Numpy学习笔记(五)_第3张图片
例如,你想指定由一个整数、一个长度为6的字符串和一个布尔值组成的结构体,就要在 dtype选项中按顺序指定各自的说明符。

>>> structured=np.array([(1,'first',0.5,1+2j),(2,'second',1.3,2-2j),(3,'thied',0.8,1+3j)],dtype=('i2,a6,f4,c8'))
>>> structured
array([(1, b'first', 0.5, 1.+2.j), (2, b'second', 1.3, 2.-2.j),
       (3, b'thied', 0.8, 1.+3.j)],
      dtype=[('f0', '), ('f1', 'S6'), ('f2', '), ('f3', ')])
>>> 

你还可以在数据类型( dtype)选项中明确指定每个元素的类型,如int8、uint8、f1oat16、complex64等。

>>> structured=np.array([(1,'first',0.5,1+2j),(2,'second',1.3,2-2j),(3,'thied',0.8,1+3j)],dtype=('int16,a6,float32,complex64'))
>>> structured
array([(1, b'first', 0.5, 1.+2.j), (2, b'second', 1.3, 2.-2.j),
       (3, b'thied', 0.8, 1.+3.j)],
      dtype=[('f0', '), ('f1', 'S6'), ('f2', '), ('f3', ')])
>>> 

然而,上述两种做法结果相同。生成的数组中, dtype序列包含结构体各项的名称及相应的数据类型。
使用索引值,就能获取到包含相应结构体的行。

>>> structured[1]
(2, b'second', 1.3, 2.-2.j)
>>> 

自动赋给结构体每个元素的名称可以看成数组列的名称。用它们作为结构化索引,就能引用类型相同或是位于同列的元素。

>>> structured['f1']
array([b'first', b'second', b'thied'], dtype='|S6')
>>> 

如上所见,自动分配的名称的第一个字符为f( field,字段),后面紧跟的是表示它在序列中位置的整数。其实,用更有意义的内容作为名字,用处更大。在创建数组时,可以指定各字段的名称

>>> structured=np.array([(1,'first',0.5,1+2j),(2,'second',1.3,2-2j),(3,'thied',0.8,1+3j)],dtype=[('id','i2'),('positiom','a6'),('value','f4'),('complex','c8')])
>>> structured
array([(1, b'first', 0.5, 1.+2.j), (2, b'second', 1.3, 2.-2.j),
       (3, b'thied', 0.8, 1.+3.j)],
      dtype=[('id', '), ('positiom', 'S6'), ('value', '), ('complex', ')])
>>> 

或在创建完成后,重新定义结构化数组的 dtype属性,在元组中指定各字段的名称。

>>> structured.dtype.names=('id','order','value','complex')
>>> 

现在,你可以使用更有意义的字段名来获取数组的某一列。

>>> structured['order']
array([b'first', b'second', b'thied'], dtype='|S6')

你可能感兴趣的:(numpy学习笔记)