《利用Python进行数据分析·第2版》第4章 NumPy基础:数组和矢量计算(1)


4.1 NumPy的ndarray:一种多维数组对象
NumPy最重要的一个特点就是其N维数组对象(即ndarray),该对象是一个快速而灵活的大数据集容器。你可以利用这种数组对整块数据执行一些数学运算,其语法跟标量元素之间的运算一样。

ndarray是一个通用的同构数据多维容器,也就是说,其中的所有元素必须是相同类型的。每个数组都有一个shape(一个表示各维度大小的元组)和一个dtype(一个用于说明数组数据类型的对象)。

1)创建ndarray
创建数组最简单的办法就是使用array函数。它接受一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组。
=====================================
>>> import numpy as np
>>> data1 = [6, 7.5, 8, 0, 1]
>>> arr1 = np.array(data1)
>>> arr1
array([ 6. ,  7.5,  8. ,  0. ,  1. ])

嵌套序列(比如由一组等长列表组成的列表)将会被转换为一个多维数组:
=====================================
>>> data2 = ([1, 2, 3, 4], [5, 6, 7, 8])
>>> arr2 = np.array(data2)
>>> arr2
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])

np.array会尝试为新建的这个数组推断出一个较为合适的数据类型。数据类型保存在一个特殊的dtype对象中:
=====================================
>>> arr1.dtype
dtype('float64')
>>> arr2.dtype
dtype('int32')

zeros和ones分别可以创建指定长度或形状的全0或全1数组。empty可以创建一个没有任何具体值的数组。
=====================================
>>> np.zeros(10)
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])
>>> np.zeros((3, 6))
array([[ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.]])

>>> np.empty((2, 3, 2))
array([[[  6.23042070e-307,   1.42417221e-306],
        [  1.37961641e-306,   1.11261027e-306],
        [  1.11261502e-306,   1.42410839e-306]],

       [[  7.56597770e-307,   6.23059726e-307],
        [  1.42419530e-306,   7.56599128e-307],
        [  1.78022206e-306,   8.34451503e-308]]])
注意:认为np.empty会返回全0数组的想法是不安全的。很多情况下(如前所示),它返回的都是一些未初始化的垃圾值。

arange是Python内置函数range的数组版:
>>> np.arange(15)
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

由于NumPy关注的是数值计算,因此,如果没有特别指定,数据类型基本都是float64(浮点数):
函数        说明
array        将输入数据(列表、元组、数组或其它序列类型)转换为ndarray。要么推断出dtype,要么特别制定dtype。默认直接复制输入数据
asarray        将输入转换ndarray,如果输入本身就是一个ndarray就不进行复制
ones,ones_like    根据指定的形状和dtype创建一个全1数组。one_like以另一个数组为参数,并根据其形状和dtype创建一个全1数组
zeros,zeros_like 类似于ones和ones_like,只不过产生的是全0数组
empty,empty_like 创建新数组,只分配内存空间但不填充任何值
full,full_like     用fill value中的所有值,根据指定的形状和dtype创建一个数组。full_like使用另一个数组,用相同的形状和dtype创建
eye,identity     创建一个正方的 N X N 单位矩阵(对角线为1,其余为0)

2)ndarray的数据类型
dtype(数据类型)是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息:
>>> arr1 = np.array([1,2,3], dtype=np.float64)
>>> arr2 = np.array([1,2,3], dtype=np.int32)
>>> arr1.dtype
dtype('float64')
>>> arr2.dtype
dtype('int32')

数值型dtype的命名方式相同:一个类型名(如float或int),后面跟一个用于表示各元素位长的数字。标准的双精度浮点值(即Python中的float对象)需要占用8字节(即64位)。数据类型有:int8/int16/int32/int64/float16/float32/float64/float128/complex64(分别用两个32位、64位或128位浮点数表示的复数)/complex128/complex256/bool/object(Python对象类型)/string(固定长度的字符串类型‘每个字符1个字节’)/unicode(固定长度的unicode‘字节数由平台决定’)

可以通过ndarray的astype方法明确地将一个数组从一个dtype转换成另一个dtype:
>>> arr = np.array([1,2,3,4,5])
>>> arr.dtype
dtype('int32')
>>> float_arr = arr.astype(np.float64)
>>> float_arr.dtype
dtype('float64')

