量化百科

Python最好用的科学计算库:NumPy快速入门教程(二)

由iquant创建,最终由iquant 被浏览 12 用户

本文接上一篇:Python最好用的科学计算库:NumPy快速入门教程(一)

形状操作

首先导入numpy库

>>> import numpy as np

改变数组的形状

数组的形状由每个维度的元素的数量决定。

>>> a = np.floor(10*np.random.random((3,4)))
>>> a
array([[7., 1., 0., 7.],
       [7., 3., 3., 4.],
       [3., 9., 2., 9.]])
>>> a.shape #数组的形状
(3, 4)

数组的形状能够通过很多命令改变。以下的三个命令都能返回一个被改变的数组,但是并不改变原数组。

>>> a.ravel()  # 返回被平坦化的数组
array([7., 1., 0., 7., 7., 3., 3., 4., 3., 9., 2., 9.])
>>> a.reshape(6,2)  # 返回一个改变形状的数组
array([[7., 1.],
       [0., 7.],
       [7., 3.],
       [3., 4.],
       [3., 9.],
       [2., 9.]])
>>> a.T  # 返回一个被转置的数组
array([[7., 7., 3.],
       [1., 3., 9.],
       [0., 3., 2.],
       [7., 4., 9.]])
>>> a.T.shape
(4, 3)
>>> a.shape # 原数组的形状不变
(3, 4)

ravel()返回结果的元素顺序一般来说C语言的风格,也就是说,最右的维度先变化,所以,返回结果的排列是a[0,0]之后是a[0,1]。如果数组的形状改变了,依然会采用c语言风格的处理方式。NumPy一般生成数组在内存中就是按照这种顺序存储,所以ravel()一般不会复制参数,但是如果数组是通过切片或其他非一般的方式创建的,可能就需要复制了。ravel()reshape()函数也可以通过设置一个可选参数使用FORTRAN语言风格,这二种风格下,最左的维度先变化。

reshape函数返回的是改变了形状数组,而resize方法改变的是数组本身的形状。

>>> a
array([[7., 1., 0., 7.],
       [7., 3., 3., 4.],
       [3., 9., 2., 9.]])
>>> a.resize((2,6))
>>> a # resize 之后,a的形状变了,这是与reshape的区别
array([[7., 1., 0., 7., 7., 3.],
       [3., 4., 3., 9., 2., 9.]])

如果一个维度被设置为-1,那么这个维度的大小将会自动计算。

>>> a.reshape(3,-1)
array([[7., 1., 0., 7.],
       [7., 3., 3., 4.],
       [3., 9., 2., 9.]])

堆叠不同的数组

多个数组能够沿着不同维度被堆叠在一起。

>>> a = np.floor(10*np.random.random((2,2)))
>>> a
array([[7., 8.],
       [1., 2.]])
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[7., 4.],
       [2., 6.]])
>>> np.vstack((a,b))
array([[7., 8.],
       [1., 2.],
       [7., 4.],
       [2., 6.]])
>>> np.hstack((a,b))
array([[7., 8., 7., 4.],
       [1., 2., 2., 6.]])

column_stack函数将一维数组看做列向量堆叠成二维数组。当被堆叠的数组是二维时,它的作用与hstack一样。

>>> from numpy import newaxis
>>> np.column_stack((a,b))     # 使用column_stack堆叠二维数组和上面我们使用hstack时得到的结果一样
array([[7., 8., 7., 4.],
       [1., 2., 2., 6.]])
>>> a = np.array([4.,2.])
>>> b = np.array([3.,8.])
>>> np.column_stack((a,b))     # 当被堆叠的数组是一维数组时,返回的结果还是二维数组
array([[4., 3.],
       [2., 8.]])
>>> np.hstack((a,b))           # 使用hstack堆叠一维数组,返回的还是一维数组,这是与column_stack的不同
array([4., 2., 3., 8.])
>>> a[:,newaxis]               # 通过newaxis能够为数组添加维度
array([[4.],
       [2.]])
>>> np.column_stack((a[:,newaxis],b[:,newaxis]))
array([[4., 3.],
       [2., 8.]])
