numpy的几种索引方式

为了简便的操作,我们必须重新造一个轮子,然后从此以后依着这个轮子的样子造新的轮子。

在完成CS231n的assignment的时候发现对于numpy的基本特性了解不够,于是乎就有了这篇博客。

东问问西找找,就就发现了一个新词叫indexing,大概意思是从数组中取出想要的元素的意思吧。

这篇文章是为了和我一样不想把numpy全部看完一遍就开工的人准备的。

话说为什么numpy不采用传统for循环而是自己定义了一套新的方法呢…就我看来是因为python是一种相当高级的语言,似乎是用解释器机制的,也就是说其语句的执行效率是比较低的,而numpy的各种数组操作时基于底层语言,即C,所以同样一种操作,在python中用for loop写和全部用numpy提供的向量运算来写速度可以差几百倍(字面意义上的)

然而这个就让习惯for loop的同学有些不习惯了,这可能是第一个难点吧。

不过既然numpy被如此广泛的应用,就是说这个机制基本是没有问题的,而indexing既然是用来取代for loop的很大一部分功能,这个机制当然也要很强大,事实呢,也正式如此。

  1. 单个元素的查询

    这很好说,和所有有数组的语言一样,都是

    1
    2
    3
    x = np.arange(10)
    >>> x[2]
    2

    这样的形式。

    同时支持索引用负值来从后往前提取元素,比如说:

    1
    2
    >>> x[-2]
    8

    对于取二维数组中的单个元素,和一维数组中的情况类似:

    1
    2
    3
    4
    5
    >>> x.shape = (2,5) # now x is 2-dimensional
    >>> x[1,3]
    8
    >>> x[1,-1]
    9
  2. 数组的切片和跳步

    个人感觉是替代C++中的

    1
    for(int i=0;i<n;i+=step)

    具体操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> x = np.arange(10)
    >>> x[2:5]# for(i=2;i<5;i++)
    array([2, 3, 4])
    >>> x[:-7]#for(i=n-1-7;i>=0;i--)
    array([0, 1, 2])
    >>> x[1:7:2]#for(i=1;i<7;i+=2)
    array([1, 3, 5])
    >>> y = np.arange(35).reshape(5,7)
    >>> y[1:5:2,::3]#切片和跳步同样也可以用在多维数组上
    array([[ 7, 10, 13],
    [21, 24, 27]])
  3. 索引数组索引

    这听起来可能有些绕口,但是这是numpy用到现在相对于C++来说比较方便的功能:使用数组来索引数组。

    1
    2
    3
    4
    5
    6
    7
    >>> x = np.arange(10,1,-1)
    >>> x
    array([10, 9, 8, 7, 6, 5, 4, 3, 2])
    >>> x[np.array([3, 3, 1, 8])]#即索引下标为3,3,1,8的数组元素
    array([7, 7, 9, 2])#x[3]=7,x[1]=9,x[8]=2
    >>> x[np.array([3,3,-3,8])]#对于负数下标也同样支持
    array([7, 7, 4, 2])

    对于一维数组来说,当索引数组是一个多维数组的时候,其返回结果和多维数组结果一样,结果中每一行等价于索引数组中每一行作为索引数组输入的值。

    1
    2
    3
    >>> x[np.array([[1,1],[2,3]])]
    array([[9, 9],
    [8, 7]])

    之前举的例子是一维数组,现在我们把索引数组用到多维数组上面。

    1
    2
    >>> y[np.array([0,2,4]), np.array([0,1,2])]
    array([ 0, 15, 30])#即取出[0,0][2,1][4,2]位置的元素

    在很多时候这个特性可以替代loop循环帮我们从数组中取出想要的数字,但是需要一定时间的重新思考过程。这也是numpy为了高效付出的代价吧。

    对于不符合格式的输入,会先进行广播操作。

    1
    2
    >>> y[np.array([0,2,4]), 1]
    array([ 1, 15, 29])#即取出[0,1][2,1][4,1]位置的元素

    (numpy中的广播操作还是有些门道的,而且因为广播操作是一个自动进行的操作,所以最好还是对于广播操作的机制有所了解,不然经常会出一些哭笑不得的bug。需要了解的同学可以参照这篇文章)

  4. 数组内元素筛选

    目前所用到的有两种方式,第一种方式是筛选出所有符合逻辑表达式的元素并放在一个一维的数组里:

    1
    2
    3
    >>> b = y>20
    >>> y[b]
    array([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34])

    第二种方式是将原数组中满足逻辑表达式的元素作出改动,不然就不改动:

    1
    2
    3
    4
    5
    6
    7
    8
    >>> y = np.arange(35).reshape(5,7)
    >>> y[y>10]=0
    >>> y
    array([[ 0, 1, 2, 3, 4, 5, 6],
    [ 7, 8, 9, 10, 0, 0, 0],
    [ 0, 0, 0, 0, 0, 0, 0],
    [ 0, 0, 0, 0, 0, 0, 0],
    [ 0, 0, 0, 0, 0, 0, 0]])

以上是我用到的几个numpy比较有用的查询数组的方法,其实基本是从官方文档中找了几个比较实用的加上一点自己搜集到的材料,如果还想进一步研究的话可以参考这里的官方文档(六级警告)