NumPy 基础:数组和矢量计算

NumPy 基础:数组和矢量计算

NumPy 的 ndarray : 一种多维数组对象

import numpy as np
data = np.array()
data.shape  #对象的结构,如(2,3)
data.dtype  #对象元素的数据类型
data.ndim  #对象的维度

创建ndarray

data = []  #创建一个list对象
arr = np.array(data)  #传递一个list对象创建ndarray对象
np.zeros(10)  #创建长度为10的全0数组
np.ones(10)  #创建长度为10的全1数组
np.zeros((3,6))  #创建3行6列的全0二维数组
np.arrange(10)  #对应python内置的range函数  

数组创建函数

函数说明
array将输入数据(列表、元祖、数组或其他序列类型)转换为ndarray。
asarray将输入转换为ndarray,如果输入本身就是一个ndarray就不进行复制。
arrange类似于内置的range。
ones、ones_like根据指定的形状和dtype创建一个全1数组。ones_like以另一个数组为参数。
zeros、zeros_like同上
empty、empty_like创建新数组,只分配内存空间但不填充任何值。
eye、identity创建一个正方的NxN单位矩阵(对角线为1,其余为0)

ndarray的数据类型

arr1 = np.array([1, 2, 3], dtype=np.float64)
arr2 = np.array([1, 2, 3], dtype=np.int32)
float_arr1 = arr1.astype(np.float64)  #data.astype()显式转换数据类型
float_arr2 = arr2.astype(arr1.dtype)

调用astype无论如何都会常见一个新的数组(原始数组的一份拷贝),即使新dtype跟老dtype相同也是如此。

基本的索引和切片

一维数组跟Python列表的功能差不多,跟列表最重要的区别在于,数组切片是原始数组的视图,并非拷贝。如果要想得到的是ndarray切片的一个副本而非视图,就需要显式的进行复制操作,例如==arr[5:8].copy()==。

以下两种方式是等价的

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[0][2]
arr2d[0, 2]

切片索引

ndarray的切片语法跟Python列表这样的一维对象差不多。高维度对象的花样更多,可以在一个或多个轴上进行切片,也可以跟整数索引混合使用。

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

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

arr2d[:2]
Out: 
array([[1, 2, 3],
       [4, 5, 6]])

arr2d[:2,1:]
Out: 
array([[2, 3],
       [5, 6]])

布尔型索引

names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = randn(7,4)  //使用numpy.random中的randn函数生成一些正态分布的随机数据。
data
Out: 
array([[-0.29387507,  0.17205361,  0.66953008,  1.52117065],
       [ 1.10443341,  1.38657273, -0.07698117,  0.14266073],
       [ 0.76090409,  0.971822  , -0.32016532,  0.8410247 ],
       [-0.16898366,  0.18393705, -0.68519614,  1.22744236],
       [ 0.50100988, -0.75611675,  0.18750586, -1.64240784],
       [-0.3859573 ,  0.53440464, -0.74281885,  0.83665929],
       [-0.52089554,  0.65959834, -0.17651269, -1.58237464]])
names == 'Bob'
Out: array([ True, False, False,  True, False, False, False], dtype=bool)

data[names == 'Bob']
Out: 
array([[-0.29387507,  0.17205361,  0.66953008,  1.52117065],
       [-0.16898366,  0.18393705, -0.68519614,  1.22744236]])

布尔型数组的长度必须跟被索引的轴长度一致。还可以跟切片、整数(或整数序列)混合使用:

data[names == 'Bob',2:]
Out: 
array([[ 0.66953008,  1.52117065],
       [-0.68519614,  1.22744236]])

要选择“Bob”以外的其他值,既可以使用不等于号(!=),也可以通过负号(-)对条件进行否定:

data[names != 'Bob']
data[-(names == 'Bob')]

如果需要对布尔条件进行组合使用,可以使用&(和)、|(或)之类的布尔算数运算符即可:

mask = (names == 'Bob') | (names == 'Will')

mask
Out: array([ True, False,  True,  True,  True, False, False], dtype=bool)

