# Numpy 数组及其索引 先导入numpy: In [1]: ```py from numpy import * ``` ## 产生数组 从列表产生数组: In [2]: ```py lst = [0, 1, 2, 3] a = array(lst) a ``` Out[2]: ```py array([0, 1, 2, 3]) ``` 或者直接将列表传入: In [3]: ```py a = array([1, 2, 3, 4]) a ``` Out[3]: ```py array([1, 2, 3, 4]) ``` ## 数组属性 查看类型: In [4]: ```py type(a) ``` Out[4]: ```py numpy.ndarray ``` 查看数组中的数据类型: In [5]: ```py # 32比特的整数 a.dtype ``` Out[5]: ```py dtype('int32') ``` 查看每个元素所占的字节: In [6]: ```py a.itemsize ``` Out[6]: ```py 4 ``` 查看形状,会返回一个元组,每个元素代表这一维的元素数目: In [7]: ```py # 1维数组,返回一个元组 a.shape ``` Out[7]: ```py (4L,) ``` 或者使用: In [8]: ```py shape(a) ``` Out[8]: ```py (4L,) ``` `shape` 的使用历史要比 `a.shape` 久,而且还可以作用于别的类型: In [9]: ```py lst = [1,2,3,4] shape(lst) ``` Out[9]: ```py (4L,) ``` 查看元素数目: In [10]: ```py a.size ``` Out[10]: ```py 4 ``` In [11]: ```py size(a) ``` Out[11]: ```py 4 ``` 查看所有元素所占的空间: In [12]: ```py a.nbytes ``` Out[12]: ```py 16 ``` 但事实上,数组所占的存储空间要比这个数字大,因为要用一个header来保存shape,dtype这样的信息。 查看数组维数: In [13]: ```py a.ndim ``` Out[13]: ```py 1 ``` ## 使用fill方法设定初始值 可以使用 `fill` 方法将数组设为指定值: In [14]: ```py a.fill(-4.8) a ``` Out[14]: ```py array([-4, -4, -4, -4]) ``` 但是与列表不同,数组中要求所有元素的 `dtype` 是一样的,如果传入参数的类型与数组类型不一样,需要按照已有的类型进行转换。 ## 索引与切片 和列表相似,数组也支持索引和切片操作。 索引第一个元素: In [15]: ```py a = array([0, 1, 2, 3]) a[0] ``` Out[15]: ```py 0 ``` 修改第一个元素的值: In [16]: ```py a[0] = 10 a ``` Out[16]: ```py array([10, 1, 2, 3]) ``` 切片,支持负索引: In [17]: ```py a = array([11,12,13,14,15]) a[1:3] ``` Out[17]: ```py array([12, 13]) ``` In [18]: ```py a[1:-2] ``` Out[18]: ```py array([12, 13]) ``` In [19]: ```py a[-4:3] ``` Out[19]: ```py array([12, 13]) ``` 省略参数: In [20]: ```py a[::2] ``` Out[20]: ```py array([11, 13, 15]) ``` In [21]: ```py a[-2:] ``` Out[21]: ```py array([14, 15]) ``` 假设我们记录一辆汽车表盘上每天显示的里程数: In [22]: ```py od = array([21000, 21180, 21240, 22100, 22400]) ``` 可以这样计算每天的旅程: In [23]: ```py dist = od[1:] - od[:-1] dist ``` Out[23]: ```py array([180, 60, 860, 300]) ``` 在本质上,**Python**会将array的各种计算转换为类似这样的**C**代码: ```py int compute_sum(int *arr, int N) { int sum = 0; int i; for (i = 0; i < N; i++) { sum += arr[i]; } return sum; } ``` ## 多维数组及其属性 `array` 还可以用来生成多维数组: In [24]: ```py a = array([[ 0, 1, 2, 3], [10,11,12,13]]) a ``` Out[24]: ```py array([[ 0, 1, 2, 3], [10, 11, 12, 13]]) ``` 事实上我们传入的是一个以列表为元素的列表,最终得到一个二维数组。 甚至可以扩展到3D或者4D的情景。 查看形状: In [25]: ```py a.shape ``` Out[25]: ```py (2L, 4L) ``` 这里2代表行数,4代表列数。 查看总的元素个数: In [26]: ```py # 2 * 4 = 8 a.size ``` Out[26]: ```py 8 ``` 查看维数: In [27]: ```py a.ndim ``` Out[27]: ```py 2 ``` ## 多维数组索引 对于二维数组,可以传入两个数字来索引: In [28]: ```py a[1, 3] ``` Out[28]: ```py 13 ``` 其中,1是行索引,3是列索引,中间用逗号隔开,事实上,**Python**会将它们看成一个元组(1,3),然后按照顺序进行对应。 可以利用索引给它赋值: In [29]: ```py a[1, 3] = -1 a ``` Out[29]: ```py array([[ 0, 1, 2, 3], [10, 11, 12, -1]]) ``` 事实上,我们还可以使用单个索引来索引一整行内容: In [30]: ```py # 返回第二行元组组成的array a[1] ``` Out[30]: ```py array([10, 11, 12, -1]) ``` **Python**会将这单个元组当成对第一维的索引,然后返回对应的内容。 ## 多维数组切片 多维数组,也支持切片操作: In [31]: ```py a = array([[ 0, 1, 2, 3, 4, 5], [10,11,12,13,14,15], [20,21,22,23,24,25], [30,31,32,33,34,35], [40,41,42,43,44,45], [50,51,52,53,54,55]]) a ``` Out[31]: ```py array([[ 0, 1, 2, 3, 4, 5], [10, 11, 12, 13, 14, 15], [20, 21, 22, 23, 24, 25], [30, 31, 32, 33, 34, 35], [40, 41, 42, 43, 44, 45], [50, 51, 52, 53, 54, 55]]) ``` 想得到第一行的第 4 和第 5 两个元素: In [32]: ```py a[0, 3:5] ``` Out[32]: ```py array([3, 4]) ``` 得到最后两行的最后两列: In [33]: ```py a[4:, 4:] ``` Out[33]: ```py array([[44, 45], [54, 55]]) ``` 得到第三列: In [34]: ```py a[:, 2] ``` Out[34]: ```py array([ 2, 12, 22, 32, 42, 52]) ``` 每一维都支持切片的规则,包括负索引,省略: ```py [lower:upper:step] ``` 例如,取出3,5行的奇数列: In [35]: ```py a[2::2, ::2] ``` Out[35]: ```py array([[20, 22, 24], [40, 42, 44]]) ``` ## 切片是引用 切片在内存中使用的是引用机制。 In [36]: ```py a = array([0,1,2,3,4]) b = a[2:4] print b ``` ```py [2 3] ``` 引用机制意味着,**Python**并没有为 `b` 分配新的空间来存储它的值,而是让 `b` 指向了 `a` 所分配的内存空间,因此,改变 `b` 会改变 `a` 的值: In [37]: ```py b[0] = 10 a ``` Out[37]: ```py array([ 0, 1, 10, 3, 4]) ``` 而这种现象在列表中并不会出现: In [38]: ```py a = [1,2,3,4,5] b = a[2:3] b[0] = 13234 print a ``` ```py [1, 2, 3, 4, 5] ``` 这样做的好处在于,对于很大的数组,不用大量复制多余的值,节约了空间。 缺点在于,可能出现改变一个值改变另一个值的情况。 一个解决方法是使用copy()方法产生一个复制,这个复制会申请新的内存: In [39]: ```py a = array([0,1,2,3,4]) b = a[2:4].copy() b[0] = 10 a ``` Out[39]: ```py array([0, 1, 2, 3, 4]) ``` ## 花式索引 切片只能支持连续或者等间隔的切片操作,要想实现任意位置的操作,需要使用花式索引 `fancy slicing` 。 ### 一维花式索引 与 range 函数类似,我们可以使用 arange 函数来产生等差数组。 In [40]: ```py a = arange(0, 80, 10) a ``` Out[40]: ```py array([ 0, 10, 20, 30, 40, 50, 60, 70]) ``` 花式索引需要指定索引位置: In [41]: ```py indices = [1, 2, -3] y = a[indices] print y ``` ```py [10 20 50] ``` 还可以使用布尔数组来花式索引: In [42]: ```py mask = array([0,1,1,0,0,1,0,0], dtype=bool) ``` In [43]: ```py a[mask] ``` Out[43]: ```py array([10, 20, 50]) ``` 或者用布尔表达式生成 `mask`,选出了所有大于0.5的值: In [44]: ```py from numpy.random import rand a = rand(10) a ``` Out[44]: ```py array([ 0.37214708, 0.48594733, 0.73365131, 0.15769295, 0.30786017, 0.62068734, 0.36940654, 0.09424167, 0.53085308, 0.12248951]) ``` In [45]: ```py mask = a > 0.5 a[mask] ``` Out[45]: ```py array([ 0.73365131, 0.62068734, 0.53085308]) ``` mask 必须是布尔数组。 ### 二维花式索引 In [46]: ```py a = array([[ 0, 1, 2, 3, 4, 5], [10,11,12,13,14,15], [20,21,22,23,24,25], [30,31,32,33,34,35], [40,41,42,43,44,45], [50,51,52,53,54,55]]) a ``` Out[46]: ```py array([[ 0, 1, 2, 3, 4, 5], [10, 11, 12, 13, 14, 15], [20, 21, 22, 23, 24, 25], [30, 31, 32, 33, 34, 35], [40, 41, 42, 43, 44, 45], [50, 51, 52, 53, 54, 55]]) ``` 对于二维花式索引,我们需要给定 `row` 和 `col` 的值: In [47]: ```py a[(0,1,2,3,4), (1,2,3,4,5)] ``` Out[47]: ```py array([ 1, 12, 23, 34, 45]) ``` 返回的是一条次对角线上的5个值。 In [48]: ```py a[3:, [0,2,5]] ``` Out[48]: ```py array([[30, 32, 35], [40, 42, 45], [50, 52, 55]]) ``` 返回的是最后三行的第1,3,5列。 也可以使用mask进行索引: In [49]: ```py mask = array([1,0,1,0,0,1], dtype=bool) a[mask, 2] ``` Out[49]: ```py array([ 2, 22, 52]) ``` 与切片不同,花式索引返回的是原对象的一个复制而不是引用。 ### “不完全”索引 只给定行索引的时候,返回整行: In [50]: ```py y = a[:3] y ``` Out[50]: ```py array([[ 0, 1, 2, 3, 4, 5], [10, 11, 12, 13, 14, 15], [20, 21, 22, 23, 24, 25]]) ``` 这时候也可以使用花式索引取出第2,3,5行: In [51]: ```py condition = array([0,1,1,0,1,0], dtype=bool) a[condition] ``` Out[51]: ```py array([[10, 11, 12, 13, 14, 15], [20, 21, 22, 23, 24, 25], [40, 41, 42, 43, 44, 45]]) ``` ### 三维花式索引 In [52]: ```py a = arange(64) a.shape = 4,4,4 a ``` Out[52]: ```py array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]], [[16, 17, 18, 19], [20, 21, 22, 23], [24, 25, 26, 27], [28, 29, 30, 31]], [[32, 33, 34, 35], [36, 37, 38, 39], [40, 41, 42, 43], [44, 45, 46, 47]], [[48, 49, 50, 51], [52, 53, 54, 55], [56, 57, 58, 59], [60, 61, 62, 63]]]) ``` In [53]: ```py y = a[:,:,[2, -1]] y ``` Out[53]: ```py array([[[ 2, 3], [ 6, 7], [10, 11], [14, 15]], [[18, 19], [22, 23], [26, 27], [30, 31]], [[34, 35], [38, 39], [42, 43], [46, 47]], [[50, 51], [54, 55], [58, 59], [62, 63]]]) ``` ## where语句 ```py where(array) ``` `where` 函数会返回所有非零元素的索引。 ### 一维数组 先看一维的例子: In [54]: ```py a = array([0, 12, 5, 20]) ``` 判断数组中的元素是不是大于10: In [55]: ```py a > 10 ``` Out[55]: ```py array([False, True, False, True], dtype=bool) ``` 数组中所有大于10的元素的索引位置: In [56]: ```py where(a > 10) ``` Out[56]: ```py (array([1, 3], dtype=int64),) ``` 注意到 `where` 的返回值是一个元组。 使用元组是由于 where 可以对多维数组使用,此时返回值就是多维的。 在使用的时候,我们可以这样: In [57]: ```py indices = where(a > 10) indices = indices[0] indices ``` Out[57]: ```py array([1, 3], dtype=int64) ``` 或者: In [58]: ```py indices = where(a>10)[0] indices ``` Out[58]: ```py array([1, 3], dtype=int64) ``` 可以直接用 `where` 的返回值进行索引: In [59]: ```py loc = where(a > 10) a[loc] ``` Out[59]: ```py array([12, 20]) ``` ### 多维数组 考虑二维数组: In [60]: ```py a = array([[0, 12, 5, 20], [1, 2, 11, 15]]) loc = where(a > 10) ``` 返回结果是一个二维的元组,每一维代表这一维的索引值: In [61]: ```py loc ``` Out[61]: ```py (array([0, 0, 1, 1], dtype=int64), array([1, 3, 2, 3], dtype=int64)) ``` 也可以直接用来索引a: In [62]: ```py a[loc] ``` Out[62]: ```py array([12, 20, 11, 15]) ``` 或者可以这样: In [63]: ```py rows, cols = where(a>10) ``` In [64]: ```py rows ``` Out[64]: ```py array([0, 0, 1, 1], dtype=int64) ``` In [65]: ```py cols ``` Out[65]: ```py array([1, 3, 2, 3], dtype=int64) ``` In [66]: ```py a[rows, cols] ``` Out[66]: ```py array([12, 20, 11, 15]) ``` 再看另一个例子: In [67]: ```py a = arange(25) a.shape = 5,5 a ``` Out[67]: ```py array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]]) ``` In [68]: ```py a > 12 ``` Out[68]: ```py array([[False, False, False, False, False], [False, False, False, False, False], [False, False, False, True, True], [ True, True, True, True, True], [ True, True, True, True, True]], dtype=bool) ``` In [69]: ```py where(a > 12) ``` Out[69]: ```py (array([2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4], dtype=int64), array([3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4], dtype=int64)) ```