一、数组转置和换轴
转置是一种特殊的数据重组形式,可以返回底层数据的视图而不需要复制任何内容。数组拥有transpose方法,也有特殊的T属性:
import numpy as np
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]])
np.transpose(arr)
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
当进行矩阵计算时,你可能会经常进行一些特定操作,例如,当计算矩阵内积会使用np.dot:
arr = np.random.randn(6, 3)
arr
array([[ 0.51674907, 0.14972018, -0.19700823],
[-0.12662813, 0.72435835, 0.78049985],
[ 1.7046739 , -0.00777044, -0.6994555 ],
[ 0.76440228, -0.22231852, 0.0798294 ],
[-0.37986392, -0.45803112, -0.93404632],
[-0.92366358, -0.1957148 , -1.9926958 ]])
np.dot(arr.T, arr)
array([[ 4.77073923, 0.1572209 , 0.86343221],
[ 0.1572209 , 0.84469385, 1.34137535],
[ 0.86343221, 1.34137535, 5.98688207]])
对于更高维度的数组,transpose方法可以接收包含之后编号的元组,用于置换轴(拓展下思维):
arr = np.arange(16.).reshape((2, 2, 4))
arr
array([[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.]],
[[ 8., 9., 10., 11.],
[ 12., 13., 14., 15.]]])
arr.transpose((1, 0, 2))
array([[[ 0., 1., 2., 3.],
[ 8., 9., 10., 11.]],
[[ 4., 5., 6., 7.],
[ 12., 13., 14., 15.]]])
三个轴可以看成x,y,z轴,在这里,轴已经被重新排序,使得原先的y轴变成x,原先的x轴变成了y轴,z轴并没有改变。
使用.T进行转置是转轴的一个特殊案例。ndarray有一个swapaxes方法,该方法接收一对轴编号作为参数,并对轴进行调整用于重组数据:
arr
array([[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.]],
[[ 8., 9., 10., 11.],
[ 12., 13., 14., 15.]]])
arr.swapaxes(0, 1)
array([[[ 0., 1., 2., 3.],
[ 8., 9., 10., 11.]],
[[ 4., 5., 6., 7.],
[ 12., 13., 14., 15.]]])
arr.swapaxes(1, 2)
array([[[ 0., 4.],
[ 1., 5.],
[ 2., 6.],
[ 3., 7.]],
[[ 8., 12.],
[ 9., 13.],
[ 10., 14.],
[ 11., 15.]]])
二、通用函数:快速的逐元素数组函数
通用函数,也称为ufunc,是一种在ndarray数据中进行逐元素操作的函数。某些简单函数接受一个或多个标量数值,并产生一个或多个标量结果,而通用函数就是对这些简单函数的向量化封装。
有很多ufunc是简单的逐元素转换,比如sqrt或exp函数:
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.sqrt(arr)
array([ 0. , 1. , 1.41421356, 1.73205081, 2. ,
2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.exp(arr)
array([ 1.00000000e+00, 2.71828183e+00, 7.38905610e+00,
2.00855369e+01, 5.45981500e+01, 1.48413159e+02,
4.03428793e+02, 1.09663316e+03, 2.98095799e+03,
8.10308393e+03])
这些都是所谓的一元通用函数。还有一些通用函数,比如add或maxiumu则会接受两个数组并返回一个数组作为结果,因此称为二元通用函数:
x = np.random.randn(8)
y = np.random.randn(8)
x
array([ 0.25505513, 0.23616009, -0.83815435, 0.08750238, 1.44660586,
-0.49278317, 0.01925976, 0.36114295])
y
array([-0.77039858, 0.5399858 , -0.10889914, 0.06090177, -0.85935897,
0.72074658, -1.1126588 , -1.4409482 ])
np.maximum(x, y)
array([ 0.25505513, 0.5399858 , -0.10889914, 0.08750238, 1.44660586,
0.72074658, 0.01925976, 0.36114295])
这里maximum逐个元素地将x和y中元素的最大值计算出来
也有一些通用函数返回多个数组。比如modf,是python内建函数divnod的向量化版本,它返回了一个浮点值数组的小数部分和整数部分:
arr = np.random.randn(7) * 5
arr
array([ 7.71099483, -0.88693267, -1.70675617, -1.52965618, 4.30903282,
-2.30624022, 4.28942247])
remainder, whole_part = np.modf(arr)
remainder
array([ 0.71099483, -0.88693267, -0.70675617, -0.52965618, 0.30903282,
-0.30624022, 0.28942247])
whole_part
array([ 7., -0., -1., -1., 4., -2., 4.])
通用函数接收一个可选参数out,允许对数组按位置操作:
arr
array([ 7.71099483, -0.88693267, -1.70675617, -1.52965618, 4.30903282,
-2.30624022, 4.28942247])
np.sqrt(arr)
/home/cxd/.conda/envs/python36/lib/python3.6/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in sqrt
"""Entry point for launching an IPython kernel.
array([ 2.77686781, nan, nan, nan, 2.075821 ,
nan, 2.0710921 ])
arr
array([ 7.71099483, -0.88693267, -1.70675617, -1.52965618, 4.30903282,
-2.30624022, 4.28942247])
np.sqrt(arr, arr)
/home/cxd/.conda/envs/python36/lib/python3.6/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in sqrt
"""Entry point for launching an IPython kernel.
array([ 2.77686781, nan, nan, nan, 2.075821 ,
nan, 2.0710921 ])
arr
array([ 2.77686781, nan, nan, nan, 2.075821 ,
nan, 2.0710921 ])
下表是常用的一元通用函数:
函数名 | 描述 |
---|---|
abs、fabs | 逐元素地计算整数、浮点数或复数的绝对值 |
sqrt | 计算每个元素的平方根(与arr ** 0.5相等) |
square | 计算每个元素的平方(与arr ** 2 相等) |
exp | 计算每个元素的自然指数 |
log、log10、log2、log1p | 分别对应:自然对数、对数10为底、对数2为底、log(1+x) |
sign | 计算每个元素的符号值:1(正数)、0(0)、\1(负数)即符号函数 |
ceil | 计算每个元素的最高整数值(即大于等于给定熟知的最小整数) |
floor | 计算每个元素的最小正数值 |
rint | 将元素保留到整数位,并保持dtype |
modf | 分别将数组的小数部分和正数部分按数组形式返回 |
isnan | 返回数组中的元素是否是一个NaN(不是一个数值)、形式为布尔值数组 |
isfinite、isinf | 分别返回数组中的元素是否有限(非inf、非NaN)、是否无限的,形式为布尔值数组 |
cos、cosh、sin、sinh、tan、tanh、arccos、arccosh、arcsin、arcsinh、arctan、arctanh | 三角函数反三角函数 |
logical_not | 对数组的元素按位取反(与-arr效果一致) |
二元通用函数:
函数名 | 描述 |
---|---|
np.add(ndarray, ndarray) | 相加 |
np.subtract(ndarray, ndarray) | 相减 |
np.multiply(ndarray, ndarray) | 相乘 |
np.divide(ndarray, ndarray) | 相除 |
np.floor_divide(ndarray, ndarray) | 地板除 |
np.power(ndarray, ndarray) | 次方 |
np.mod(ndarray, ndarray) | 取模 |
np.maximum(ndarray, ndarray) | 求最大值 |
np.fmax(ndarray, ndarray) | 求最大值(忽略NaN) |
np.minimun(ndarray, ndarray) | 求最小值 |
np.fmin(ndarray, ndarray) | 求最小值(忽略NaN) |
np.copysign(ndarray, ndarray) | 将参数2中的符号赋予参数1 |
np.greater(ndarray, ndarray) | > |
np.greater_equal(ndarray, ndarray) | >= |
np.less(ndarray, ndarray) | < |
np.less_equal(ndarray, ndarray) | <= |
np.equal(ndarray, ndarray) | == |
np.not_equal(ndarray, ndarray) | != |
logical_and(ndarray, ndarray) | & |
logical_or(ndarray, ndarray) | $ |
logical_xor(ndarray, ndarray) | ^ |
np.dot( ndarray, ndarray) | 计算两个ndarray的矩阵内积 |
np.ix_([x,y,m,n],…) | 生成一个索引器,用于Fancy indexing(花式索引) |
arr1 = np.random.randn(2, 3)
arr2 = np.random.randn(2, 3)
print(arr1)
print(arr2)
np.mod(arr1, arr2)
[[-0.6468063 1.77175494 1.48766826]
[-0.05857401 -0.14579956 -0.39975639]]
[[-0.85607957 -0.17774132 1.37001464]
[-1.10156641 0.51484269 0.1242562 ]]
array([[-0.6468063 , -0.00565824, 0.11765362],
[-0.05857401, 0.36904313, 0.09726842]])
三、使用数组进行面向数组编程
作为一个简单的示例,假设我们想要对一些网格数据来计算函数sqrt(x^2 + y^2)的值。np.meshgrid函数接收两个一位数组,并根据两个数组的所有(x, y)对生成一个二维矩阵:
points = np.arange(-5, 5, 0.01)
xs, ys = np.meshgrid(points, points)
ys
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
[-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
[-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
...,
[ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
[ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
[ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]])
xs
array([[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
...,
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99]])
z = np.sqrt(xs ** 2 + ys ** 2)
z
array([[ 7.07106781, 7.06400028, 7.05693985, ..., 7.04988652,
7.05693985, 7.06400028],
[ 7.06400028, 7.05692568, 7.04985815, ..., 7.04279774,
7.04985815, 7.05692568],
[ 7.05693985, 7.04985815, 7.04278354, ..., 7.03571603,
7.04278354, 7.04985815],
...,
[ 7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 ,
7.03571603, 7.04279774],
[ 7.05693985, 7.04985815, 7.04278354, ..., 7.03571603,
7.04278354, 7.04985815],
[ 7.06400028, 7.05692568, 7.04985815, ..., 7.04279774,
7.04985815, 7.05692568]])
我们使用matplotlib来生成这个二维数组的可视化:
import matplotlib.pyplot as plt
plt.imshow(z, cmap=plt.cm.gray);plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x7fbbfe751b00>
plt.title('image plot of $\sqrt{x^2 + y^2}$ for a grid of values')
Text(0.5, 1.0, 'image plot of $\\sqrt{x^2 + y^2}$ for a grid of values')
plt.show()
四、将条件逻辑作为数组操作
np.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
[1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]
这样会产生很多问题。首先,如果数组很大的话,速度会很慢。其次,当数组是多维时,就无法奏效了。而使用np.where时,就可以非常简单地完成:
result = np.where(cond, xarr, yarr)
result
array([ 1.1, 2.2, 1.3, 1.4, 2.5])
np.where第二个和第三个参数并不需要是数组,它们可以是标量:
arr = np.random.randn(4, 4)
arr
array([[ 0.0462423 , 1.39587524, 0.7302261 , 0.21081567],
[-0.18468851, 0.94854582, 0.70048342, 1.17255176],
[ 0.72976014, 0.16594239, -1.32911281, 1.61769938],
[ 0.98796864, -0.76306741, 0.79224391, -1.05967434]])
arr > 0
array([[ True, True, True, True],
[False, True, True, True],
[ True, True, False, True],
[ True, False, True, False]], dtype=bool)
np.where(arr > 0, 2, -2)
array([[ 2, 2, 2, 2],
[-2, 2, 2, 2],
[ 2, 2, -2, 2],
[ 2, -2, 2, -2]])
np.where(arr > 0, 2, arr)
array([[ 2. , 2. , 2. , 2. ],
[-0.18468851, 2. , 2. , 2. ],
[ 2. , 2. , -1.32911281, 2. ],
[ 2. , -0.76306741, 2. , -1.05967434]])