data[mask]
Out: 
array([[-0.29387507,  0.17205361,  0.66953008,  1.52117065],
       [ 0.76090409,  0.971822  , -0.32016532,  0.8410247 ],
       [-0.16898366,  0.18393705, -0.68519614,  1.22744236],
       [ 0.50100988, -0.75611675,  0.18750586, -1.64240784]])

通过布尔型索引选取数组中的数据,总是创建数据的副本,即使返回一模一样的数组也是如此。
==Python关键字and和or在布尔型数组中无效。==

可以对数组中的一些符合条件的数据进行重新赋值:

data[data < 0] = 0

data
Out: 
array([[ 0.        ,  0.17205361,  0.66953008,  1.52117065],
       [ 1.10443341,  1.38657273,  0.        ,  0.14266073],
       [ 0.76090409,  0.971822  ,  0.        ,  0.8410247 ],
       [ 0.        ,  0.18393705,  0.        ,  1.22744236],
       [ 0.50100988,  0.        ,  0.18750586,  0.        ],
       [ 0.        ,  0.53440464,  0.        ,  0.83665929],
       [ 0.        ,  0.65959834,  0.        ,  0.        ]])

花式索引

即利用整数数组进行索引。

为了以特定的书序选取行子集,只需传入一个用于指定顺序的整数列表或ndarray即可:

arr = np.empty((8, 4))
for i in range(8):
    arr[i] = i
arr
Out: 
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.]])

arr[[4, 3, 0, 6]]
Out: 
array([[ 4.,  4.,  4.,  4.],
       [ 3.,  3.,  3.,  3.],
       [ 0.,  0.,  0.,  0.],
       [ 6.,  6.,  6.,  6.]])

一次传入多个索引数组返回的是一个以为数组,其中的元素对应各个索引元组:

arr = np.arange(32).reshape((8, 4))
arr
Out: 
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]]  #返回的是元素(1,0)、(5,3)、(7,1)和(2,2)。
Out: array([ 4, 23, 29, 10])

我们可以通过以下方式获取矩阵的行列子集:

arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
Out: 
array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])

还可以通过使用np.ix函数,它可以将两个一维整数数组转换为一个用于选取方形区域的所引器:

arr[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])]
Out: 
array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])

==花式索引总是将数据复制到新数组中。==

数组转置和轴对换

==转置是重塑的一种特殊形式,它返回的事源数据的视图(不会进行任何复制操作)==。数组不仅有transppose方法,还有一个特殊的T属性:

arr = np.arange(15).reshape((3, 5))
arr
Out: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

arr.T
Out: 
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])
       
arr = np.random.randn(6, 3)
np.dot(arr.T, arr)  #利用np.dot计算矩阵内积X.T*X

对于高维数组,transpose需要得到一个由轴编号组成的元组才能对这些轴进行转置(比较费脑子):

arr = np.arange(16).reshape((2, 2, 4))
arr
Out: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])

arr.transpose(1,0,2)
Out: 
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])

ndarray还有一个swapaxes方法,它接受一对轴编号:

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

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])

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

       [[ 8, 12],
        [ 9, 13],
        [10, 14],
        [11, 15]]])

通用函数:快速的元素级数组函数

一元ufunc

函数说明
abs、fabs计算整数、浮点数或复数的绝对值。对于复数数值,可以使用更快的fabs。
sqrt计算各元素的平方根
square计算各元素的平方
exp计算各元素的指数e^x
log、log10、log2、log1p分别为自然对数、底数为10的log、底数为2的log、log(1 + x)
sign计算各元素的正负号:1(正数)、0(零)、-1(负数)
ceil计算各元素的ceiling值
floor计算各元素的floor值
rint四舍五入到正数,保留dtype
modf将数组的小数和整数部分以两个独立数组的形式返回
isnan返回一个表示“哪些值是NaN”的布尔型数组
isfinite、isinf分别返回一个表示“哪些元素是有穷的”或“哪些元素是无穷的”的布尔型数组
cos、cosh、sin、sinh、tan、tanh普通型和双曲型三角函数
arccos、arccosh、arcsin、arcsinh、arctan、arctanh反三角函数
logical_not计算各元素not x的真值。想到与-arr

二元ufunc

