第4章 Numpy基础(2)

以下内容主要学习自《利用Python进行数据分析》

第4章 NumPy基础(2)

NumPy是Numerical Python的简称,它是目前Python数值计算中最为重要的基础包。大多数计算包都提供了基于Numpy的科学函数功能,将NumPy的数组对象作为数据交换的通用语言。

本章将介绍NumPy数组的基础操作。虽然深入理解NumPy对于大部分数据分析应用并不是必需的,但是精通基于数组的编程和思维是成为Python科学计算专家的第一步

第4章 Numpy基础(2)_第1张图片
NumPy-2

ndarray索引与切片

Numpy.ndarray的索引与切片是一个值得学习的话题。有很多方式可以让你选中数据的子集或某个单一元素。

所谓“索引”,就是通过单一的索引号,得到ndarray的部分数据;所谓“切片”,就是通过索引号段,得到ndarray的部分数据。索引可以取得原数组降维的部分数据,而切片不会降低维度,而是得到一个维度相同的子集

如果熟悉Python的列表操作,那么很容易掌握ndarray的索引与切片,因为语法是相似的。

索引

通过索引获得单一的元素

NumPy的索引语法与Python列表的索引语法是一样的。如下是对一维数组进行索引:

In[1]: arr = np.arange(10)

In[2]: arr
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In[3]: arr[5]  #一维数组的索引
Out[3]: 5

如果要从二维数组(或多维数组)中取得单一元素,那么就要提供多个索引值。如下是对二维数组进行索引:

In [4]: arr = arr.reshape(2, 5)

In [5]: arr
Out[5]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

In [6]: arr[1][3]  # 二维数组的索引
Out[6]: 8

In [7]: arr[1, 3]  # 尽管索引语法不同,但索引效果一样
Out[7]: 8
通过索引获得ndarray的子集

可以从二维(或多维)数组中,获得ndarray的自己,方法是在多维数组中,如果省略后续索引值,返回的对象将是降低一个维度的数组。

# 生成一个维度为(2,3,4)的三维数组
In [1]: arr = np.arange(24).reshape(2, 3, 4)

In [2]: arr
Out[2]:
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]]])

In [3]: arr[0]  # 返回第1维、索引号为0的子集,返回的将是二维数组
Out[3]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [4]: arr[0][1]  # 返回一维数组
Out[4]: array([4, 5, 6, 7])

切片

一维数组的切片

与Python列表类似,NumPy数组可以通过相似的语法对数组进行切片。

In[1]: arr = np.arange(10)

In[2]: arr
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In[3]: sub = arr[5:8]  # 通过切片获取部分数据

In[4]: sub
Out[4]: array([5, 6, 7])

In [5]: sub.shape  # sub是一个有三个元素的一维数组
Out[5]: (3,)

In[6]: sub[0:2] = 12  # 对切片赋值,就是对切片中的每一个元素赋值

In[7]: arr
Out[7]: array([ 0,  1,  2,  3,  4, 12, 12, 7,  8,  9])

注意,区别于Python的内建列表,ndarray的切片是原数组的视图,这意味着数据并不是被复制了,任何对于视图的修改都会反映到原数组上。这是因为NumPy被设计成适合处理非常大的数组,你可以想象如果NumPy持续复制数据会引起内存和性能问题。

如果你是NumPy新手,你可能对此感到惊讶,因为其他的编程语言都是急切地复制数据。如果你坚持要得到一份数组切片的拷贝,而不是一份视图的话,可以试用ndarray.copy()方法显式地复制数据。

In [8]: sub = arr[0:3].copy()  # 显式地复制数据

In [9]: sub
Out[9]: array([0, 1, 2])

In [10]: sub[0:3] = 99

In [11]: sub
Out[11]: array([99, 99, 99])

# 通过复制的子集,不会影响原数组
In [12]: arr
Out[12]: array([ 0,  1,  2,  3,  4, 13, 13, 12,  8,  9])
多维数组的切片
In [1]: arr = np.arange(12).reshape(3,4)

In [2]: arr  # 一个3X4的二维数组
Out[2]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

# 对二维数组进行切片,得到一个子集
# 1:3表示从第2行开始,取3-1=2行
# 2:4表示从第3列开始,取4-2=2列
In [3]: arr[1:3, 2:4]
Out[3]:
array([[ 6,  7],
       [10, 11]])

注意:索引可以取得原数组降维的部分数据,而切片不会降低维度,而是得到一个维度相同的子集。妥善地混合索引和切片,就可以得到低维度的切片。

In [4]: arr[1, 1:3]  # 获取第2行,2、3列数据
Out[4]: array([5, 6])