如果将浮点数转换成整数,则小数部分将会被截取删除:
>>> import numpy as np
>>> arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
>>> arr
array([  3.7,  -1.2,  -2.6,   0.5,  12.9,  10.1])
>>> arr.astype(np.int32)
array([ 3, -1, -2,  0, 12, 10])

如果某字符串数组表示的全是数字,也可以用astype将其转换为数值形式:
>>> numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
>>> numeric_strings.astype(float)
array([  1.25,  -9.6 ,  42.  ])

3)NumPy数组的运算
数组很重要,因为它使你不用编写循环即可对数据执行批量运算。NumPy用户称其为矢量化(vectorization)。大小相等的数组之间的任何算术运算都会将运算应用到元素级:
>>> arr = np.array([[1.,2.,3.],[4.,5.,6.]])
>>> arr
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])
>>> arr * arr
array([[  1.,   4.,   9.],
       [ 16.,  25.,  36.]])
>>> arr - arr
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

数组与标量的算术运算会将标量值传播到各个元素:
>>> arr
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])
>>> 1 / arr
array([[ 1.        ,  0.5       ,  0.33333333],
       [ 0.25      ,  0.2       ,  0.16666667]])
>>> arr ** 0.5
array([[ 1.        ,  1.41421356,  1.73205081],
       [ 2.        ,  2.23606798,  2.44948974]])

大小相同的数组之间的比较会生成布尔值数组:
>>> arr
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])
>>> arr2
array([[  0.,   4.,   1.],
       [  7.,   2.,  12.]])
>>> arr2 > arr
array([[False,  True, False],
       [ True, False,  True]], dtype=bool)

4)基本的索引和切片
>>> arr = np.arange(10)
>>> arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> arr[5]
5
>>> arr[5:8]
array([5, 6, 7])
>>> arr[5:8] = 12
>>> arr
array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])

如上所示,当你将一个标量值赋值给一个切片时(如arr[5:8]=12),该值会自动传播(也就说后面将会讲到的“广播”)到整个选区。

跟列表最重要的区别在于,数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到源数组上。

>>> arr
array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])
>>> arr_slice = arr[5:8]
>>> arr_slice
array([12, 12, 12])
>>> arr_slice[1] = 10086
>>> arr_slice
array([   12, 10086,    12])
>>> arr
array([    0,     1,     2,     3,     4,    12, 10086,    12,     8,     9])

注意:如果你想要得到的是ndarray切片的一份副本而非视图,就需要明确地进行复制操作,例如arr[5:8].copy()。
>>> arr
array([    0,     1,     2,     3,     4,    12, 10086,    12,     8,     9])
>>> arr_slice = arr[5:8].copy()
>>> arr_slice
array([   12, 10086,    12])
>>> arr_slice[1] = 12
>>> arr_slice
array([12, 12, 12])
>>> arr
array([    0,     1,     2,     3,     4,    12, 10086,    12,     8,     9])

5)切片索引
>>> arr
array([    0,     1,     2,     3,     4,    12, 10086,    12,     8,     9])
>>> arr[1:6]
array([ 1,  2,  3,  4, 12])
>>> arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> arr2d[:2]
array([[1, 2, 3],
       [4, 5, 6]])

可以看出,它是沿着第0轴(即第一个轴)切片的。也就是说,切片是沿着一个轴向选取元素的。表达式arr2d[:2]可以被认为是“选取arr2d的前两行”。

可以一次传入多个切片,就像传入多个索引那样:
>>> arr2d[:2]
array([[1, 2, 3],
       [4, 5, 6]])
>>> arr2d[:2,1:]
array([[2, 3],
       [5, 6]])
>>> arr2d[1,:2]
array([4, 5])
>>> arr2d[:2,2]
array([3, 6])

6)布尔型索引
假设我们有一个用于存储数据的数组以及一个存储姓名的数组(含有重复项)
>>> names = np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])
>>> data = np.random.randn(7,4)
>>> names
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], 
      dtype='<U4')
>>> data
array([[ 2.33435872, -0.09425565, -0.54968452,  1.25980465],
       [-1.24212636,  0.2105887 ,  0.62627693, -1.21755861],
       [ 0.95837351, -0.32158179, -0.56410757, -0.88881926],
       [ 1.542188  , -0.57044935, -0.86164921,  1.9207535 ],
       [-0.22747738,  1.37821422, -0.30053196,  0.17158332],
       [ 0.25252632, -0.95601655,  1.54038747, -0.91323128],
       [ 1.3660947 ,  0.01724225, -0.41897805, -0.25942858]])