函数说明
add将数组中对应的元素相加
subtract从第一个数组中减去第二个数组中的元素
multiply数组元素相乘
divide、floor_divide除法或向下圆整除法
power对第一个数组中的元素A,根据第二个数组中的相应元素B,计算A的B次方
maximum、fmax元素级的最大值计算。fmax忽略NaN
minmum、fmin元素级的最小值计算。fmin忽略NaN
mod元素级的求模计算
copysign将第二个数组中的值得负号复制到第一个数组中的值
greate、greate_equal、less、less_equal、equal、not_equal执行元素级的比价运算,最终产生布尔型的数组。相当于运算符>、>=、<、<=、==、!=
logical_and、logical_or、logical_xor执行元素级的真值逻辑运算。相当于中缀运算符&、、^

利用数组进行数据处理

将条件逻辑表述为数组运算

numpy.where 函数是三元表达式 x if condition else y 的矢量化版本。

xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result = [(x if c else y)
   .....: for x, y, c in zip(xarr, yarr, cond)]
result
Out: [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]

这样做会有许多问题。首先,对于大的数组,它不会很快(因为所有的工作都是有纯Python来做的)。其次,对于多维数组,它不能工作。使用 np.where 你可以像这样非常简洁的编写:

result = np.where(cond, xarr, yarr)
result
Out: array([ 1.1, 2.2, 1.3, 1.4, 2.5])

np.where 的第一个和第二个参数不需要是数组;它们中的一个或两个可以是纯量。 在数据分析中 where 的典型使用是生成一个新的数组,其值基于另一个数组。假如你有一个矩阵,其数据是随机生成的,你想要把其中的正值替换为2,负值替换为-2,使用 np.where 非常容易:

arr = randn(4, 4)
arr
Out:
array([[ 0.6372, 2.2043, 1.7904, 0.0752],
       [-1.5926, -1.1536, 0.4413, 0.3483],
       [-0.1798, 0.3299, 0.7827, -0.7585],
       [ 0.5857, 0.1619, 1.3583, -1.3865]])
np.where(arr > 0, 2, -2)
Out:
array([[ 2, 2, 2, 2],
       [-2, -2, 2, 2],
       [-2, 2, 2, -2],
       [ 2, 2, 2, -2]])

np.where(arr > 0, 2, arr) # 仅设置正值为 2
Out:
array([[ 2. , 2. , 2. , 2. ],
       [-1.5926, -1.1536, 2. , 2. ],
       [-0.1798, 2. , 2. , -0.7585],
       [ 2. , 2. , 2. , -1.3865]])

np.where 可以嵌套使用。

数学统计方法

一组数学函数,计算整个数组或一个轴向上数据的统计,和数组函数一样是容易访问的。聚合(通常被称为 reductions ),如 sum , mean ,标准偏差 std 可以使用数组实例的方法,也可以使用顶层NumPy的函数:

arr = randn(5,4)
arr
Out: 
array([[ 0.39013323, -0.65003199, -1.7659255 ,  0.50657869],
       [ 1.49064958, -2.12313076, -0.06437275, -1.74020972],
       [ 0.58393273, -2.54944833,  1.3207072 ,  0.4929906 ],
       [ 0.29181077,  0.24600015, -0.88524769,  0.1694354 ],
       [-1.29550423, -0.67156125,  0.04152137,  0.6270823 ]])

arr.mean()
Out: -0.27922950905899768

np.mean(arr)
Out: -0.27922950905899768

arr.sum()
Out: -5.5845901811799532

np.sum(arr)
Out: -5.5845901811799532

像 mean 和 sun 函数可以有一个可选的 axis参数,它对给定坐标轴进行统计,结果数组将会减少一个维度:

arr.mean(axis=1)
Out[84]: array([-0.37981139, -0.60926591, -0.03795445, -0.04450034, -0.32461545])
arr.sum(0)
Out[86]: array([ 1.46102209, -5.74817217, -1.35331736,  0.05587727])

像 cumsum 和 cumprod 这些函数并不聚集,而是产生一个中间结果组成的数组:

arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
arr.cumsum(axis=0)
Out: 
array([[ 0,  1,  2],
       [ 3,  5,  7],
       [ 9, 12, 15]])
