Numpy可以高效处理大数组的数据,因为:
- Numpy在一个连续的内存块中存储数据,独立于其他Python内置对象。Numpy是C写的,有优化,比Python内置序列使用的内存少
- 可以在整个数组上进行复杂运算,不需要for循环
下面的代码可以看出,同样的数据量,numpy比原生list快几十倍。
import numpy as np
import time
my_arr = np.arange(1000000)
my_list = list(range(1000000))
start = time.time()
for _ in range(10):
my_arr = my_arr * 2
end = time.time()
print(end-start) # 0.014997482299804688
start = time.time()
for _ in range(10):
my_list = [x * 2 for x in my_list]
end = time.time()
print(end-start) # 0.7048995494842529
ndarray
ndarray = n + d(dimension) + array,即N维数组。这个数组里所有元素都是同一类型的,用.dtype可以看到类型。
>>> import numpy as np
>>> data = np.random.randn(2, 3)
>>> print(data)
[[ 0.70162402 0.00711845 0.83315757]
[-1.1485004 1.19954043 -0.26478855]]
>>> data.shape
(2, 3)
>>> data.dtype
dtype('float64')
print(data * 10) # 每个元素都x10
print(data + data) # 对应位置相加
创建ndarray
# 使用list创建。如果是嵌套的,说明是多维数据
>>> data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
>>> arr2 = np.array(data2)
# 也可以用np.asarray(data2),一样的效果
>>> arr2
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
>>> arr2.ndim
2
>>> arr2.shape
(2, 4)
>>> arr2.dtype
dtype('int32')
# 创建全0数组。创建时需要输入每个维度上的size,多维度的时候要用元组
>>> np.zeros(5)
array([0., 0., 0., 0., 0.])
# 仿照对象的维度创造全0数组
np.zeros_like(对象)
# 创建全1数组
>>> np.ones((2,3))
array([[1., 1., 1.],
[1., 1., 1.]])
# 仿照对象的维度创造全1数组
>>> np.ones_like(data2)
array([[1, 1, 1, 1],
[1, 1, 1, 1]])
# empty创建的是没有值的数组,只是分配空间。empty_like()同理
>>> np.empty((2, 3, 1))
array([[[1.],
[1.],
[1.]],
[[1.],
[1.],
[1.]]])
# 使用指定值填充所有位置。第一个参数是维度,第二个是指定值。full_like同理
>>> np.full((2,3),4)
array([[4, 4, 4],
[4, 4, 4]])
>>> np.full_like(data2, 4)
array([[4, 4, 4, 4],
[4, 4, 4, 4]])
# eye和identity都是创造NxN单位矩阵(对角线是1,其余是0)
>>> np.eye(3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
>>> np.identity(3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
# np.arange是range的np版,但是返回的是一个ndarray
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 使用astype可以修改数据类型
>>> arr2.astype(np.float32)
array([[1., 2., 3., 4.],
[5., 6., 7., 8.]], dtype=float32)
数组运算
大小相等的ndarray之间的任何算术运算都是应用到元素级的,即一一对应操作。比如两个ndarray加减乘除和比较(对应为位置返回True/False)
ndarray与标量之间的算术运算也是元素级的,比如1/arr就是每个元素被1除,arr ** 0.5就是每个元素开方。
通用函数ufunc
就是对ndarray中数据执行元素级运算的函数
ufunc有一个可选参数out,所以可以将out写为ndarray的名字,以实现原地操作。比如 np.sqrt(arr, arr)
np.modf会返回浮点数组的小数和整数部分
>>> arr = np.random.randn(7) * 5
>>> arr
array([ 2.89470277, -7.35643986, 8.69725165, 11.98482889, -6.53072422,
-3.27780558, 4.71749496])
>>> remainder, whole_part = np.modf(arr)
>>> remainder
array([ 0.89470277, -0.35643986, 0.69725165, 0.98482889, -0.53072422,
-0.27780558, 0.71749496])
>>> whole_part
array([ 2., -7., 8., 11., -6., -3., 4.])
abs/fabs:计算绝对值。对于非复数,使用fabs更快
square/sqrt:平方与开方。开方的时候复数对象会产出nan,报warning但继续执行
exp、log、log10、log2、log1p:最后一个是log(1+x),log是以e为底
sign:计算每个元素的符号,1,0,-1
ceil/floor
rint:四舍五入到最近的整数
isnan:判断是不是nan,返回bool数组,同理isfinite isinf
cos、cosh、sin、sinh、tan、tanh、arccos、arccosh、arcsin、arcsinh、arctan、arctanh
二元:
add、subtract、multiply、divide、floor_divide、power、maximum、fmax(取最大值,但是忽略NaN)、minimum、fmin(取最小只,忽略NaN)、mod、copysign(将第二个数组的符号复制给第一个数组)、greater/greater_equal/less/less_equal/equal/not_equal、logical_and、logical_or、logical_xor
索引与切片
index和切片跟原生List类似,不同的是,切片是“引用”,修改切片会影响到原来的值。如果切片时需要得到副本,应该用arr[5:8].copy()。
默认是引用是因为复制需要消耗内存,而Numpy经常要处理大量的数据,显然复制的消耗太大了。
>>> arr = np.arange(10)
>>> arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> arr[5:8]
array([5, 6, 7])
# 使用单一值赋值切片时会“广播”
>>> arr[5:8] = 12
>>> arr
array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
# 再取一个切片
>>> d = arr[0:1]
>>> d
array([0])
# 对切片中某个位置赋值
>>> d[0] = -1
# 直接影响原数组,说明是引用
>>> arr
array([-1, 1, 2, 3, 4, 12, 12, 12, 8, 9])
# 二维数组
>>> arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> arr2d[0][2]
3
>>> arr2d[0,2]
3
# 可以一次传入多个切片
>>> arr2d[:2, 1:]
array([[2, 3],
[5, 6]])
# 如果是用列表切片,表示切特定行
>>> arr2d[[0,2]]
array([[1, 2, 3],
[7, 8, 9]])
# 如果传入多个列表,就按维度切:
>>> arr2d[[0,2], [0,1]]
array([1, 8])
布尔型切片
# bool切片,除了==还有!=,也可以用 ~(names == 'Bob')
>>> names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
>>> names == 'Bob'
array([ True, False, False, True, False, False, False])
>>> data
array([[ 0.90597376, 0.48901929, -1.29117936, -0.54704399],
[ 1.3251687 , -0.97710089, 1.18568268, 0.58076217],
[ 0.38585117, 0.51111432, -0.36119234, -0.55018491],
[ 1.23013959, -0.60359478, -0.84073159, -1.03601213],
[-0.60764054, -0.17980566, -0.10172273, 0.26845593],
[ 0.4917044 , -0.2176951 , 0.63413077, 0.17411876],
[-0.29955537, 0.89073944, 0.27782948, 0.85028906]])
# 可以看到是选取了为True的行作为索引
>>> data[names=='Bob', 2:]
array([[-1.29117936, -0.54704399],
[-0.84073159, -1.03601213]])
# 可以起个名字。。
>>> cond = names == 'Bob'
>>> data[~cond]
array([[ 1.3251687 , -0.97710089, 1.18568268, 0.58076217],
[ 0.38585117, 0.51111432, -0.36119234, -0.55018491],
[-0.60764054, -0.17980566, -0.10172273, 0.26845593],
[ 0.4917044 , -0.2176951 , 0.63413077, 0.17411876],
[-0.29955537, 0.89073944, 0.27782948, 0.85028906]])
# ~cond切的行,1再切列
>>> data[~cond, 1]
array([-0.97710089, 0.51111432, -0.17980566, -0.2176951 , 0.89073944])
# 组合条件可以使用& |, 不能用and or
>>> mask = (names == 'Bob') | (names == 'Will')
# 将data中所有负值都设为0
>>> data[data < 0] = 0
>>> data
array([[0.90597376, 0.48901929, 0. , 0. ],
[1.3251687 , 0. , 1.18568268, 0.58076217],
[0.38585117, 0.51111432, 0. , 0. ],
[1.23013959, 0. , 0. , 0. ],
[0. , 0. , 0. , 0.26845593],
[0.4917044 , 0. , 0.63413077, 0.17411876],
[0. , 0.89073944, 0.27782948, 0.85028906]])
数组转置和轴对换
二维数组的转置用arr.T,产生新的拷贝
>>> 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]])
# 或者用transpose,需要输入一个由轴编号组成的元组
# 下面表示把0轴和1轴调换,跟.T的效果一样
arr.transpose(1,0)
arr.transpose((1,0))
还可以用.swaoaxes()方法,需要输入一对轴编号。也是返回视图/引用,不会进行复制。
绘制函数图像
>>> pts = np.arange(-5,5,0.01)
>>> xs, ys = np.meshgrid(pts, pts)
>>> 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)
>>> import matplotlib.pyplot as plt
>>> plt.imshow(z);plt.colorbar()
<matplotlib.image.AxesImage object at 0x000001DF26F272B0>
<matplotlib.colorbar.Colorbar object at 0x000001DF2A6D8F40>
>>> plt.show()
数组运算(列表推导)
np.where(条件bool数组,正确时的取值,else时的取值)。后两个参数不一定是数组,也可以是标量值
>>> 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])
>>> np.where(cond, xarr, yarr)
array([1.1, 2.2, 1.3, 1.4, 2.5])
将数组中大于0的值替换为2,否则为-2:
np.where(arr > 0, 2, -2)
而且,替换值也可以是ndarray,比如如果 np.where(arr > 0, 2, arr)表示,如果某个位置的数值<=0,那么该位置的值就保持不变。
统计
需要注意的是,axis=1计算的是行,axis=0计算的是列
>>> arr = np.random.randn(5, 4)
>>> arr
array([[-0.84075089, -0.75502507, 1.28231228, 0.64605601],
[-1.0410616 , -0.98069774, -0.44841819, 0.33743376],
[-0.20403139, -1.73880457, -1.79108436, 1.47435236],
[-0.36401451, 0.73726505, -0.8924274 , -2.01459786],
[ 0.26463865, 0.57256931, 0.37323712, 0.91512178]])
# 计算均值
>>> arr.mean()
-0.22339636293037574
>>> np.mean(arr)
-0.22339636293037574
# 计算总和
>>> arr.sum()
-4.4679272586075145
# 注意,axis=1是计算行的,axis=0是计算列的
>>> arr.mean(axis = 1)
array([ 0.08314808, -0.53318594, -0.56489199, -0.63344368, 0.53139172])
>>> arr.sum(axis=1)
array([ 0.33259233, -2.13274377, -2.25956796, -2.53377473, 2.12556687])
>>> arr.sum(axis=0)
array([-2.18521974, -2.16469302, -1.47638054, 1.35836604])
>>> arr = np.array([0,1,2,3,4,5,6,7])
# 计算累加
>>> arr.cumsum()
array([ 0, 1, 3, 6, 10, 15, 21, 28])
>>> arr = np.arange(9).reshape(3,3)
>>> arr
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
# 列向的累加(竖)
>>> arr.cumsum(axis=0)
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]])
# 累乘
>>> arr.cumprod(axis=0)
array([[ 0, 1, 2],
[ 0, 4, 10],
[ 0, 28, 80]])
std、var、min、max、argmin(最小元素的索引)、argmax(最大元素的索引)
.any()检查是否存在True或者非0元素,.all()检查是否所有都是True或者所有都非0。
bool的True会被当成1,所以可以用sum计算True的个数:
>>> bools = np.array([False, False, True, False, True])
>>> bools.sum()
2
排序
arr.sort()是就地的,默认升序。多维的时候默认是按行,即1,如果按列用0。
np.sort()返回的是副本
>>> arr = np.random.randn(5, 3)
>>> arr
array([[ 0.05913053, 2.46861144, -2.08584111],
[ 0.69658644, -0.5621056 , -0.12076698],
[ 0.23630043, -2.56341922, -0.55533511],
[-0.51205824, -0.17731922, -0.27384838],
[ 1.22365087, 0.24846602, -1.20284707]])
>>> arr.sort(1)
>>> arr
array([[-2.08584111, 0.05913053, 2.46861144],
[-0.5621056 , -0.12076698, 0.69658644],
[-2.56341922, -0.55533511, 0.23630043],
[-0.51205824, -0.27384838, -0.17731922],
[-1.20284707, 0.24846602, 1.22365087]])
>>> arr.sort()
>>> arr
array([[-2.08584111, 0.05913053, 2.46861144],
[-0.5621056 , -0.12076698, 0.69658644],
[-2.56341922, -0.55533511, 0.23630043],
[-0.51205824, -0.27384838, -0.17731922],
[-1.20284707, 0.24846602, 1.22365087]])
>>> arr.sort(0)
>>> arr
array([[-2.56341922, -0.55533511, -0.17731922],
[-2.08584111, -0.27384838, 0.23630043],
[-1.20284707, -0.12076698, 0.69658644],
[-0.5621056 , 0.05913053, 1.22365087],
[-0.51205824, 0.24846602, 2.46861144]])
集合
unique(x):类似set,返回结果是有序的
intersect1d(x, y):计算公共元素,返回有序结果
union1d(x, y):并集,有序
in1d(x, y):计算x中每个元素是否属于y
setdiff1d(x, y):x - y
setxor1d(x, y):xor
数据的导入与保存
使用save时,默认情况下,数组是以未压缩的原始二进制格式保存在扩展名为.npy的文件中的。
np.save('xxx', arr)
arr = np.load('xxx.npy')
# 保存多个数组
np.savez('array_archive.npz', a=arr, b=arr)
arch = np.load('array_archive.npz')
b = arch['b']
# 如果希望压缩数据,则
np.savez_compressed('arrays_compressed.npz', a=arr, b=arr)
线性代数
# 矩阵乘法,也可以 x @ y
np.dot(arr.T, arr)
numpy.linalg是线性代数库,跟matlab、R用的是一样的
diag 以1d数组的形式返回方阵的对角线元素,或将1d数组转换为方阵(非对角线为0)
trace 计算对角线元素的和
det 计算矩阵行列式
eig 计算方阵的本征值和本征向量
inv 计算方阵的逆
pinv 计算矩阵的Moore-Penrose伪逆
qr 计算QR分解
svd 计算奇异值分解
solve 解Ax=b,其中A是方阵
lstsq 计算Ax=b的最小二乘解
伪随机数生成
# 设置全局的随机种子
np.random.seed(123)
# 如果想用单独的随机种子,可以创建单独的生成器:
rng = np.random.RandomState(1234)
rng.randn(10)
# 标准正态分布,4x4
samples = np.random.normal(size=(4, 4))
# 等价于: 但是np的会更快
from random import normalvariate
samples = [normalvariate(0, 1) for _ in range(100)]
permutation 返回一个随机排列
shuffle 就地随机排列
rand 产生均匀分布的样本
randint 从给定的范围内随机选取整数
randn 正态分布,mean=0 std=1
beta/chisquare/gamma/uniform/normal/binomial 几种分布