对names和字符串"Bob"的比较运算将会产生一个布尔型数组:
>>> names == 'Bob'
array([ True, False, False,  True, False, False, False], dtype=bool)

这个布尔型数组可用于数组索引:
>>> data[names == 'Bob']
array([[ 2.33435872, -0.09425565, -0.54968452,  1.25980465],
       [ 1.542188  , -0.57044935, -0.86164921,  1.9207535 ]])

~操作符用来反转条件很好用:
>>> cond = names == 'Bob'
>>> data[~cond]
array([[-1.24212636,  0.2105887 ,  0.62627693, -1.21755861],
       [ 0.95837351, -0.32158179, -0.56410757, -0.88881926],
       [-0.22747738,  1.37821422, -0.30053196,  0.17158332],
       [ 0.25252632, -0.95601655,  1.54038747, -0.91323128],
       [ 1.3660947 ,  0.01724225, -0.41897805, -0.25942858]])

选取这三个名字中的两个需要组合应用多个布尔条件,使用&(和)、|(或)之类的布尔算术运算符即可:
>>> mask = (names == 'Bob') | (names == 'Will')
>>> mask
array([ True, False,  True,  True,  True, False, False], dtype=bool)
>>> data[mask]
array([[ 2.33435872, -0.09425565, -0.54968452,  1.25980465],
       [ 0.95837351, -0.32158179, -0.56410757, -0.88881926],
       [ 1.542188  , -0.57044935, -0.86164921,  1.9207535 ],
       [-0.22747738,  1.37821422, -0.30053196,  0.17158332]])

注意:Python关键字and和or在布尔型数组中无效。要使用&与|。

通过布尔型数组设置值是一种经常用到的手段。为了将data中的所有负值都设置为0,我们只需:
>>> data
array([[ 2.33435872, -0.09425565, -0.54968452,  1.25980465],
       [-1.24212636,  0.2105887 ,  0.62627693, -1.21755861],
       [ 0.95837351, -0.32158179, -0.56410757, -0.88881926],
       [ 1.542188  , -0.57044935, -0.86164921,  1.9207535 ],
       [-0.22747738,  1.37821422, -0.30053196,  0.17158332],
       [ 0.25252632, -0.95601655,  1.54038747, -0.91323128],
       [ 1.3660947 ,  0.01724225, -0.41897805, -0.25942858]])
>>> data[data < 0] = 0
>>> data
array([[ 2.33435872,  0.        ,  0.        ,  1.25980465],
       [ 0.        ,  0.2105887 ,  0.62627693,  0.        ],
       [ 0.95837351,  0.        ,  0.        ,  0.        ],
       [ 1.542188  ,  0.        ,  0.        ,  1.9207535 ],
       [ 0.        ,  1.37821422,  0.        ,  0.17158332],
       [ 0.25252632,  0.        ,  1.54038747,  0.        ],
       [ 1.3660947 ,  0.01724225,  0.        ,  0.        ]])

通过一维布尔数组设置整行或列的值也很简单:
>>> data[names != 'Joe'] = 7
>>> data
array([[ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 0.        ,  0.2105887 ,  0.62627693,  0.        ],
       [ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 0.25252632,  0.        ,  1.54038747,  0.        ],
       [ 1.3660947 ,  0.01724225,  0.        ,  0.        ]])

7)花式索引
花式索引(Fancy indexing)是一个NumPy术语,它指的是利用整数数组进行索引。
>>> arr = np.empty((8,4))
>>> for i in range(8):
    arr[i] = i
>>> arr
array([[ 0.,  0.,  0.,  0.],
       [ 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.]])

为了以特定顺序选取行子集,只需传入一个用于指定顺序的整数列表或ndarray即可:
>>> arr[[4,3,0,6]]
array([[ 4.,  4.,  4.,  4.],
       [ 3.,  3.,  3.,  3.],
       [ 0.,  0.,  0.,  0.],
       [ 6.,  6.,  6.,  6.]])

8)数组转置和轴对换
转置是重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制操作)。
>>> arr = np.arange(15).reshape((3,5))
>>> arr
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>> arr.T
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])

猜你喜欢

转载自blog.csdn.net/zhaocen_1230/article/details/82223473
今日推荐