单独一个冒号表示选择整个轴上的数组,并且对原数组的切片赋值,那么原数组也会被赋值。这也是因为NumPy被设计成适合处理非常大的数组,因此缺省是按地址引用。

In [1]: arr = np.arange(12).reshape(3,4)

In [2: arr
Out[2]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

# 获取所有行、第1列的切片
In [3]: sub = arr[:, :1]

In [4]: sub
Out[4]:
array([[0],
       [4],
       [8]])

# 把切片中的所有元素赋值为0
In [5]: sub[:,:] = 0

# 对切片的赋值,会影响到原数组
In [6]: arr
Out[6]:
array([[ 0,  1,  2,  3],
       [ 0,  5,  6,  7],
       [ 0,  9, 10, 11]])

神奇索引

神奇索引这个词很容易让人误解,它是NumPy的一个术语。简单来讲,神奇索引的目的是用整数数组对ndarray数据进行索引。

假设我们有一个8X4的数组:

In [1]: arr = np.empty((8,4))

In [2]: for i in range(8):
    ...:     arr[i] = i+1

In [3]: arr
Out[3]:
array([[1., 1., 1., 1.],
       [2., 2., 2., 2.],
       [3., 3., 3., 3.],
       [4., 4., 4., 4.],
       [5., 5., 5., 5.],
       [6., 6., 6., 6.],
       [7., 7., 7., 7.],
       [8., 8., 8., 8.]])

如果我们要从如上的数组中,选择第5、4、1、7行组成的子集,那么可以传递[4,3,0,6]来得到(注意数组下标从0开始)

In [4]: arr[[4, 3, 0, 6]]
Out[4]:
array([[5., 5., 5., 5.],
       [4., 4., 4., 4.],
       [1., 1., 1., 1.],
       [7., 7., 7., 7.]])

如果使用负数索引,将从尾部进行选择:

In [5]: arr[[-3, -5, -7]]
Out[5]:
array([[6., 6., 6., 6.],
       [4., 4., 4., 4.],
       [2., 2., 2., 2.]])

注意上面的例子,原数组是二维的,但传入了一个索引维度,所以是得到了降维后的子集。如果传入两个索引,将得到一维数组。

In [1]: arr = np.arange(12).reshape(3,4)

In [2]: arr
Out[2]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

# 选去第1行第2个元素、以及第3行第4个元素。
In [3]: arr[[0,2], [1, 3]]
Out[3]: array([ 1, 11])

请牢记:神奇索引与切片不同,它总是将数据复制到一个新的数组中。

布尔索引

假设我们有7X3的数据,这7行数据数据属于3个人,如下示例:

In [1]: names = np.array(['张三', '王五', '李四', '张三', '王五', '王五', '李四'])

# 生成一个7X3的数组,数组中的每个元素是0~10的随机数
In [2]: data = np.random.randint(low=0, high=10, size=(7,3))

In [3]: data
Out[3]:
array([[4, 0, 2],
       [8, 1, 5],
       [3, 3, 1],
       [4, 9, 3],
       [3, 7, 3],
       [7, 8, 4],
       [5, 4, 7]])

用names数组与字符串'张三'比较会产生一个布尔值数组:

In [4]: names == '张三'
Out[4]: array([ True, False, False,  True, False, False, False])

如果把这个布尔值数组当作“神奇索引”,传递给data数组,会得到如下结果

In [5]: data[names == '张三']
Out[5]:
array([[4, 0, 2],
       [4, 9, 3]])

如上,其效果等同于选择出了所有“张三”的行数据。

ndarray排序

一维数组的排序

数组实例可以使用sort方法进行排序。

In [1]: arr = np.random.randn(6)

In [2]: arr
Out[2]:
array([-1.51683294,  0.71207739, -1.91733282, -1.07901923, -0.35628366,
       -0.52888226])

In [3]: arr.sort()

In [4]: arr
Out[4]:
array([-1.91733282, -1.51683294, -1.07901923, -0.52888226, -0.35628366,
        0.71207739])

多维数组的排序

对于多维数组的排序,可以使用NumPy的顶层sort函数,该函数可以接送axis参数,以便按照某一个维度排序。

In [1]: arr = np.random.randint(-5, 5, (3,4))

In [2]: arr
Out[2]:
array([[ 2,  4, -4, -3],
       [-1, -5,  2,  0],
       [-3, -4, -3, -3]])

In [3]: np.sort(arr, axis=0)
Out[3]:
array([[-3, -5, -4, -3],
       [-1, -4, -3, -3],
       [ 2,  4,  2,  0]])

In [4]: np.sort(arr, axis=1)
Out[4]:
array([[-4, -3,  2,  4],
       [-5, -1,  0,  2],
       [-4, -3, -3, -3]])

你可能感兴趣的:(第4章 Numpy基础(2))