arr.cumprod(axis=0)
Out: 
array([[ 0,  1,  2],
       [ 0,  4, 10],
       [ 0, 28, 80]])

数学统计方法清单

方法描述
sum对数组的所有或一个轴向上的元素求和。零长度的数组的和为灵。
mean算术平均值。灵长度的数组的均值为NaN。
std, var标准差和方差,有可选的调整自由度(默认值为n)。
min, max最大值和最小值
argmin, argmax索引最小和最大元素。
cumsum从0元素开始的累计和。
cumprod从1元素开始的累计乘。

用于布尔型数组的方法

在上面的方法中,布尔值被强制为1( True )和0a( False )。因此, sum 经常被用来作为对一个布尔数组中的 True 计数的手段:

arr = randn(100)
(arr > 0).sum() # 正值的个数
Out: 44

有两个额外的方法, any 和 all ,对布尔数组尤其有用。 any 用来测试一个数组中是否有一个或更多的 True ,而 all 用来测试所有的值是否为 True :

bools = np.array([False, False, True, False])
bools.any()
Out: True
bools.all()
Out: False

==这些方法这些方法也可以工作在非布尔型数组上,非零元素作为 True 。==

排序

arr.sort(axis=None)会就地排序修改数组本身,而np.sort(arr,axis=None)返回的是数组的已排序数组。计算数组分位数最简单的方法是对其进行排序,然后选取特定位置的值:

large_arr = randn(1000)
large_arr.sort()
large_arr[int(0.05 * len(large_arr))] # 5% 分位点
Out: -1.5791023260896004

唯一化以及其他的集合逻辑

Numpy有一些基本的针对一维ndarrays的集合操作。最常使用的一个可能是 np.unique ,它返回一个数组的经过排序的 unique 值:

names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.unique(names)
Out:
array(['Bob', 'Joe', 'Will'],
      dtype='|S4')
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
np.unique(ints)
Out: array([1, 2, 3, 4])

另一个函数 np.in1d 用于测试一个数组中的值在另一个数组中的成员资格,返回一个布尔型数组:

values = np.array([6, 0, 0, 3, 2, 5, 6])
np.in1d(values, [2, 3, 6])
Out: array([ True, False, False, True, True, False, True], dtype=bool)
函数说明
unique(x)计算x单一的元素,并对结果排序
intersect1d(x, y)计算x和y相同的元素,并对结果排序
union1d结合x和y的元素,并对结果排序
in1d(x, y)得到一个布尔数组指示x中的每个元素是否在y中
setdiff1d(x, y)差集,在x中但不再y中的集合
setxor1d(x, y)对称差集,不同时在两个数组中的元素

用于数组的文件输入输出

NumPy 能够保存数据到磁盘和从磁盘加载数据,不论数据是文本或二进制的。

将数组以二进制格式保存到磁盘

np.save 和 np.load 是两个主力功能,有效的保存和加载磁盘数据。数组默认保存为未经过压缩的原始二进制数据,文件扩展名为 .npy :

arr = np.arange(10)
np.save('some_array', arr)

如果文件路进并不是以 .npy 结尾,扩展名将会被自动加上。在磁盘上的数组可以使用 np.load 加载:

np.load('some_array.npy')
Out: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

可以使用 np.savez 并以关键字参数传递数组来保存多个数组到一个zip的归档文件中:

np.savez('array_archive.npz', a=arr, b=arr)

加载一个 .npz 文件时,会得到一个字典对象,该对象会对各个数组进行延时加载:

arch = np.load('array_archive.npz')
arch['b']
Out: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

存取文本文件

从文件加载文本是一个相当标准的任务。对一个新人来说,Python的文件加读取和写入函数的景象可能有一点儿混乱,因此我将主要集中在pandas的 read_csv 和 read_table 函数上。有时使用 np.loadtxt 或更专门的 np.genfromtxt 对于加载数据到普通的 NumPy 数组是很有用的。

# Windows下用type
!cat array_ex.txt
0.580052,0.186730,1.040717,1.134411
0.194163,-0.636917,-0.938659,0.124094
-0.126410,0.268607,-0.695724,0.047428
-1.484413,0.004176,-0.744203,0.005487
2.302869,0.200131,1.670238,-1.881090
-0.193230,1.047233,0.482803,0.960334