>>> np.hstack((a[:,newaxis],b[:,newaxis]))   # 添加维度之后再使用hstack就可以得到和column_stack一样的结果
array([[4., 3.],
       [2., 8.]])

另外,对于任意数组row_stack与vstack的作用相同。通常,对于大于二维的数组,hstack作用在第二个维度上,而vstack作用在第一个维度上,而concatenate函数允许用户通过设置一个可选参数决定连接应该发生在哪个维度。

注意

在一些复杂的情况下, 使用r_c_ 创建数组非常实用. 他们允许使用范围表达符号:

>>> np.r_[1:4,0,4]
array([1, 2, 3, 0, 4])

当作用在数组时,r_ 和 c_ 的作用与vstack和hstack一样,但是允许我们通过可选参数决定连接发生在哪个维度。

将数组拆分为更小的数组

使用hsplit,你可以沿着水平方向拆分数组,即可以通过指定平均切分的数量,也可以通过指定切分的列来实现。

>>> a = np.floor(10*np.random.random((2,12)))
>>> a
array([[0., 2., 5., 6., 0., 2., 0., 2., 1., 5., 6., 2.],
       [3., 3., 2., 3., 6., 8., 8., 5., 6., 2., 0., 2.]])
>>> np.hsplit(a,3)   # 延水平方向切分为3个相同大小的数组
[array([[0., 2., 5., 6.],
        [3., 3., 2., 3.]]), array([[0., 2., 0., 2.],
        [6., 8., 8., 5.]]), array([[1., 5., 6., 2.],
        [6., 2., 0., 2.]])]
np.hsplit(a,(3,4))   # 从第3第4列切分a
[array([[0., 2., 5.],
        [3., 3., 2.]]), array([[6.],
        [3.]]), array([[0., 2., 0., 2., 1., 5., 6., 2.],
        [6., 8., 8., 5., 6., 2., 0., 2.]])]

vsplit沿着垂直方向切分,array_split 可以指定沿着哪个维度切分。

复制与Views

当操作数组时,有时候数据被复制到一个新的数组,而有时候又没有。这经常困扰新手,以下是三种情况。

不复制

简单的赋值语句不会复制数组对象或者他们的值。

>>> a = np.arange(12)
>>> b = a            # 简单的赋值不会复制新的对象
>>> b is a           # a和b是同一个数组的两个不同的名称而已
True
>>> b.shape = 3,4    # 改变b的话也会改变a
>>> a.shape
(3, 4)

Python将可变对象作为引用传递,因此函数调用不会复制。

>>> def f(x):
...     print(id(x))
...
>>> id(a)    # id是一个对象的唯一标识符
4557575552
>>> f(a)     # 将a传入函数f,不会复制a
4557575552

View或者浅复制

不同的数组对象能够共享相同的数据。view方法创建一个新的数组对象,这个对象与原始数组使用相同的数据。

>>> c = a.view() # c是a的view,或者说c是a的浅复制,c是另一个对象
>>> c is a
False
>>> c.base is a  # 确切的说,c是数值的view,数值的属于a
True
>>> c.flags.owndata #所以c的数值并不属于c
False
>>> c.shape = 2,6    # 如果改变c的形状,并不影响a
>>> a.shape
(3, 4)
>>> c[0,4] = 1234    # 但是,如果改变c的数值,a也会受影响
>>> a
array([[   0,    1,    2,    3],
       [1234,    5,    6,    7],
       [   8,    9,   10,   11]])

以上在python中称作view,或者叫浅复制,切片操作就会返回一个view:

>>> s = a[ : , 1:3]     # s是a的切片
>>> s[:] = 10           # 改变s的值也会影响a
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

深复制

copy方法生成一个数组的完整备份。

>>> d = a.copy()  # 生成一个新的数组对象
>>> d is a
False
>>> d.base is a   # d与a不共享任何数值
False
>>> d[0,0] = 9999   #改变d的数值,也不会影响a
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

\

标签

NumPyNumPy基础NumpyPython