NumPy
numpy是python科学计算的基础包,是一个python库,提供多维数组对象,各自派生对象(如掩码数组和矩阵),及用于数组快速操作的各种API,有包括数字,逻辑,形状操作,排序,选择,输入输出,离散傅里叶变换,基本线性代数,基本统计运算和随机模拟等等。
import numpy as np
# 生成一个对角矩阵
np.eye(4)
# array([[1., 0., 0., 0.],
# [0., 1., 0., 0.],
# [0., 0., 1., 0.],
# [0., 0., 0., 1.]])
numpy数组相加:两个数组相对应的位置相加,我称为一维数组相加。
import numpy as np
a = np.array([1,2,3,4])
b = np.array([4,5,6,7])
print(a+b)
# array([ 5, 7, 9, 11])
numpy对传入的数据类型是有强管制的。
- 基础操作
import numpy as np
a = np.array(['a','s','d','f'])
b = np.array([1,2,3,4])
c = np.array([4,5,6,7])
d = np.eye(4)
读取,修改
# 读取一维数组指定值
a[1]
# output
's'
#读取二维数组指定值
d[1,1]
# output
1.0
# 读取一行
d[1]
#output
array([0., 1., 0., 0.])
# 读取一列(之所以不用d[,2]是因为numpy是按行存储的)
d[:,2]
#output
array([0., 0., 1., 0.])
#修改二维数组
d[2,3] = 5
# output
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 5.],
[0., 0., 0., 1.]])
-------------------------------------------------
加减乘除
b + c
#output
array([ 5, 7, 9, 11])
b - c
#output
array([-3, -3, -3, -3])
b * c
#output
array([ 4, 10, 18, 28])
b / c
#output
array([0.25 , 0.4 , 0.5 , 0.57142857])
b ** c
#output
array([ 1, 32, 729, 16384], dtype=int32)
shape(各维度长度)
shape返回一个元组,列出每个维度的数组长度
a.shape
#output
(1,)
d.shape
#output
(4, 4)
ndim(维度)
输出数值,表示数组的维度。
a.ndim
#output
1
d.ndim
#output
2
dtype(类型)
通过dtype属性查看numpy数组的类型:float64, int32,<U2
a.dtype
#output
dtype('<U2')
d.dtype
#output
dtype('int32')
指定数据类型dtype
在创建数组的时候,可以指定dtype
arr = np.array([1, 2.2, 3, 4.9],dtype = 'int32')
# output
array([1, 2, 3, 4])
# 如果遇到无法转换,则会报错
arr = np.array([1. , 2.2, 3. , 'a'],dtype = 'int32')
ValueError: invalid literal for int() with base 10: 'a'
修改数据dtype类型:astype()
numpy数据类型转换需要调用方法 astype()
,不能直接修改 dtype
。调用 astype
返回数据类型修改后的数据,但是源数据的类型不会变。
arr = np.array([1 , 2.2, 3, 4.9])
a = arr.astype(int)
# output
array([1, 2, 3, 4])
a = arr.astype(np.int64)
# output
array([1, 2, 3, 4], dtype=int64)
a = arr.astype(np.str)
# output
array(['1.0', '2.2', '3.0', '4.9'], dtype='<U32')
------------------------------------
直接修改dtype则不会转换,而是直接切分,导致数据出错
arr = np.array([1 , 2.2, 3, 4.9])
arr.dtype
#oupput
dtype('float64')
#将原来每个64bits的浮点数 硬生生切成32bits的整型
arr.dtype = np.int32
#output
array([ 0, 1072693248, -1717986918, 1073846681, 0,
1074266112, -1717986918, 1075026329])
itemsize(最大元素的字节数)
a.itemsize
#output
4
b.itemsize
#output
4
nbytes(总元素字节数)
a.nbytes
# output
16
b.nbytes
# output
16
fill(填充)
指定字符元素填充数组
a.fill('a')
#output
array(['a', 'a', 'a', 'a'], dtype='<U1')
reshape(重塑)
在不改变原数据的情况下,重新按指定形状生成数组, 并且返回该数组b=a.shape(3, 2, 3)的意思是:第一个3,再三维数组里面分成三个二维数组,
第二个2,二维数组里面分两行,
第三个3,二维数组里分三列(或者一维数组有3个元素)。
>>> a = np.arange(1,26)
>>> a
array([ 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])
# 元素不匹配时,则会报错
>>> a.reshape(5,6)
ValueError: cannot reshape array of size 25 into shape (5,6)
>>> a.reshape(5,5)
array([[ 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]])
sum
- axis
a = np.arange(12).reshape(2, 3, 2)
print(a)
a_0 = a.sum(axis=0) # 数组内的两个二维数组对应位置相加。
a_1 = a.sum(axis=1) # 数组内的二维数组自相加,也就是二维数组内的一维数组相加。
a_2 = a.sum(axis=2) # 数组内的二维数组内的一维数组元素相加的值作为一个一维数组的值。
print('-'*30)
print(a_0)
print('-'*30)
print(a_1)
print('-'*30)
print(a_2)
#output
[[[ 0 1]
[ 2 3]
[ 4 5]]
[[ 6 7]
[ 8 9]
[10 11]]]
------------------------------
[[ 6 8]
[10 12]
[14 16]]
------------------------------
[[ 6 9]
[24 27]]
------------------------------
[[ 1 5 9]
[13 17 21]]
- keepdims
keepdims是用来设置sum函数计算后的结果维度是否保持不变。
b = a.sum(axis = 2,keepdims = True)
# output
array([[[ 1],
[ 5],
[ 9]],
[[13],
[17],
[21]]])
b.shape
# output
(2, 3, 1)
- initinal
通过initinal设置初始值
a.sum()
# output
66
a.sum(initial = 10)
# output
76
其他相似函数
函数名 | 含义 |
---|---|
prod | 返回数组的或给定轴上数组元素的乘积。(空数组的乘积为1) |
min/max | 返回数组的最小(大)值或给定轴的最小(大)值。 |
argmin/argmin | 返回沿轴的最小(大)值的索引。(无initial和keepdims) |
max | 返回数组的最大值或给定轴的最大值。 |
ptp | 沿轴的值范围(max-min, 无initial) |
mean | 返回数组的均值或给定轴的均值。 |
std/var | 计算沿指定轴的标准偏差(方差)。(其中自由度ddof=0为总体标准差(总体方差),ddof=1为样本标准差(样本方差)。无initial) |
any/all | 是元组元素只要有一个为真才为真,all是所有元素为真才为真。(无initial) |
flags(返回内存信息)
flags
返回 ndarray
对象的内存信息,包含以下属性:
属性 | 描述 |
---|---|
C_CONTIGUOUS | 数据在一个单一的C风格的连续字段中 |
F_CONTIGUOUS | 数据在一个单一的Fortran风格的连续字段中 |
OWNDATA | 数据拥有它所使用的内存(True)或从另一个对象中借用它(False) |
WRITEABLE | 数据区域可以被写入(True),设置为False时为只读。 |
ALIGNED | 数据和所有元素都适当对其到硬件上(True为对齐) |
WRITEBACKIFCOPY | UPDATEIFCOPY 已弃用,由 WRITEBACKIFCOPY 取代; |
UPDATEIFCOPY | 这个数组是其它数组的一个副本,当这个数组被释放时,原数组的内容将被更新 |
C_CONTIGUOUS和F_CONTIGUOUS
C_CONTIGUOUS
和 F_CONTIGUOUS
分别表示C风格和Fortran风格。这是两种不同的编程语言,在C语言中数组的存储风格是按行存储,而在Fortran语言中则是按列存储。
- 一维数组
对于一维数组,不管按行存储还是按列存储内存中的顺序都是不变的, 因此既符合 C_CONTIGUOUS
又符合 F_CONTIGUOUS
。数组 a
的存储方式如下图所示:
a = np.array([1,2,3,4])
a.flags
# output
C_CONTIGUOUS : True
F_CONTIGUOUS : True
- 二维数组
当然,对于二维数组就不一样了,我们可以看到二维数组b在内存中是按C风格(行优先)存储的。所以二维数组符合C_CONTIGUOUS不符合F_CONTIGUOUS。内存中的存储方式如下图左,现在我们将 np.transpose(b)
赋给c 。如果为c单独开辟一段内存是不划算的,numpy做的是给你一个相同内存缓冲区的一个视图,尽量不创建一个副本。对于c来说, c的C风格相当于是b的Fortran风格 。因此只需要在b上创建一个视图,并标为Fortran风格。便可作为c。
b = np.array([[1,2,3,4],[2,5,1,4]])
# output
C_CONTIGUOUS : True
F_CONTIGUOUS : False
···
c = b.T
# output
In [7]: c.flags
Out[7]:
C_CONTIGUOUS : False
F_CONTIGUOUS : True
OWNDATA
OWNDATA判断变量上的数据是否拥有所属内存。 因为 b
是直接创建的,因此 b
上的数据拥有所属的内存,而 c
相当于是 b
上的一个视图,因此数据没有所属的内存
b.flags
# outpit
···
OWNDATA : True
···
c.flags
# output
OWNDATA : False
类x数组
函数 | 解释 |
---|---|
empty_like | 返回 形状和类型与给定数组相同 的新数组。(值为内存中的 原垃圾值 ) |
ones_like | 返回 形状与类型与给定数组相同 的数组。( 1 填充) |
zeros_like | 返回 形状与类型与给定数组相同 的数组。( 0 填充) |
a = np.range(36).reshape(6,6)
np.empty_like(a)
np.ones_like(a)
np.zeros_like(a)
# output
array([[ 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0],
[-2, 0, -2, -1, 0, 0]])
array([[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1]])
array([[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]])
where
where(条件,[x,y])
返回根据条件从x或y中选择的元素。
a = np.array([[1,2],[3,4]])
b = np.array([[9,8],[7,6]])
mask = np.array([[1,0],[0,1]],dtype = bool)
np.where(mask,a,b)
# output
array([[1, 8],
[7, 4]])
- 索引
现给出两个数组,下面所有操作均在其上。
>>> import numpy as np
>>>#负索引 -6 -5 -4 -3 -2 -1
···#正索引 0, 1, 2, 3, 4, 5
··· a = np.numpy([10, 5, 7, 9, 8, 4])
>>> b = np.array([[ 1, 2, 9, 4, 3],
[ 0, 5, 3, 5, 1],
[ 8, 3, 2, 4, 7]])
切片
一维切片,跟普通的python列表切片一样。
# 正索引从零开始,len(a)-1结束
# 负索引从-len(a)开始,-1结束
>>> a
array([10, 5, 7, 9, 8, 4])
>>> a[1:3]
array([5, 7])
>>> a[:3]
array([10, 5, 7])
>>> a[-4:]
array([7, 9, 8, 4])
>>> a[:-3]
array([10, 5, 7])
# 只能正向索引,否则返回为空
# 无法从倒数第4个到正数第2个
>>> a[-4:2]
array([], dtype=int32)
# 可以从正数第2个到倒数第1个
>>> a[2:-1]
array([7, 9, 8])
#每n间隔n-1个取一个
>>> a[::3]
array([10, 9])
>>> a[::2]
array([10, 7, 8])
# 间隔反取
>>> a[::-2]
array([4, 9, 5])
# 区间跳转 (开始:结束:间隔)
>>> b = np.arange(10)
>>> b
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> b[1:7:2]
array([1, 3, 5])
>>> b[7:1:-2]
array([7, 5, 3])
多维切片
先行切片,再列切片。
 # 灰色区域
