文章目录NumPy(Numerical Python的简称)是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。本篇总结梳理了数据分析中使用频率相对较高的一些NumPy基本命令。
- NumPy基本命令汇总整理
- 导入包
- `ndarray`: 一种多维数组对象
- `Numpy`数组的运算
- 索引和切片
- 通用函数:快速的元素级数组函数
- 利用数组进行数据处理
import numpy as npndarray: 一种多维数组对象
ndarray是一个通用的同构数据多维容器,也就是说,其中的所有元素必须是相同类型的。每个数组都有一个shape(一个表示各维度大小的元组)、一个dtype(一个用于说明数组数据类型的对象)和一个strides(一个用于说明为了前进到当前维度下一个元素需要“跨过”的字节数的跨度元组)。
常用命令:
- ①创建ndarray:
- np.array(),将输入数据(列表、元组、数组或其它序列类型)转换为ndarray。要么推断出dtype,要么指定特别dtype。默认直接复制输入数据。
- np.zeros() : 创建指定长度或形状的全0数组,只需传入一个表示形状的元组。
- np.zeros_like() : 返回与指定数组同样形状和数据类型的全0数组。
- np.ones() : 创建指定长度或形状的全1数组,只需传入一个表示形状的元组。
- np.ones_like() : 返回与指定数组同样形状和数据类型的全1数组。
- np.empty()| np.empty_like() : 创建没有任何具体值的数组。
- np.arange():类似于内置的range,但返回的是一个ndarray而不是列表。
- np.full(shape, fill_value)|np.full_like():用fill_value中的所有值,根据指定的形状和dtype创建数组。
- ②查看shape: a.shape
- ③数据类型:
- 查看数据类型: a.dtype
- 更改数据类型:a.astype(dtype)
- 检查是否为某类型:np.issubdtype(a.dtype, np.integer),np.issubdtype(a.dtype, np.floating),np.issubdtype(a.dtype, np.number)
- ④数据重塑:
- 将一维数组转换为多维:a.reshape(shape, order = 'C'),它返回的是源数据的视图(不会进行任何复制操作)。注意,作为参数的形状的其中一维可以是-1,它表示该维度的大小由数据本身推断而来。下图说明了order参数所起的作用:
- 将多维数组转换为一维:
- a.ravel():扁平化或散开,不产生源数据的副本
- a.flatten():扁平化或散开,但产生源数据的副本
- a.T,用于简单的转置即轴转换,在进行矩阵内积运算时会用到此命令。
- a.transpose(*axes),用于高维数组,*axes为由轴编号组成的元组。
- a.swapaxes(axis1, axis2),对axis1和axis1进行轴转换。同样返回的是源数据的视图。
- 将一维数组转换为多维:a.reshape(shape, order = 'C'),它返回的是源数据的视图(不会进行任何复制操作)。注意,作为参数的形状的其中一维可以是-1,它表示该维度的大小由数据本身推断而来。下图说明了order参数所起的作用:
- ⑤数组的合并和拆分:
- np.concatenate((a1,a2,....), axis=0):按指定轴将一个由数组组成的序列(如元组、列表等)连接到一起。
- np.hstack(tup):将2个矩阵按列合并。
- np.vstack(tup):将2个矩阵按行合并。
- np.split(a, indices_or_sections, axis=0):将一个数组沿指定轴拆分为多个数组。
- ⑥元素的重复操作:
- a.repeat(repeats, axis=None):将各个元素重复一定的数量(repeats)。默认情况下,如果传入的是一组整数,则各个元素可以重复不同的次数。
- np.tile(A, reps):沿指定轴堆叠数组的副本。reps为表示堆叠布局的元组或标量,如果是标量,默认为水平堆叠。
- ⑦伪随机数生成:
- np.random.normal():标准正态(高斯)分布样本值
- np.random.seed():更改随机数生成种子
- np.random.rand:产生均匀分布的样本值
- np.random.randint(low,high,size):从给定的上(exclusive)下(inclusive)限范围内随机选取整数
- np.random.randn:产生正态分布(平均值为0,标准差为1)的样本值
- np.random.shuffle:对一个序列就地随机排列
- np.random.permutation:返回一个序列的随机排列或返回一个随机排列的范围
- ①形状相同的数组间运算:
- shape相同的数组之间的任何算术运算都会将运算应用到元素级
- 数组与标量的算术运算会将标量值传播到各个元素
- shape相同的数组之间的比较会生成布尔值数组
- ②形状不同的数组间运算:
- 广播
比如,下方的例子中,希望通过减去列平均值的方式对数组的每一列进行距平化处理。因为arr.mean(0)的形状为(3,),而arr的后缘维度为3,其与arr的后缘维度是相同的,所以它们是广播兼容的。广播在行的维度上进行。
# 对数组每一列进行距平均化
>>> arr = np.random.randn(4, 3)
>>> arr
array([[-0.18889954, -2.00928225, -2.3916824 ],
[-2.46055652, -1.02446526, -1.03097103],
[ 0.48938275, 0.37362381, 0.63674138],
[-1.41777675, 0.58666021, -0.86618113]])
>>> arr.mean(0).shape
(3,)
>>> arr - arr.mean(0)
array([[ 0.70556298, -1.49091638, -1.4786591 ],
[-1.56609401, -0.50609939, -0.11794773],
[ 1.38384527, 0.89198968, 1.54976468],
[-0.52331424, 1.10502608, 0.04684216]])
而如果希望对各行减去行平均值,因为arr.mean(1)的形状为(4,),而arr的后缘维度为3,其与arr的后缘维度不同。根据广播原则,要在1轴上广播做减法,较小的那个数组的形状必须是(4,1)。
# 对数组每一行进行距平均化
>>> arr
array([[-0.18889954, -2.00928225, -2.3916824 ],
[-2.46055652, -1.02446526, -1.03097103],
[ 0.48938275, 0.37362381, 0.63674138],
[-1.41777675, 0.58666021, -0.86618113]])
>>> arr.mean(1).shape
(4,)
>>> arr - arr.mean(1)
Traceback (most recent call last):
File "", line 1, in
ValueError: operands could not be broadcast together with shapes (4,3) (4,)
>>> arr - arr.mean(1).reshape((4,1))
array([[ 1.34105519, -0.47932753, -0.86172767],
[-0.95522558, 0.48086568, 0.47435991],
[-0.01053323, -0.12629217, 0.1368254 ],
[-0.85201086, 1.1524261 , -0.30041524]])
对于三维的情况,下图说明了要在三维数组各维度上广播的形状需求:
在为了广播而添加一个长度为1的新轴时,可以使用np.newaxis属性以及“全”切片来插入新轴:
>>> arr = np.zeros((4,4))
>>> arr
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> arr_3d = arr[:,np.newaxis,:]
>>> arr_3d.shape
(4, 1, 4)
>>> arr_1d = np.random.normal(size=3)
>>> arr_1d
array([-0.53656014, -0.821661 , -0.07331854])
>>> arr_1d[:,np.newaxis]
array([[-0.53656014],
[-0.821661 ],
[-0.07331854]])
>>> arr_1d[np.newaxis,:]
array([[-0.53656014, -0.821661 , -0.07331854]])
索引和切片说明:上述添加新轴的方式同样适用于通过广播进行赋值,只要保证形状兼容即可。
几种索引方式:基本索引、切片索引、布尔式索引、花式索引
基本索引:下图说明了二维数组基本的索引方式:
常用命令:
- arr[start:end] = some value:给数组中某个切片赋值,当将一个标量值赋值给一个切片时(如arr[5:8]=12),该值会自动传播(即,“广播”)到整个选区。
- arr[:] = some value:给数组中的所有值赋值。
切片索引:切片默认是沿着第0轴(即第一个轴)切片的,如表达式arr[:2]可以被认为是“选取arr的前两行”。
>>> arr2d =np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> arr2d
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> arr2d[:2]
array([[1, 2, 3],
[4, 5, 6]])
下图为二维数组切片的简单示例:
布尔型索引:组合应用多个布尔条件,使用&(和)、|(或)之类的布尔算术运算符,且每个条件需用括号括起来,比如 (cond1) | (cond2)。注意:通过布尔型索引选取数组中的数据,将总是创建数据的副本。
花式索引:可以实现以特定顺序选取行子集,只需传入一个用于指定顺序的整数列表或ndarray即可。而使用负数索引将会从末尾开始选取行。同样需注意,花式索引跟切片不一样,它总是将数据复制到新数组中。
一次传入多个索引数组会有一点特别。它返回的是一个一维数组,其中的元素对应各个索引元组,如下方的例子中,最终选出的元素是(1,0)、(5,3)、(7,1),(2,2):
>>> arr = np.arange(32).reshape((8, 4))
>>> arr
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]])
>>> arr[[1,5,7,2],[0,3,1,2]]
array([ 4, 23, 29, 10])
需要说明:除布尔型索引、花式索引外,一般的情况下,数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到源数组上。
注意下方的例子,选取arr的一个切片arr_slice,当修改arr_slice时,原数组也发生了变化!
>>> arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> arr_slice = arr[5:8]
>>> arr_slice
array([5, 6, 7])
>>> arr_slice[1] = 12345
>>> arr
array([ 0, 1, 2, 3, 4, 5, 12345, 7, 8,
9])
注意:如果你想要得到的是ndarray切片的一份副本而非视图,就需要明确地进行复制操作,例如arr[5:8].copy()。
再看一下下方的例子:
>>> arr = np.arange(10) >>> arr array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> arr_slice = arr[5:8] >>> arr_slice array([5, 6, 7]) >>> arr = np.arange(10) >>> arr array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> arr_slice = arr[5:8].copy() >>> arr_slice[1] = 12345 >>> arr_slice array([ 5, 12345, 7]) >>> arr array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])通用函数:快速的元素级数组函数
通用函数(即ufunc)是一种对ndarray中的数据执行元素级运算的函数。
- 一元(unary)ufunc:接受1个数组
- 二元(binary)ufunc:接受2个数组
注意:Ufuncs可以接受一个out可选参数,这样就能在数组原地进行操作。
比如下方示例:
>>> arr = np.random.randn(7) * 5
>>> arr
array([-5.93919409, -2.97839885, 1.18773417, 1.61830444, -6.56820877,
3.94290995, -3.82345097])
>>> np.sqrt(arr)
:1: RuntimeWarning: invalid value encountered in sqrt
array([ nan, nan, 1.08983217, 1.27212595, nan,
1.98567619, nan])
>>> np.sqrt(arr,arr)
array([ nan, nan, 1.08983217, 1.27212595, nan,
1.98567619, nan])
>>> arr
array([ nan, nan, 1.08983217, 1.27212595, nan,
1.98567619, nan])
>>>
此外,还有一些高级的ufunc应用:
- reduce(x):通过连续执行原始运算的方式对值进行聚合,比如np.add.reduce(arr)等同于np.sum(arr)。
- accumulate(x):聚合值,保留所有局部聚合结果,比如np.add.accumulate(arr)等同于np.cumsum(arr)。
- reduceat(x,bins):“局部”约简(也就是groupby)。约简数据的各个切片以产生聚合型数组,比如np.add.reduceat(arr,[0,5,8])是在arr[0:5]、arr[5:8]以及arr[8:]上执行的求和约简。
- outer(x,y):对x和y中的每对元素应用原始运算。结果数组的形状为x.shape + y.shape。
NumPy数组使你可以将许多种数据处理任务表述为简洁的数组表达式(否则需要编写循环),即矢量化。一般来说,矢量化数组运算要比等价的纯Python方式快上一两个数量级(甚至更多)。
常用命令:
- np.where(condition, x,y):根据condition选择x中的值或y中的值,是三元表达式x if condition else y的矢量化表达;x、y可以是数组,也可以是标量值。
- 数学和统计方法:
- a.sum():对数组中全部或某轴向的元素求和
- a.mean():算术平均数
- a.std()|a.var():标准差和方差
- a.min()|a.max():最小值和最大值
- a.argmin()|a.argmax():最小和最大元素的索引
- a.cumsum():所有元素的累计和
- a.cumprod():所有元素的累计积
argmax一个巧妙的用法:获取布尔型数组第一个最大值的索引(True就是最大值)。
比如,希望获取arr第一个大于5的值的位置索引:
>>> arr = np.array([1,3,2,5,4,10,4]) >>> arr array([ 1, 3, 2, 5, 4, 10, 4]) >>> (arr>4).argmax() 3
- 用于布尔型数组的方法:
- .sum():经常被用来对布尔型数组中的True值计数
- .any():用于测试数组中是否存在一个或多个True
- .all():用于检查数组中所有值是否都是True
- 排序:
- a.sort(axis):在轴向上进行排序,顶级方法np.sort返回的是数组的已排序副本,而就地排序则会修改数组本身。
- arr[:,::-1],返回一个列反序的列表。
- arr.argsort():返回用于数组排序的索引。
- arr.lexsort():类似arr.argsort(),只不过可以一次性对多个键数组执行间接排序。
- arr.searchsorted(data):在有序数组arr中查找data中的元素插入后的位置索引。
以下为使用argsort()命令实现部分排序数组的示例:
# 根据数组的第一行排序
>>> arr = np.random.randn(3,5)
>>> values = np.array([5,0,1,3,2])
>>> arr[0] =values
>>> arr
array([[ 5.00000000e+00, 0.00000000e+00, 1.00000000e+00,
3.00000000e+00, 2.00000000e+00],
[ 1.23514614e+00, 1.35506346e-01, -7.05498872e-04,
2.53602483e-01, -1.83245736e-01],
[-7.06630296e-01, 4.26760743e-01, -2.77577070e-01,
-8.28284581e-01, -2.76283358e+00]])
>>> arr[:,arr[0].argsort()]
array([[ 0.00000000e+00, 1.00000000e+00, 2.00000000e+00,
3.00000000e+00, 5.00000000e+00],
[ 1.35506346e-01, -7.05498872e-04, -1.83245736e-01,
2.53602483e-01, 1.23514614e+00],
[ 4.26760743e-01, -2.77577070e-01, -2.76283358e+00,
-8.28284581e-01, -7.06630296e-01]])
以下为使用searchsorted命令实现数据数组面元切分的示例:
# 根据“面元边界”数组拆分数据数组
>>> data = np.floor(np.random.uniform(0,10000,size=50))
>>> bins = np.array([0,100,1000,5000,10000])
>>> data
array([7.194e+03, 1.291e+03, 4.934e+03, 8.405e+03, 8.240e+03, 1.784e+03,
4.710e+03, 5.545e+03, 3.315e+03, 5.217e+03, 1.110e+02, 2.937e+03,
7.204e+03, 8.029e+03, 2.849e+03, 3.814e+03, 5.179e+03, 7.877e+03,
3.910e+02, 4.625e+03, 9.455e+03, 3.580e+02, 3.770e+03, 1.123e+03,
4.276e+03, 1.846e+03, 4.529e+03, 2.485e+03, 7.122e+03, 7.936e+03,
7.573e+03, 2.222e+03, 7.643e+03, 7.839e+03, 4.581e+03, 7.493e+03,
9.578e+03, 1.930e+02, 2.845e+03, 7.794e+03, 4.001e+03, 8.519e+03,
4.281e+03, 6.000e+00, 8.091e+03, 8.760e+02, 1.220e+03, 2.005e+03,
7.626e+03, 8.274e+03])
>>> labels = bins.searchsorted(data)
>>> labels
array([4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 2, 3, 4, 4, 3, 3, 4, 4, 2, 3, 4, 2,
3, 3, 3, 3, 3, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 2, 3, 4, 3, 4, 3, 1,
4, 2, 3, 3, 4, 4], dtype=int64)
Tips: 因为sort方法不可以被设置的降序,这时可以使用类似value[::-1]的方法产生一个反序的数组。
- 数组的集合运算:
- np.unique():找出数组中的唯一值并返回已排序的结果。
- np.intersect1d(x,y):计算x和y中的公共元素,并返回有序结果。
- np.union1d(x,y):计算x和y的并集,并返回有序结果。
- in1d(x,y):得到一个表示“x的元素是否包含于y”的布尔型数组。
- setdiff1d(x,y):集合的差,即元素在x中且不在y中。
- setxor1d(x,y):集合的对称差,即存在于一个数组中但不同时存在于两个数据中的元素。