# 直接将文件加载到一个二维数组中
arr = np.loadtxt('array_ex.txt', delimiter=',')
arr
Out:
array([[ 0.5801, 0.1867, 1.0407, 1.1344],
[ 0.1942, -0.6369, -0.9387, 0.1241],
[-0.1264, 0.2686, -0.6957, 0.0474],
[-1.4844, 0.0042, -0.7442, 0.0055],
[ 2.3029, 0.2001, 1.6702, -1.8811],
[-0.1932, 1.0472, 0.4828, 0.9603]])

np.savatxt 执行相反的操作:将数组写到以某种分隔符隔开的文本文件中。 genfromtxt 与 loadtxt 相似,但是它是面向结构数组和缺失数据处理的。

线性代数

dot 函数,是数组的一个方法和 numpy 命名空间中的一个函数,用来进行矩阵乘法运算:

x = np.array([[1., 2., 3.], [4., 5., 6.]])
In [195]: y = np.array([[6., 23.], [-1, 7], [8, 9]])

x.dot(y) # 等价于 np.dot(x, y)
Out:
array([[ 28., 64.],
       [ 67., 181.]])

numpy.linalg 有一个关于矩阵分解和像转置和行列式等的一个标准集合。

from numpy.linalg import inv, qr

常见numpy.linalg

函数描述
diag返回一个方阵的对角线(或非对角线)元素为一个一维数组,或者转换一个一维数组到一个方阵(非对角线元素为零)
dot矩阵乘积
trace计算对角线上元素的和
det计算矩阵行列式
eig计算方阵的特征值和特征向量
inv计算方阵的逆
pinv计算方阵 Moore-Penrose 伪逆
qr计算 QR 分解
svd计算奇异值分解(SVD)
solve求解线性系统方程 Ax = b 的x,其中A是一个方阵
lstsq计算 y = Xb 的最小二乘解

随机数生成

numpy.random 模块对 Python 内置的 random 进行了补充,增加了一些用于高效生成多种概率分布的样本值得函数。

numpy.random 函数

函数说明
seed确定随机数生成数的种子
permutation返回一个序列的随机排列或返回一个随机排列的范围
shuffle对一个序列就地随机排列
rand产生均匀分布的样本值
randint从给定的上下限范围内(不包括上限)随机选取整数
randn产生标准正态分布的样本值
binomial产生二项分布的样本值
normal产生正态(高斯)分布的样本值
beta产生Beta分布的样本值
chisquare产生卡方分布的样本值
gamma产生Gamma分布的样本值
uniform产生在[0,1]中均匀分布的样本值

模拟随机漫步

多个随机漫步

nwalks = 5000
nsteps = 1000
draws = np.random.randint(0, 2, size=(nwalks, nsteps)) # 0 or 1
steps = np.where(draws > 0, 1, -1)
walks = steps.cumsum(1)
walks
Out:
array([[ 1, 0, 1, ..., 8, 7, 8],
       [ 1, 0, -1, ..., 34, 33, 32],
       [ 1, 0, -1, ..., 4, 5, 4],
       ...,
       [ 1, 2, 1, ..., 24, 25, 26],
       [ 1, 2, 3, ..., 14, 13, 14],
       [ -1, -2, -3, ..., -24, -23, -22]])

# 我们可以获得所有游走的最大和最小值     
walks.max() In [229]: walks.min()
Out: 138 Out[229]: -133

# 在这些游走中,让我们来计算到达30或-30的最短时间。这有一点儿狡猾,
# 因为不是所有的5000个游走都能到达30。我们可以使用 any 方法来检测
hits30 = (np.abs(walks) >= 30).any(1)
hits30
Out: array([False, True, False, ..., False, True, False], dtype=bool)
hits30.sum() # 30或-30的个数
Out: 3410

# 我们可以使用这个布尔数组来选择这些游走中跨过绝对值30的行,并调用 argmax 来取得坐标轴1的穿越时间:
crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1)
crossing_times.mean()
Out[234]: 498.88973607038122
    原文作者:昱灬岩
    原文地址: https://www.jianshu.com/p/2403e6dcf7a1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