>>> b[1,3:5]
array([5, 1])
# 橙色区域
>>> b[0:2,0:2]
array([[1, 2],
[0, 5]])
# 蓝色区域
>>> b[:,2]
array([9, 3, 2])
# 间隔选取
>>> b[::2,1::2]
array([[2, 4],
[3, 4]])
花式索引
本质上是传入一个坐标列表来作为索引获取数据。
通过位置索引
a = np.arange(8)
indices = [1,2,-2]
b = a[indices]
# output
array([1, 2, 6])
# 也可以直接传入行和列(对应位置组成点)
a = np.arange(36).reshape([6,6])
# putout
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, 32, 33, 34, 35]])
b = a[[0,1,2,3,4],[1,2,3,4,5]]
# output
array([ 1, 8, 15, 22, 29])
布尔索引
通过传入一个等长的bool列表来进行索引截取。
a = np.arange(10)
b = a < 5
# output 返回的是一个bool数组
array([True, True, True, True, True, False, False, False, False, False])
a[a < 5] = 0
print(a)
# output
array([0, 0, 0, 0, 0, 5, 6, 7, 8, 9])
# 注意这里的bool个数应该和a对等
c = np.array([0,1,0,0,0,1,0,1,0,1],dtype = bool)
a[c]
# output
array([1, 5, 7, 9])
# 值得注意的是,只有0才为False
In [1]: c = np.array([1,2,3,4,0,0,-5],dtype = bool)
Out[2]: array([ True, True, True, True, False, False, True])
any()
和 all()
# any()意思是只要有一个为真,这个数组就为真
(a < 5).any()
# output
True
#all()意思是所有为真才为真
(a < 9).all()
(a < 10).all()
#output
False True
多条件布尔索引
操作符
操作符 | 含义 |
---|---|
& | and |
丨 | or |
~ | not |
^ | xor |
# 之所以使用括号是因为位运算符优先级高于比较运算符
(a > 3) & (a < 5)
# output
array([False, False, False, False, True, False, False, False, False, False])
# 因此这样我们就可以进行bool索引
a[(a > 3) & (a < 5)]
# output
array([4])
如何得到满足条件的索引值???
a = np.array([[1,2,3,4],[2,5,9,7],[5,2,3,4]])
np.nonzero(a < 3) # 输出的是一个符合条件的二维索引坐标
# output 既分别是(0,0),(0,1),(1,0),(2,1)四个点
(array([0, 0, 1, 2], dtype=int64), array([0, 1, 0, 1], dtype=int64))
#上面的输出方式如果看不习惯我们可以反转一下就比较清楚了
np.transpose(np.nonzero(a < 3))
# output
array([[0, 0],
[0, 1],
[1, 0],
[2, 1]], dtype=int64)
输出下列表格中各颜色区域的元素。
a = np.arange(36).reshape([6,6])
# 黄色区域
y = a[[0,1,2,3,4],[1,2,3,4,5]]
# 红色区域
mask = np.array([1,0,1,0,0,1],dtype = bool)
r = a[mask,2]
# 蓝色区域
b = a[3:,[0,2,5]]
输出上表中能被3整除的数
这里需要说明的是,在通过以对应位置输出时,由于引入Nan(表空缺,浮点型),所以原始数据不可避免的被强制改变成浮点型。
# 以一维数组形式输出
mask = a % 3 ==0
a[mask]
# output
array([ 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33])
# 以对应位置输出
# 方法一,使用emoty_like函数
b = np.empty_like(a,dtype = np.float)
b.fill(np.nan)
b[mask] = a[mask]
#output
array([[ 0., nan, nan, 3., nan, nan],
[ 6., nan, nan, 9., nan, nan],
[12., nan, nan, 15., nan, nan],
[18., nan, nan, 21., nan, nan],
[24., nan, nan, 27., nan, nan],
[30., nan, nan, 33., nan, nan]])
# 方法二,使用where函数
mask = a % 3 == 0
np.where(mask,a,np.nan)
# output
array([[ 0., nan, nan, 3., nan, nan],
[ 6., nan, nan, 9., nan, nan],
[12., nan, nan, 15., nan, nan],
[18., nan, nan, 21., nan, nan],
[24., nan, nan, 27., nan, nan],
[30., nan, nan, 33., nan, nan]])
切片与花式索引
值得注意的是花式索引给出的是一个 副本 而切片给出的是一个 视图
a = np.arange(10)
num1 = a[(a > 2) & (a < 8)]
num2 = a[3,8]
# output num1和num2均为
array([3, 4, 5, 6, 7])
# 修改num1
num1[2] = 9
# output num1改变,a未改变
array([3, 4, 9, 6, 7])
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 修改num2
num2[2] = 9
# output num2和a均改变
array([3, 4, 9, 6, 7])
array([0, 1, 2, 3, 4, 9, 6, 7, 8, 9])
形状完全一致
在最简单的情况下,如下两个形状 (3,2)
的数组,假设我们把它们加在一起,形状显然匹配,因为它们是相同的形状。得到的结果是数组元素对应位置逐个相加。
形状不一致
另一种是兼容的数组,对于左边 a
形状为 (3,2)
,右边 b
形状 (2,)
的数组,则numpy会认为从 右侧对齐 。然后广播数组 b
依次与 a
的每一行的元素进行操作。此时numpy并不需要额外的内存来复制 b
将他们达到完全相同的形状。
# (3,2)、 (2,) 右侧对齐为(2) 广播3次
a = np.arange(1,7).reshape(3,2)
b = np.array([5,6])
# output a+b
array([[ 6, 8],
[ 8, 10],
[10, 12]])
# (2,3,2,2)、 (2,2) 右侧对齐为(2,2) 广播2*3次
a = np.arange(24).reshape(2,3,2,2)
b = np.array([[1,1],[1,1]])
# output a+b
array([[[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]],
[[ 9, 10],
[11, 12]]],
[[[13, 14],
[15, 16]],
[[17, 18],
[19, 20]],
[[21, 22],
[23, 24]]]])