机器学习基础:对NumPy数组进行数据处理操作
编辑在将目标数据加载到NumPy数组中后,就需要进行一系列的数组处理操作,包括:对NumPy数组的访问、切片、过滤、计算数据的统计学指标、数据间的聚合和组合以及对计算后的结果保存和再加载的操作,本篇博文就对上述常用操作进行了阐述...
访问NumPy
数组和对Numpy
数组切片
对NumPy
数组进行访问
对于NumPy
数组中的数据的索引方式与Python
中的list
的索引访问是一致的,均可以通过数组的下标来进行访问。对于NumPy
中的多维数组,它等价于Python
中的list
中的list
arr = np.array([1, 2, 3, 4, 5])
print(arr[0])
print(arr[4])
arr = np.array([[6, 3], [0, 2]])
# Subarray
print(repr(arr[0]))
对NumPy
数组切片
NumPy
数组也支持切片,可以使用:
运算符来进行切片。在NumPy
数组切片时,也可以使用负数指定索引方向是从后往前的方向进行索引的。
下面的示例是对一维数组进行切片的代码示例:
arr = np.array([1, 2, 3, 4, 5])
print(repr(arr[:]))
print(repr(arr[1:]))
print(repr(arr[2:4]))
print(repr(arr[:-1]))
print(repr(arr[-2:]))
需要注意的是,在切片时,它遵循的也是左闭右开的表示方式,即:[m, n)
,不包括索引为n
的元素。
对于多维数组的切片,使用逗号,
将各个维度分开即可。
arr = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(repr(arr[:]))
print(repr(arr[1:]))
print(repr(arr[:, -1]))
print(repr(arr[:, 1:]))
print(repr(arr[0:1, 1:]))
print(repr(arr[0, 1:]))
返回最大值或最小值元素的索引下标
有的时候,我们经常需要返回数组中最大值或者最小值元素所在的索引下标,可以使用np.argmax
和np.argmin
分别获取最大值和最小值元素所在的索引下标值。
arr = np.array([[-2, -1, -3],
[4, 5, -6],
[-3, 9, 1]])
print(np.argmin(arr[0]))
print(np.argmax(arr[2]))
print(np.argmin(arr))
对于多维数组的argmax
或者argmin
,它返回的索引下标相当于将给定的多维数组flatten
为一维数组后,在一维数组中的索引下标值。
np.argmin
和np.argmax
还有一个重要的参数axis
,这个关键字参数可以指定是对于哪一维度进行的操作。
arr = np.array([[-2, -1, -3],
[4, 5, -6],
[-3, 9, 1]])
print(repr(np.argmin(arr, axis=0)))
print(repr(np.argmin(arr, axis=1)))
print(repr(np.argmax(arr, axis=-1)))
arr = np.arange(24)
reshaped = arr.reshape((2, 4, 3))
np.random.shuffle(reshaped)
print(repr(reshaped))
print(repr(np.argmin(reshaped, axis=-1)))
当axis=0
时,相当于对列做argmax/argmin
的操作,返回行下标;当axis=1
时,相当于对行做argmax/argmin
操作,返回列下标值;当axis=-1
相当于对数组的最后一个维度做argmax/agmin
,在上例中,由于arr
是二维数组,所以等价于是对行做argmax/argmin
操作,返回列下标值。简而言之:axis
指定的是返回的是最大值或者最小值所在位置的行下标值还是列下标值。
过滤NumPy
数组中的数据
过滤数据
有的时候,我们只关心数据中的符合条件的那一部分,而不是数据的整体。因此我们需要对数据进行过滤。比如说:当我们需要统计某一类别的图书的售价时,我们只关心数据中每个图书的售价部分,而不是所有的数据。
过滤数据的关键是通过基础关系运算符,如:==
, >
等等。在NumPy
的数组中逐个元素的做基础关系运算。
arr = np.array([[0, 2, 3],
[1, 3, -6],
[-3, -2, 1]])
print(repr(arr == 3))
print(repr(arr > 0))
print(repr(arr != 1))
# Negated from the previous step
print(repr(~(arr != 1)))
Numpy
之所以操作如此方便,就在于它对于常用的运算符进行了操作符重载,方便我们的使用。
上面示例中的关系运算的结果是一个boolean
数组,它标志着数组所在位置中的元素是否符合所进行的关系运算。需要注意的是:np.nan
不能进行任何的关系运算,它提供了一个专有的内置函数来判断一个值是否是nan
,这个函数是np.isnan
。
arr = np.array([[0, 2, np.nan],
[1, np.nan, -6],
[np.nan, -2, 1]])
print(repr(np.isnan(arr)))
在NumPy
中进行过滤
np.where
函数接受一个boolean
数组,返回boolean
数组中值为True
的数组下表所有所构成的数组的元组。一般情况下,boolean
数组的True
标志的是我们所关心的值。
print(repr(np.where([True, False, True])))
上述代码示例返回的是一维数组的元组,这个元组的元素的个数等于数据的维度个数。上述元组中的每一个数组表示的是在该维度下,True
元素所在的位置的索引下标。
arr = np.array([0, 3, 5, 3, 1])
print(repr(np.where(arr == 3)))
arr = np.array([[0, 2, 3],[1, 0, 0], [-3, 0, 0]])
x_ind, y_ind = np.where(arr != 0)
print(repr(x_ind)) # x indices of non-zero elements
print(repr(y_ind)) # y indices of non-zero elements
print(repr(arr[x_ind, y_ind]))
在使用np.where
函数时,它只接受1个参数或者3个参数。当使用3个参数时,第一个实参仍然是一个boolean
数组,而第二个参数和第三个参数分别指的是boolean
数组中的True
所在的位置的元素的替换值以及False
所在的位置的元素的替换值。说的有点绕,可以看下面的代码示例:
np_filter = np.array([[True, False], [False, True]])
positives = np.array([[1, 2], [3, 4]])
negatives = np.array([[-2, -5], [-1, -8]])
print(repr(np.where(np_filter, positives, negatives)))
np_filter = positives > 2
print(repr(np.where(np_filter, positives, negatives)))
np_filter = negatives > 0
print(repr(np.where(np_filter, positives, negatives)))
输出的结果为:
array([[ 1, -5],
[-1, 4]])
array([[-2, -5],
[ 3, 4]])
array([[-2, -5],
[-1, -8]])
需要注意的是:np.where
的第二个参数和第三个参数要和第一个参数的形状是相同的。但是当我们想要使用一个常量替换值时,可以使用broadcasting
技术,我们可以使用这个常量值本身作为参数而不是由它构成的数组。其他情况下,第二个和第三个参数的形状要与第一个参数的形状相同。
np_filter = np.array([[True, False], [False, True]])
positives = np.array([[1, 2], [3, 4]])
print(repr(np.where(np_filter, positives, -1)))
轴向过滤(Axis-wise filtering)
当我们的过滤操作是基于行数据或者列数据时,可以使用np.any
和np.all
函数。它们所接受的参数是相同的,都是一个boolean
数组,返回的是一个布尔值或者一个布尔数组。
arr = np.array([[-2, -1, -3],
[4, 5, -6],
[3, 9, 1]])
print(repr(arr > 0))
print(np.any(arr > 0))
print(np.all(arr > 0))
np.any
函数等价于逻辑或操作,只要有一个符合条件,则值为真,否则为假。np.all
函数等价于逻辑与操作,必须所有的都要符合条件,否则值为假。
当只传入一个参数,它对于整个传入数组做操作,所以这两个函数返回的都是一个布尔值。当不仅仅传入一个参数,指定axis
关键字参数时,返回的就会是一个布尔数组。当axis=0
时,相当于是对数组中逐列做操作;当axis=1
时,相当于是对数组中逐行做某种操作;当axis=-1
时,以数组中的最后一个维度为单位逐一的做某种动作。简而言之:当指定axis
时,相当于对另一维度逐一验证是否满足某一条件。
arr = np.array([[-2, -1, -3],
[4, 5, -6],
[3, 9, 1]])
print(repr(arr > 0))
print(repr(np.any(arr > 0, axis=0)))
print(repr(np.any(arr > 0, axis=1)))
print(repr(np.all(arr > 0, axis=1)))
可以将np.any
, np.all
和np.where
组合使用,过滤出一行或者一列数据。示例代码如下:
arr = np.array([[-2, -1, -3], [4, 5, -6], [3, 9, 1]])
has_positive = np.any(arr > 0, axis=1)
print(has_positive)print(repr(arr[np.where(has_positive)]))
NumPy
数组中的统计学指标
获取数组中的最大最小值
在NumPy
的数组对象中,内置了min
和max
函数,可以返回当前数组对象中所存储的数据的最小或者最大值。它们也都有axis
关键词参数,它的值与上面两节提到的用法相同。
arr = np.array([[0, 72, 3], [1, 3, -60],[-3, -2, 4]])
print(arr.min())
print(arr.max())
print(repr(arr.min(axis=0)))
print(repr(arr.max(axis=-1)))
统计学指标
np.mean
, np.median
, np.var
分别用来计算均值、中位数以及方差。
arr = np.array([[0, 72, 3], [1, 3, -60], [-3, -2, 4]])
print(np.mean(arr))
print(np.var(arr))
print(np.median(arr))
print(repr(np.median(arr, axis=-1)))
可以在NumPy中的统计学指标查看其余的统计学指标
NumPy
中的数据聚合技术
对数组求和
对于一维数组可以使用np.sum
函数求累加和。对于多维数组,可以通过axis
关键词参数指定对哪一个维度求累加和,其用法与之前两节所提到的用法一致;如果没有指定axis
最是对整个数组求累加和
arr = np.array([[0, 72, 3], [1, 3, -60], [-3, -2, 4]])
print(np.sum(arr))
print(repr(np.sum(arr, axis=0)))
print(repr(np.sum(arr, axis=1)))
如果想要求某个数组的前缀和,可以使用np.cumsum
进行计算。np.cumsum
也有两个参数,第一个参数待求和的数组,第二个参数是axis
参数。如果axis
参数没有指定,则相当于将多维数组平坦化(flatten)后,求前缀和。
arr = np.array([[0, 72, 3], [1, 3, -60], [-3, -2, 4]])
print(repr(np.cumsum(arr)))
print(repr(np.cumsum(arr, axis=0)))
print(repr(np.cumsum(arr, axis=1)))
结果为:
array([ 0, 72, 75, 76, 79, 19, 16, 14, 18])
array([[ 0, 72, 3],
[ 1, 75, -57],
[ -2, 73, -53]])
array([[ 0, 72, 75],
[ 1, 4, -56],
[ -3, -5, -1]])
级联技术(Concatenation)
在NumPy
中,对于不同数据集中的数据聚合在一起相当于是多个数组组合并成一个数组。有多种实现方式,如np.concatenate
,np.hstack
和np.vstack
等。其中hstack
其实就是水平拼接(按列顺序堆叠);而vstack
其实就是垂直拼接(按行顺序堆叠)。
本文中重点讲解的是np.concatenate
,该函数有两个参数需要注意,第一个参数是要拼接的数组的列表,第二个参数是axis
关键词参数,用来指定拼接的方式,默认是axis=0
,也就是按行顺序拼接(垂直拼接)
arr1 = np.array([[0, 72, 3], [1, 3, -60], [-3, -2, 4]])
arr2 = np.array([[-15, 6, 1], [8, 9, -4], [5, -21, 18]])
print(repr(np.concatenate([arr1, arr2])))
print(repr(np.concatenate([arr1, arr2], axis=1)))
print(repr(np.concatenate([arr2, arr1], axis=1)))
拼接结果如下所示:
array([[ 0, 72, 3],
[ 1, 3, -60],
[ -3, -2, 4],
[-15, 6, 1],
[ 8, 9, -4],
[ 5, -21, 18]])
array([[ 0, 72, 3, -15, 6, 1],
[ 1, 3, -60, 8, 9, -4],
[ -3, -2, 4, 5, -21, 18]])
array([[-15, 6, 1, 0, 72, 3],
[ 8, 9, -4, 1, 3, -60],
[ 5, -21, 18, -3, -2, 4]])
将计算结果保存
当我们进行了一系列的操作后,肯定要把运算的结果保存下来,否则每次都需要从头处理,耗时耗力。NumPy
提供了一键序列化的功能。
使用np.save
保存数据,保存的文件格式后缀为.npy
,如果未指定,NumPy
会自动添加一个.npy
的后缀;而np.load
可以加在.npy
格式的文件,但是需要注意的是,如果在调用的时候,给定的文件路径未带有.npy
后缀,NumPy
不会自动补上这个后缀。
保存数据的示例代码:
arr = np.array([1, 2, 3]) # Saves to 'arr.npy'
np.save('arr.npy', arr) # Also saves to 'arr.npy'np.save('arr', arr)
加在数据的示例代码:
arr = np.array([1, 2, 3])
np.save('arr.npy', arr)
load_arr = np.load('arr.npy')
print(repr(load_arr))
# Will result in FileNotFoundError
load_arr = np.load('arr')
- 0
-
分享