#!/usr/bin/env python # coding: utf-8 # # 第四章 简介 # NumPy之于数值计算特别重要的原因之一,是因为它可以高效处理大数组的数据。这是因为: # # * NumPy是在一个连续的内存块中存储数据,独立于其他Python内置对象。NumPy的C语言编写的算法库可以操作内存,而不必进行类型检查或其它前期工作。比起Python的内置序列,NumPy数组使用的内存更少。 # * NumPy可以在整个数组上执行复杂的计算,而不需要Python的for循环。 # In[4]: import numpy as np my_arr = np.arange(1000000) my_list = list(range(1000000)) get_ipython().run_line_magic('time', 'for n in range(10):my_arr2 = my_arr * 2') # In[5]: my_arr2[:10] # In[6]: get_ipython().run_line_magic('time', 'for m in range(10): my_list2 = [x * 2 for x in my_list]') # In[8]: my_list2[:10] # * 通过上述实验对比可知,numpy在大型数组计算时会快很多 # ## 4.1ndarray:多维数组对象 # In[10]: import numpy as np # 产生2*3的数组 data = np.random.randn(2,3) data # In[11]: #数组的每个元素乘以一个实数 data * 10 # In[12]: data + data # * ndarray是一个通用的同构数据多维容器,即其所有元素必须是相同类型的。每个数组都有一个shape(一个表示各维度大小的元组)和一个dtype(一个用于说明数组数据类型的对象) # In[13]: data.shape # In[14]: data.dtype # * 创建数组:最简单的办法就是使用array函数。它接受一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组 # In[17]: data1 = [1,2,3,6.5,0] arr1 = np.array(data1) arr1 # * 嵌套序列:有一组等长的列表组成的列表 # In[19]: data2 = [[1,2,3],[4,5,6]] arr2 = np.array(data2) arr2 # In[20]: arr2.ndim #维度 # In[21]: arr2.shape # In[22]: arr2.dtype # * zeros和ones分别可以创建指定长度或形状的全0或全1数组。empty可以创建一个没有任何具体值的数组。要用这些方法创建多维数组,只需传入一个表示形状的元组即可 # In[24]: np.zeros((2,3)) # In[26]: arr3 = np.ones((2,2,2)) arr3 # In[27]: arr3.ndim # In[28]: arr3.shape # * arange()是Python内置函数range()函数的数组版 # In[29]: np.arange(10) # ![数组创建函数](data/array_func.png) # * 也可以在创建数组时指定数据类型 # In[33]: arr4 = np.array([1,2,3],dtype = np.float) arr4 # In[34]: arr4.dtype # In[36]: arr5 = np.array([1,2,3],dtype=np.int32) arr5 # In[37]: arr5.dtype # * 可以使用astype(type)将一个类型的数组转换为指定类型 # In[38]: float_arr5 = arr5.astype(np.float64) # In[39]: float_arr5.dtype # * 上例中将整数转换为浮点数;如果将浮点数转换为整数,则小数部分将被截取删除 # In[42]: f_arr = np.array([1.22, 33.211, 20.01]) f_arr # In[44]: f_arr.dtype # In[45]: i_arr = f_arr.astype(np.int32) # In[46]: i_arr # In[47]: i_arr.dtype # * 如果某个数组元素全是数字字符串,也可以使用astype进行转换 # In[50]: s_arr = np.array(['1','2.22','3.01']) s_arr.dtype # In[51]: s_arr.astype(np.float64) # ## numpy 数组的运算 # In[52]: arr = np.array([[1,2,3.0],[4.,5.,6.]]) arr # In[58]: arr **2 # In[59]: arr * arr # In[63]: #数组与标量的算术运算会将标量值传播到各个元素 #不同大小的数组之间的运算叫做广播(broadcasting) arr / 2 # In[56]: arr // 2 # In[60]: arr - 1 # In[62]: # shape相同的数组比较会产生一个布尔数组 arr2 = np.array([[4,1,1],[7,5,5]]) arr > arr2 # ## 基本的索引和切片 # In[66]: arr = np.arange(10) arr # In[67]: arr[5] # In[68]: arr[:6] # In[70]: arr[2:5] = 10 arr # * 数组切片跟列表最重要的区别在于,数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到源数组上。 # In[76]: arr_slice = arr[2:5] arr_slice # In[78]: #当修改arr_slice时,arr数组也会被修改 arr_slice[1] = 100 arr_slice # In[79]: arr # * 如果想要得到数组的一个副本,而不是原数组的一个视图,可以使用copy()方法 # In[109]: arr_copy = arr[:3].copy() arr_copy # In[110]: arr_copy[0] = 100 arr_copy # In[111]: arr # In[112]: arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]]) arr2d[1] # In[113]: arr2d[1,1] # In[114]: arr2d[1][1] # In[115]: arr2d.ndim # In[116]: # 三维数组 arr3d = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]) arr3d # In[117]: arr3d.shape # In[118]: arr3d[0] # In[119]: arr3d[0,1] # In[120]: arr3d[0,1,2] # In[125]: # 标量值可以直接赋值给数组得某个元素,具有广播 arr3d[0] = 0 arr3d # ## 切片索引 # In[126]: arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]]) arr2d # In[127]: arr2d[:2] # In[128]: arr2d[:2,:2] # In[129]: arr2d[1,:2] # * 对切片表达式的赋值也会扩散到整个选区 # In[131]: arr2d[:2,:2] = 1 arr2d # ## 布尔型索引 # In[134]: names = np.array(['Bob','Joe','Will','Bob','will','Joe','Joe']) data = np.random.randn(7,4) names # In[135]: data # In[136]: names == 'Bob' # In[137]: data[names == 'Bob'] # In[138]: data[names == 'Bob',2:] # In[140]: data[names == 'Bob',2] #索引列 # * 如果要获取除了‘Bob’以外的值,既可以使用 != ,也可以使用~取反 # In[141]: names != 'Bob' # In[142]: data[~(names == 'Bob')] # * 也可以使用& 、 | 、等不二算术运算 # In[143]: mask = (names == 'Bob') |(names == 'Will') mask # In[144]: data[mask] # ### 通过布尔型索引选取数组元素,都将创建数组的副本。而数字切片索引将获得数组的视图 # In[145]: # 将data中小于零的元素设置为0 data[data < 0] = 0 data # In[147]: # 通过一维布尔数组设置整行或列的值 data[names != 'Joe'] = 7 data # ## 花式索引 # * 花式索引(Fancy indexing)是一个NumPy术语,它指的是利用整数数组进行索引。 # In[151]: arr = np.empty((8,4)) for i in range(8): arr[i] = i arr # In[152]: # 为了以特定的顺序选取行子集,只需要传入一个用于指定顺序的整数列表或数组即可 arr[[4,3,0,6]] # In[153]: # 也可以使用负数索引,会从末尾开始选取行 arr[[-1,-3,-5]] # * 一次传入多个索引数组会有一点特别。它返回的是一个一维数组,其中的元素对应各个索引元组: # In[154]: arr = np.arange(32).reshape((8,4)) arr # In[156]: #最终选出的是元素(1,0)、(5,3)、(7,1)和(2,2)。无论数组是多少维的,花式索引总是一维的。 arr[[1,5,7,2],[0,3,1,2]] # In[160]: arr[[1,5,7,2]] # In[161]: arr[[1,5,7,2]][:,[0,3,1,2]] # * 花式索引跟切片不一样,它总是将数据复制到新数组中。