购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1 NumPy的使用

NumPy是一款在Python中被广泛使用的科学计算库,它的最大特点就是支持高维大型数组的运算,其图标如图2-1所示。在量化交易中使用NumPy库进行运算,尤其是在回测时计算日均盈亏、收益率、夏普比率等使用NumPy的运算符进行计算十分方便。除此之外,在与图像相关的应用中也可以使用NumPy,输入的图像实际上是一个形状为(H, W, C)的高维数组,其中H、W、C分别为图像的高度、宽度与通道数。

图2-1 NumPy的图标

2.1.1 NumPy中的数据类型

NumPy中的数据类型众多,与C语言的数据类型较为相近。例如,其中的整型数就分为int8、int16、int32、int64及它们对应的无符号形式,而Python中的整型则只用int进行表示。同样,对于float类型与此类似。数据类型之间的转换使用np.[类型](待转换数组)或.as_type([类型])。如希望将int64类型的数组a转换为float32,则可以使用np.float32(a)或a.as_type(np.float32)完成。

2.1.2 NumPy中数组的使用

1.创建数组

在NumPy中多维数组被称作ndarray,使用NumPy创建多维数组十分方便,可以通过转换Python中的list或tuple得到,也能直接通过NumPy中的函数创建。NumPy中对数组进行操作的函数所返回的数据都是ndarray。例如要创建一个形状为(2,3,4)一共24个1的ndarray,可以通过以下的两种方式进行创建,代码如下:

使用python test_numpy.py运行程序后,发现通过这两种方式创建的数组都可以得到形状为(2, 3, 4)的数组,并且它们的类型都是<class 'numpy.ndarray'>。不同的是,a_np1与a_np2中的元素数据类型分别为<class 'numpy.int32'>与<class 'numpy.float64'>,这是因为a_list中的元素原本是Python中的int型,所以在转换为ndarray时其也是numpy.int32型。如果将a_list中的元素改为1.或1.0,此时a_np1与a_np2中的元素类型则都是<class 'numpy.float64'>。那么,有没有办法将方法2创建的ndarray中的元素转换成<class 'numpy.int32'>呢,答案是肯定的,此时使用ndarray的astype方法进行类型转换即可,代码如下:

     a_np2_int = a_np2.astype(np.int32)
     #<class 'numpy.int32'>
     print(type(a_np2_int[0][0][0]))

NumPy除了提供了创建所有元素为1的数组的方法np.ones,相似地,也可以使用np.zeros创建所有元素为0的数组,代码如下:

     b_np = np.zeros([2, 3, 4])
     print(b_np)

除了可以用np.ones和np.zeros创建指定形状的数组,也可以使用np.ones_like与np.zeros_like创建和已知数组形状相同的全1或全0数组,其过程相当于先获得目标数组的形状,再使用np.ones或np.zeros进行创建,代码如下:

     #创建和b_np数组形状相同的,其中值全为1的数组
     one_like_b_np = np.ones_like(b_np)
     print(one_like_b_np)
     #(2, 3, 4)
     print(one_like_b_np.shape)

2.创建占位符

当不知道数组中的每个元素的具体值时,还可以使用np.empty来创建一个“空”数组作为占位符,这个“空”只是语义上而言的,其实数组中存在随机的数值。NumPy对使用empty方法创建的数组元素随机进行初始化,而需要做的是后期为数组中的元素进行赋值,代码如下:

     #empty1和empty2中的值都是随机初始化的,empty方法实际上是创建了占位符,运行效率高
     empty1 = np.empty([2, 3])
     print(empty1)
     empty2 = np.empty([2, 3, 4])
     print(empty2)

3.数组的属性

所有的ndarray都有ndim、shape、size、dtype等属性,其中ndim用来查看数组的维度个数,如a_np1的形状为(2,3,4),那么它就是一个三维的数组,ndim值为3,而shape是用来查看数组的形状的,即a_np1.shape是(2, 3, 4);size的意义则是说明数组中总的元素个数,即a_np1.size=2×3×4=24,代码如下:

     #3
     print(a_np1.ndim)
     #(2, 3, 4)
     print(a_np1.shape)
     #24
     print(a_np1.size)

4.数组的转置

