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