NumPy可以对高维数组进行转置(transpose),转置是指改变数组中元素的排列关系而不改变元素的数量。转置时需要指定axes参数,它指输出的数组的轴顺序与输入数组的轴顺序之间的对应关系。如新建一个形状为(2, 3, 4)的数组a,其在0、1、2轴上的长度分别为2、3、4,使用np.transpose(a, axes=[0, 2, 1])表示对a数组的2轴和1轴进行交换,而原数组的0轴保持不变,得到的新数组的形状则为(2,4,3)。可以这样理解:原数组a是由2(0轴长度)个3×4(1轴和2轴)的矩阵组成的,0轴保持不变而只有1轴与2轴进行转置,即两个3×4的矩阵分别进行矩阵转置即为最后的结果,故最终的形状为(2,4,3),具体实现转置的代码如下:

5.数组的变形

NumPy支持对数组进行变形(reshape),变形和2.1.2节第4部分的转置一样,即都能改变数组的形状,但是转置是改变数组元素的排列,而变形是改变数组元素的分组。换言之,转置前后数组元素的顺序会发生改变而变形操作不会改变元素之间的顺序关系。比较转置操作和变形操作的异同的代码如下:

从结果可以看出,b_np_transpose和b_np_reshape的形状都是(2, 3, 4),而数组内部元素的排列不同,b_np_transpose与原数组b_np中元素的排列顺序不同,而b_np_reshape的元素排列与原数组相同。在实际操作中,常常需要将数组变形为只有一行或者一列的形状,此时将newshape指定为[1,−1]或[−1,1]即可,−1表示让程序自动求解该维度的长度。np.squeeze也是一个常用的函数,它可以对数组中长度为1的维度进行压缩,其本质也是reshape,所以在此不再赘述。

6.数组的切分与合并

NumPy可以将大数组拆分为若干个小数组,同时也能将若干个小数组合并为一个大数组,切分通常使用split方法,而根据不同的需求,通常会使用stack或者concatenate方法进行数组的合并,下面分别介绍这几种方法的应用。

当使用split将大数组切分为小数组时,需要指定切分点的下标或切分的数量(indices_or_sections)及在哪个维度上切分(axis)。当指定切分下标时,需要为indices_or_sections参数传入一个切分下标的列表(list),而当指定切分数量时,需要为indices_or_sections参数传入一个整数k,表示需要将待切分数组沿指定轴平均切分为k部分,若指定的k无法完成均分,此时split方法会抛出ValueError,分割的结果为含有若干个分割结果ndarray的列表。如果需要非均等切分,读者则可以参考array_split方法,该方法在此不进行介绍。split方法的不同使用场景与方法的代码如下:

运行以上程序,从控制台打印的结果可以看出axis_0_split_3_equal_parts与axis_1_split_2_equal_parts分别将原数组在轴0(长度为3)和轴1(长度为4)平均切分为3份和2份,此时为spilit的indices_or_sections传入的值分别为3和2,代表需要切分的数量,而当尝试在0轴上切分为两部分时,程序会报错,因为轴0无法均分为2份。当为split的indices_or_sections传入的值为[2, ]和[3, ]时,会分别得到axis_0_split_indices和axis_1_split_indices,前者表示将原数组在0轴上分为两部分,第一部分是0轴下标小于2的部分,第二部分是下标大于或等于2的部分,即分为to_split_arr[: 2, :]和to_split_arr[2:, :];类似地,axis_1_split_indices表示将原数组在1轴上分为两部分,分别为to_split_arr[:, :3]和to_split_arr[:, 3:]。

前面讲过,在NumPy中合并数组通常有两种方式:stack和concatenate,两者有很多相似之处,但是也有明显的区别。首先,这两个函数都需要传入待合并的数组列表及指定在哪个轴上进行合并;区别是stack会为合并的数组新建一个轴,而concatenate直接在原始数组的轴上进行合并。假设现在需要对两个形状都为(3,4)的数组进行合并,当使用stack函数在2轴进行合并时,由于原始数组只有0轴和1轴,并没有2轴,因此stack函数会为新数组新建一个2轴,得到的数组形状为(3,4,2),而当使用concatenate在1轴上合并时,得到的新数组形状为(3,4+4),即(3,8)。这两个函数在合并数组时的代码如下:

运行以上程序可以得知,stack会在axis参数指定的轴上新建一个轴,改变合并后数组的维度,而concatenate函数仅会在原始数组的某一axis上进行合并,不会产生新的轴。

在此,笔者仅对NumPy最基本的用法进行了介绍,有兴趣了解其更多强大功能与用法的读者可以到NumPy的官方网站(https://numpy.org)进行进一步学习。 sLSwfE1BKpgjmaZ5lYIbY5N8kmqt95dlbcrhh8LDqnhl1d3fdKiQapvLJtEvPKyd

点击中间区域
呼出菜单
上一章
目录
下一章
×