NumPy
1 NumPy概述
NumPy(Numerical Python的简称)是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。
NumPy的优点:
- 一个强大的N维数组对象ndarray,具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组
- 用于集成由C、C++、Fortran等语言类库的C语言 API
- 线性代数、随机数生成以及傅里叶变换功能。
- 用于对整组数据进行快速运算的标准数学函数(无需编写循环),支持大量的数据运算
- 是众多机器学习框架的基础库
NumPy之于数值计算特别重要的原因之一,是因为它可以高效处理大数组的数据。这是因为:
- NumPy是在一个连续的内存块中存储数据,独立于其他Python内置对象。NumPy的C语言编写的算法库可以操作内存,而不必进行类型检查或其它前期工作。比起Python的内置序列,NumPy数组使用的内存更少。
- NumPy可以在整个数组上执行复杂的计算,而不需要Python的for循环。
我们在使用NumPy时习惯给它起个别名np,推荐这么做
NumPy的运行速度:
import numpy as np
np_arr = np.arange(1000000)
py_list = list(range(1000000))
%time for _ in range(10): np_arr2 = np_arr ** 2
%time for _ in range(10): py_list2 = [x ** 2 for x in py_list]
Wall time: 20 ms
Wall time: 5.16 s
可以看到基于NumPy的算法比纯python快100倍以上,并且使用的内存更少
2 创建ndarry
NumPy最重要的一个特点就是ndarray(N-dimensional array),即N维数组),该对象是一个快速而灵活的大数据集容器。你可以利用这种数组对整块数据执行一些数学运算,其语法跟标量元素之间的运算一样。
import numpy as np
print(np.__version__)
nparr = np.array([i for i in range(10)])
print(nparr)
nparr2 = np.array(list('abcdefg'))
print(nparr2)
nparr3 = np.array([[11,22,33,44],[10,20,30,40]])
print(nparr3)
1.15.4
[0 1 2 3 4 5 6 7 8 9]
['a' 'b' 'c' 'd' 'e' 'f' 'g']
[[11 22 33 44]
[10 20 30 40]]
2.1 arange创建数组
arange函数是python内置函数range函数的数组版本.
ndarray = np.arange(10)
print(ndarray)
ndarray1 = np.arange(10, 20)
print(ndarray1)
ndarray2 = np.arange(10, 20, 2)
print(ndarray2)
print(ndarray2.shape)
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[10 12 14 16 18]
(5,)
2.2 其他创建numpy数组的方式
- 使用zeros和zeros_like创建数组
用于创建数组,数组元素默认值是0. 注意:zeros_linke函数只是根据传入的ndarray数组的shape来创建所有元素为0的数组,并不是拷贝源数组中的数据.
ndarray4 = np.zeros(10)
ndarray5 = np.zeros((3, 3))
ndarray6 = np.zeros_like(ndarray5) # 按照 ndarray5 的shape创建数组
# 打印数组元素类型
print("以下为数组类型:")
print('ndarray4:', type(ndarray4))
print('ndarray5:', type(ndarray5))
print('ndarray6:', type(ndarray6))
print("-------------")
print("以下为数组元素类型:")
print('ndarray4:', ndarray4.dtype)
print('ndarray5:', ndarray5.dtype)
print('ndarray6:', ndarray6.dtype)
print("-------------")
print("以下为数组形状:")
print('ndarray4:', ndarray4.shape)
print('ndarray5:', ndarray5.shape)
print('ndarray6:', ndarray6.shape)
以下为数组类型:
ndarray4:
ndarray5:
ndarray6:
以下为数组元素类型:
ndarray4: float64
ndarray5: float64
ndarray6: float64
以下为数组形状:
ndarray4: (10,)
ndarray5: (3, 3)
ndarray6: (3, 3)
- ones和ones_like创建数组
# 用于创建所有元素都为1的数组.ones_like用法同zeros_like用法.
# 创建数组,元素默认值是1
ndarray7 = np.ones(10)
print(ndarray7)
ndarray8 = np.ones((3, 3))
print(ndarray8)
ndarray9 = np.ones_like(ndarray8) # 按照 ndarray8 的shape创建数组
print(ndarray9)
[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.]]
- empty和empty_like创建数组
ndarray10 = np.empty(5)
ndarray11 = np.empty((2, 3))
ndarray12 = np.empty_like(ndarray11)
print(ndarray10)
print(ndarray11)
print(ndarray12)
[2.12199579e-314 6.36598737e-314 1.06099790e-313 1.48539705e-313
1.90979621e-313]
[[2.12199579e-314 6.36598737e-314 1.06099790e-313]
[1.48539705e-313 1.90979621e-313 2.33419537e-313]]
[[2.12199579e-314 6.36598737e-314 1.06099790e-313]
[1.48539705e-313 1.90979621e-313 2.33419537e-313]]
- eye创建对角矩阵数组
该函数用于创建一个N*N的矩阵,对角线为1,其余为0.
ndarray13 = np.eye(5)
print(ndarray13)
[[1. 0. 0. 0. 0.]
[0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0.]
[0. 0. 0. 0. 1.]]
- full创建数组
创建指定形状的数组并填充相应元素
ndarray14 = np.full((3, 5), 666)
print(ndarray14)
[[666 666 666 666 666]
[666 666 666 666 666]
[666 666 666 666 666]]
3 ndarray的数据类型及索引
3.1 ndarry数据类型
3.1.1 dtype 数据类型
dtype(数据类型)是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息:
arr1 = np.array([1, 2, 3], dtype=np.float64)
arr2 = np.array([1, 2, 3], dtype=np.int32)
print(arr1.dtype)
print(arr2.dtype)
float64
int32
dtype是NumPy灵活交互其它系统的源泉之一。多数情况下,它们直接映射到相应的机器表示,这使得“读写磁盘上的二进制数据流”以及“集成低级语言代码(如C、Fortran)”等工作变得更加简单。数值型dtype的命名方式相同:一个类型名(如float或int),后面跟一个用于表示各元素位长的数字。标准的双精度浮点值(即Python中的float对象)需要占用8字节(即64位)。因此,该类型在NumPy中就记作float64
类型 | 类型代码 | 说明 |
---|---|---|
int8,uint8 | i1,u1 | 有符号和无符号的8位(1个字节长度)整型 |
int16,uint16 | i2,u2 | 有符号和无符号的16位(2个字节长度)整型 |
int32,uint32 | i4,u4 | 有符号和无符号的32位(4个字节长度)整型 |
float16 | f2 | 半精度浮点数 |
float32 | f4或f | 标准单精度浮点数 |
float64 | f8或d | 双精度浮点数 |
bool | ? | 布尔类型 |
object | O | python对象类型 |
unicode | U | 固定长度的unicode类型,跟字符创定义方式一样 |
这些都不是必须要记住的,需要了解时直接百度即可
- 通过ndarray的astype方法明确地将一个数组从一个dtype转换成另一个dtype
- 整数转化成浮点数
arr = np.array([1, 2, 3, 4, 5])
print(arr.dtype)
float_arr = arr.astype(np.float64)
print(float_arr.dtype)
int32
float64
2. 浮点数转化成整数(丢失精度)
arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
print(arr)
arr1 = arr.astype(np.int32)
print(arr1)
[ 3.7 -1.2 -2.6 0.5 12.9 10.1]
[ 3 -1 -2 0 12 10]
3. 数字字符串转化成数值类型
numericstrings = np.array(['1.25', '-9.6', '42'],dtype=np.string_)
print(numericstrings.astype(float))
[ 1.25 -9.6 42. ]
注意:使用numpy.string_类型时,一定要小心,因为NumPy的字符串数据是大小固定的,发生截取时,不会发出警告。pandas提供了更多非数值数据的便利的处理方法。NumPy很聪明,它会将python类型映射到等价的dtype上,比如这里的float会被自动转化成float64
int_array = np.arange(10)
calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
print(int_array.astype(calibers.dtype))
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
dtype会自动省略浮点数中小数点后的零
4. 使用简写形式
empty_uint32 = np.empty((3,3), dtype='u4') # uint32可以写为u4
print(empty_uint32)
[[ 39583911 0 4600192]
[ 0 4600192 0]
[3636330753 2046 0]]
注意:调用astype总会创建一个新的数组(一个数据的备份),即使新的dtype与旧的dtype相同。
3.1.2 Numpy数组运算
数组很重要,因为它使你不用编写循环即可对数据执行批量运算。NumPy用户称其为矢量化(vectorization)。大小相等的数组之间的任何算术运算都会将运算应用到元素级:(不需要循环即可对数据进行批量运算,叫做矢量化运算. 不同形状的数组之间的算数运算,叫做广播,后面会介绍)
- 相同形状的数组的运算
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
print(arr)
print(arr * arr)
print(arr - arr)
[[1. 2. 3.]
[4. 5. 6.]]
[[ 1. 4. 9.]
[16. 25. 36.]]
[[0. 0. 0.]
[0. 0. 0.]]
- 数组与标量的运算
数组与标量的算术运算会将标量值传播到各个元素
print(1 / arr)
print(arr ** 0.5)
[[1. 0.5 0.33333333]
[0.25 0.2 0.16666667]]
[[1. 1.41421356 1.73205081]
[2. 2.23606798 2.44948974]]
- 数组的比较
形状相同的数组之间的比较会生成布尔值数组
arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
print(arr2)
print(arr2 > arr)
[[ 0. 4. 1.]
[ 7. 2. 12.]]
[[False True False]
[ True False True]]
- 数组的索引和切片
numpy数组的索引是一个内容丰富的主题,因为选取数据子集或单个元素的方式有很多。一维数组很简单,从表面上看,它们和python列表的功能差不多。所以主要看二维数组和三维数组:
import numpy as np
x = np.arange(10)
print(x)
X = np.arange(15).reshape((3,5))
print(X)
# 访问x中索引是1的元素
print(x[1])
# 赋值 损失了精度,截断操作
x[1] = 3.64
print(x)
# 切片
print(x[1:4])
# 按照先行后列的访问方式
print(X[1][4])
# 第二种写法,推荐, 逗号前面是行索引,后面是列索引
print(X[1,4])
X[1,4] = 33
print(X)
# X的切片
print(X[:2, 2:4])
[0 1 2 3 4 5 6 7 8 9]
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
1
[0 3 2 3 4 5 6 7 8 9]
[3 2 3]
9
9
[[ 0 1 2 3 4]
[ 5 6 7 8 33]
[10 11 12 13 14]]
[[2 3]
[7 8]]
3.1.3 numpy的特殊之处
- 当把一个数字值赋值给一个切片时,该值会自动传播到整个选区。跟列表的区别在于,数组切片是原始数组的视图,这意味着数据不会被赋值,视图上的任何修改都会直接反应到源数组上.
- 大家可能对此感到不解,由于Numpy被设计的目的是处理大数据,如果Numpy将数据复制来复制去的话会产生何等的性能和内存问题.
- 如果要得到一个切片副本的话,必须显式进行复制操作.
# 切片赋值
x[3:6] = 12
print(x)
# 对切片的值进行修改,也会体现到原数组身上
arr_slice = x[3:6]
arr_slice[0] = 999
print(arr_slice)
arr_slice[:] =666
print(x)
# 如果你还是想要数组切片的拷贝而不是一份视图的话,可以进行如下操作
print(X[:2, 2:4].copy())
[ 0 3 2 12 12 12 6 7 8 9]
[999 12 12]
[ 0 3 2 666 666 666 6 7 8 9]
[[2 3]
[7 8]]
3.2 Fancy indexing花式索引
花式索引是一个NumPy术语,它指的是利用整数数组进行索引
arr = np.empty((8, 4))
for i in range(8):
arr[i] = i
print(arr)
[[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.]]
# 为了以特定顺序选取行子集,只需传入一个用于指定顺序的整数列表或ndarray即可
print(arr[[4, 3, 0, 6]])
[[4. 4. 4. 4.]
[3. 3. 3. 3.]
[0. 0. 0. 0.]
[6. 6. 6. 6.]]
# 这段代码确实达到我们的要求了!使用负数索引将会从末尾开始选取行
print(arr[[-3, -5, -7]])
[[5. 5. 5. 5.]
[3. 3. 3. 3.]
[1. 1. 1. 1.]]
# 一次传入多个索引数组会有一点特别。它返回的是一个一维数组,其中的元素对应各个索引元组
arr = np.arange(32).reshape((8, 4))
print(arr)
print()
print(arr[[1, 5, 7, 2], [0, 3, 1, 2]]) # 第一个索引中是行坐标,第二个索引中的是列坐标。相同位置的元素组成一个坐标,选出一个元素
[[ 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]][ 4 23 29 10]
最终选出的是元素(1,0)、(5,3)、(7,1)和(2,2)。无论数组是多少维的,花式索引总是一维的。
这个花式索引的行为可能会跟某些用户的预期不一样,选取矩阵的行列子集应该是矩形区域的形式才对。下面是得到该结果的一个办法:
print(arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]])
[[ 4 7 5 6]
[20 23 21 22]
[28 31 29 30]
[ 8 11 9 10]]
注意:花式索引跟切片不一样,它总是将数据复制到新数组中。对于新数组中数据的改变不会反映到原数组中
3.3 布尔型索引
来看这样一个例子,假设我们有一个用于存储数据的数组以及一个存储姓名的数组(含有重复项)。在这里,我将使用numpy.random中的randn函数生成一些正态分布的随机数据:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
print(names)
print()
# 我将使用numpy.random中的randn函数生成一些正态分布的随机数据
data = np.random.randn(7, 4)
print(data)
['Bob' 'Joe' 'Will' 'Bob' 'Will' 'Joe' 'Joe']
[[-2.33086753e-01 -2.11559329e-01 -5.68176123e-03 4.25333048e-01]
[ 3.51430509e-01 -3.75894745e-03 -1.50462755e+00 9.31211514e-03]
[ 4.09926614e-01 -7.18040329e-01 1.22509643e+00 8.95648381e-01]
[ 3.99166096e-01 -3.76927909e-01 1.71268889e-03 -1.23910399e+00]
[ 2.11765485e-01 -8.20546694e-01 4.44831659e-01 -1.78620834e+00]
[ 9.65591103e-01 -9.40774521e-01 4.76456319e-01 -9.68547600e-01]
[ 1.85897140e+00 3.81405447e-01 -7.36214596e-01 -1.89778617e+00]]
假设每个名字都对应data数组中的一行,而我们想要选出对应于名字"Bob"的所有行。跟算术运算一样,数组的比较运算(如==)也是矢量化的。因此,对names和字符串"Bob"的比较运算将会产生一个布尔型数组:
print(names == 'Bob')
[ True False False True False False False]
这个布尔型数组可用于数组索引:
print(data[names == 'Bob']) # 实际上选的是第1行和第4行的筛选
[[-0.23308675 -0.21155933 -0.00568176 0.42533305]
[ 0.3991661 -0.37692791 0.00171269 -1.23910399]]
# 如果布尔型数组的长度不对,布尔型选择就会出错,因此一定要小心。
# 下面的例子,我选取了names == 'Bob'的行,并索引了列:
print(data[names == 'Bob', 2:])
[[-0.00568176 0.42533305]
[ 0.00171269 -1.23910399]]
# 要选择除"Bob"以外的其他值,既可以使用不等于符号(!=),也可以通过~对条件进行否定:
print(names != 'Bob')
print(data[~(names == 'Bob')])
[False True True False True True True]
[[ 0.35143051 -0.00375895 -1.50462755 0.00931212]
[ 0.40992661 -0.71804033 1.22509643 0.89564838]
[ 0.21176548 -0.82054669 0.44483166 -1.78620834]
[ 0.9655911 -0.94077452 0.47645632 -0.9685476 ]
[ 1.8589714 0.38140545 -0.7362146 -1.89778617]]
3.3.1 使用布尔类型数组设置值是一种经常用到的手段
import numpy as np
ndarray1 = np.arange(5)
print(ndarray1)
ndarray2 = np.arange(16).reshape((4, 4))
print(ndarray2)
names = np.array(['aaa', 'bbb', 'ccc', 'ddd'])
print(names)
# 将数组ndarray1中所有大于2的元素设置成8
ndarray1[ndarray1 > 2] = 8
print(ndarray1)
# 将ndarray2的aaa这一行所有的元素设置为0
ndarray2[names == 'aaa'] = 0
# 将ndarray2的bbb这一行2位置往后所有的元素设置为1
ndarray2[names == 'bbb', 2:] = 1
print(ndarray2)
# 将ndarray2的ccc ddd这2行所有的元素设置为2
ndarray2[(names == 'ccc') | (names == 'ddd')] = 2
print(ndarray2)
[0 1 2 3 4]
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
['aaa' 'bbb' 'ccc' 'ddd']
[0 1 2 8 8]
[[ 0 0 0 0]
[ 4 5 1 1]
[ 8 9 10 11]
[12 13 14 15]]
[[0 0 0 0]
[4 5 1 1]
[2 2 2 2]
[2 2 2 2]]
4 数组函数
4.1 通用函数:快速的元素级数组函数
通用函数(即universal function)是一种对ndarray中的数据执行元素级运算的函数。你可以将其看做简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。 许多ufunc都是简单的元素级变体,如sqrt和exp:
- 常用的一元ufunc:
函数 | 说明 |
---|---|
abs | 计算证书、浮点数的绝对值 |
aqrt | 计算各元素的平方根。相当于arr ** 0.5 |
square | 计算各元素的平方。相当于arr ** 2 |
sign | 计算各元素的正负号,1(正数)、0(零)、-1(负数) |
cell | 计算各元素的celling值,即大于该值的最小整数 |
floor | 计算各元素的floor值,即小于该值的最大整数 |
rint | 将各元素值四舍五入到最近的整数,保留dtype |
modf | 将数组的小数和整数部分以两个独立数组的形式返回 |
isnan | 返回一个表示“那些是NaN(这不是一个数字)”的布尔类型数组 |
import numpy as np
ndarray1 = np.array([3.5, 1.7, 2.2, -7.8, np.nan, 4.6, -3.4])
print(ndarray1)
# abs 计算整数、浮点数的绝对值。
print(np.abs(ndarray1))
# square计算各元素的平方。相当于arr ** 2
print(np.square(ndarray1))
# sign 计算各元素的正负号,1(正数)、0(零)、-1(负数)
print(np.sign(ndarray1))
# ceil 计算各元素的celling值,即大于该值的最小整数。
print(np.ceil(ndarray1))
# floor 计算各元素的floor值,即小于等于该值的最大整数。
print(np.floor(ndarray1))
# rint 将各元素值四舍五入到最近的整数,保留dtype
print(np.rint(ndarray1))
# isnan 返回一个表示“那些是NaN(这不是一个数字)”的布尔类型数组.
print(np.isnan(ndarray1))
[ 3.5 1.7 2.2 -7.8 nan 4.6 -3.4]
[3.5 1.7 2.2 7.8 nan 4.6 3.4]
[12.25 2.89 4.84 60.84 nan 21.16 11.56]
[ 1. 1. 1. -1. nan 1. -1.]
[ 4. 2. 3. -7. nan 5. -3.]
[ 3. 1. 2. -8. nan 4. -4.]
[ 4. 2. 2. -8. nan 5. -3.]
[False False False False True False False]
- 常用的二元ufunc:
函数 | 说明 |
---|---|
add | 将数组中对应的元素相加 |
subtract | 从第一个数组中减去第二个数组中的元素 |
multiply | 数组元素相乘 |
divide、floor_divide | 除法、向下整除法(丢弃余数) |
power | 对第一个数组中元素A,根据第二数组中的相应元素B,计算A的B次方 |
maximum、fmax | 元素级的最大值计算,fmax将忽略NaN |
minimum、fmin | 元素级的最小值计算,fmin将忽略NaN |
mod | 元素级的求模运算 |
copysign | 将第二个数组中的值的符号赋值给第一个数组中的值 |
greater、greater_equal | 执行元素级的运算比较,最终产生布尔类型数组 |
ndarray2 = np.random.randint(1, 20, (4, 5))
ndarray3 = np.random.randint(-10, 10, (4, 5))
ndarray3 = np.where(ndarray3 == 0, 1, ndarray3)
print(ndarray2)
print(ndarray3)
# add 将数组中对应的元素相加.
print(np.add(ndarray2, ndarray3))
# subtract 从第一个数组中减去第二个数组中的元素.
print(np.subtract(ndarray2, ndarray3))
# maximum、fmax 从两个数组中取出最大值。fmax将忽略NaN
print(np.maximum(ndarray2, ndarray3))
# mod 元素级的求模计算.
print(np.mod(ndarray2, ndarray3))
# copysign 将第二个数组中的值的符号复制给第一个数组中的值.
print(np.copysign(ndarray2, ndarray3))
# greater、greater_equal 执行元素级的运算比较,最终产生布尔类型数组。
print(np.greater(ndarray2, ndarray3))
[[ 5 4 4 9 13]
[ 8 19 19 15 13]
[16 11 1 9 15]
[11 8 13 14 8]]
[[-7 -5 -5 1 3]
[ 5 -5 -5 -7 -1]
[ 8 5 7 -7 1]
[-7 1 6 5 -4]]
[[-2 -1 -1 10 16]
[13 14 14 8 12]
[24 16 8 2 16]
[ 4 9 19 19 4]]
[[12 9 9 8 10]
[ 3 24 24 22 14]
[ 8 6 -6 16 14]
[18 7 7 9 12]]
[[ 5 4 4 9 13]
[ 8 19 19 15 13]
[16 11 7 9 15]
[11 8 13 14 8]]
[[-2 -1 -1 0 1]
[ 3 -1 -1 -6 0]
[ 0 1 1 -5 0]
[-3 0 1 4 0]]
[[ -5. -4. -4. 9. 13.]
[ 8. -19. -19. -15. -13.]
[ 16. 11. 1. -9. 15.]
[-11. 8. 13. 14. -8.]]
[[ True True True True True]
[ True True True True True]
[ True True False True True]
[ True True True True True]]
4.2 数组统计函数
可以通过数组上的一组数学函数对整个数组或某些数据进行统计计算。 基本的数组统计方法:
方法 | 说明 |
---|---|
mean | 算术平均数。零长度的数组的mean为NaN |
sum | 所有元素之和 |
max、min | 所有元素的最大值、所有元素的最小值 |
std、var | 所有元素的标准差、所有元素的方差 |
argmax/argmin | 最大值的下标索引值、最小值的下标索引值 |
cumsum/cumprod | 所有元素的累计和、所有元素的累计积 |
多维数组默认统计全部维度,axis参数可以按指定轴心统计,值为0则按列统计,值为1则按行统计。
import numpy as np
ndarray1 = np.random.randint(1, 10, (4, 5))
print(ndarray1)
[[2 1 8 1 6]
[7 7 3 6 7]
[1 2 6 1 7]
[3 3 1 5 4]]
- sum求元素和
# 0-列 1-行
# sum-计算所有元素和
print(np.sum(ndarray1))
# sum-计算每一列的元素和
print(np.sum(ndarray1, axis=0))
# sum-计算每一行的元素和
print(np.sum(ndarray1, axis=1))
81
[13 13 18 13 24]
[18 30 17 16]
- armax求最大值索引
# argmax-默认情况下按照一维数组索引
print(np.argmax(ndarray1))
# argmax-统计每一列最大
print(np.argmax(ndarray1, axis=0))
# argmax-统计每一行最大
print(np.argmax(ndarray1, axis=1))
2
[1 1 0 1 1]
[2 0 4 3]
- mean求平均数
# mean-求所有元素的平均值
print(np.mean(ndarray1))
# mean-求每一列元素的平均值
print(np.mean(ndarray1, axis=0))
# mean-求每一行元素的平均值
print(np.mean(ndarray1, axis=1))
4.05
[3.25 3.25 4.5 3.25 6. ]
[3.6 6. 3.4 3.2]
- cumsum求元素累计和
# cumsum-前面元素的累计和
print(np.cumsum(ndarray1))
# cumsum-每一列元素的累计和
print(np.cumsum(ndarray1, axis=0))
# cumsum-每一行元素的累计和
print(np.cumsum(ndarray1, axis=1))
[ 2 3 11 12 18 25 32 35 41 48 49 51 57 58 65 68 71 72 77 81]
[[ 2 1 8 1 6]
[ 9 8 11 7 13]
[10 10 17 8 20]
[13 13 18 13 24]]
[[ 2 3 11 12 18]
[ 7 14 17 23 30]
[ 1 3 9 10 17]
[ 3 6 7 12 16]]
4.3 all和any函数
import numpy as np
# 判断两个数组元素是否相等
ndarray1 = np.arange(6).reshape((2, 3))
ndarray2 = np.arange(6).reshape((2, 3))
print(ndarray1)
print(ndarray2)
ndarray3 = np.array([[ 0, 1, 2], [ 8, 9, 10]])
print(ndarray3)
print((ndarray1 == ndarray2).all())
print((ndarray1 == ndarray3).all())
print((ndarray1 == ndarray3).any())
[[0 1 2]
[3 4 5]]
[[0 1 2]
[3 4 5]]
[[ 0 1 2]
[ 8 9 10]]
True
False
True
4.4 添加和删除函数
方法 | 描述 |
---|---|
delete | 返回一个新的数组,该数组具有沿轴删除的子数组 |
insert(arr,obj,values[,axis]) | 沿给定轴插入值 |
append(arr,values[,axis]) | 将值添加到数组的末尾 |
resize(a,new_shape) | 返回具有指定形状的新数组 |
concatenate((a1,a2,...),axis=0) | 沿现有轴合并多个数组 |
注意:reshape:有返回值,即不对原始多维数组进行修改; resize:无返回值,即会对原始多维数组进行修改;
import numpy as np
ndarray1 = np.arange(4)
ndarray2 = np.arange(4)
print(ndarray2)
ndarray3 = np.arange(12).reshape((3, 4))
print(ndarray3)
[0 1 2 3]
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
- delete删除一行或者一列数组元素
ndarray5 = np.arange(20).reshape((4, 5))
print(ndarray5)
# 删除第0行元素
print(np.delete(ndarray5, 0, axis=0))
# 删除第2列元素
print(np.delete(ndarray5, 1, axis=1))
# 删除第0、2、3列元素
print(np.delete(ndarray5, [0, 2, 3], axis=1))
# 使用np.s_[::]创建切片对象
# 删除1、2列元素
print(np.delete(ndarray5, np.s_[1:3], axis=1))
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
[[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
[[ 0 2 3 4]
[ 5 7 8 9]
[10 12 13 14]
[15 17 18 19]]
[[ 1 4]
[ 6 9]
[11 14]
[16 19]]
[[ 0 3 4]
[ 5 8 9]
[10 13 14]
[15 18 19]]
- append数组中追加元素
# 数组追加一个数值元素
print(np.append(ndarray1, 100))
# 在一维数组后追加一维数组
print(np.append(ndarray1, ndarray2))
# 在二维数组后追加标量元素
print(np.append(ndarray3, 100))
# append总是返回一维数组
print(np.append(ndarray1, ndarray3))
[ 0 1 2 3 100]
[0 1 2 3 0 1 2 3]
[ 0 1 2 3 4 5 6 7 8 9 10 11 100]
[ 0 1 2 3 0 1 2 3 4 5 6 7 8 9 10 11]
- insert插入元素
# 在第2个位置插入元素100
ndarray6 = np.arange(4)
print(ndarray6)
print(np.insert(ndarray6, 1, 100))
# 在第3个位置插入两个元素10、20
print(np.insert(ndarray6, 2, [10, 20]))
# 在第2行插入一行元素
print(np.insert(ndarray6, 1, np.array([100, 200, 300, 400]), axis=0)) # ndarray6是一维数组所以不会添加在第一行下面,而是会添加在相应位置。结果还是一维数组
# 在第3列插入一列元素
ndarray7 = np.arange(12).reshape((3, 4))
print(np.insert(ndarray7, 2, np.array([100, 200, 300]), axis=1))
[0 1 2 3]
[ 0 100 1 2 3]
[ 0 1 10 20 2 3]
[ 0 100 200 300 400 1 2 3]
[[ 0 1 100 2 3]
[ 4 5 200 6 7]
[ 8 9 300 10 11]]
- concatenate合并两个数组元素
ndarray4 = np.arange(12).reshape((3, 4))
print(ndarray4)
# 合并两个一维数组
print(np.concatenate((ndarray1, ndarray2)))
# 合并两个二维数组(列)
print(np.concatenate((ndarray3, ndarray4), axis=0))
# 合并两个二维数组(行)
print(np.concatenate((ndarray3, ndarray4), axis=1))
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[0 1 2 3 0 1 2 3]
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 0 1 2 3 0 1 2 3]
[ 4 5 6 7 4 5 6 7]
[ 8 9 10 11 8 9 10 11]]
5 唯一化和集合函数
NumPy提供了一些针对一维ndarray的基本集合运算。最常用的就是np.unique了,它用于找出数组中的唯一值并返回已排序的结果。
方法 | 说明 |
---|---|
unique(x) | 计算x中惟一的元素,并返回有序结果 |
intersect1d(x,y) | 计算x和y中的公共元素,并返回有序结果 |
union1d(x,y) | 计算x和y的并集,并返回有序结果 |
in1d(x,y) | 得到一个表示“x的元素是否包含于y”的布尔型数组 |
setdiff1d(x,y) | 集合的差,即元素在x中且不在y中 |
- 唯一化
import numpy as np
names = np.array(['aaa', 'bbb', 'ccc', 'aaa', 'ddd', 'eee', 'ccc'])
ndarray1 = np.random.randint(1, 5, 10)
ndarray2 = np.random.randint(1, 5, (3, 4))
print(ndarray1)
print(ndarray2)
print(np.unique(names))
print(np.unique(ndarray1))
print(np.unique(ndarray2))
[2 1 4 1 4 2 4 1 2 4]
[[3 4 2 3]
[2 4 4 2]
[3 4 4 1]]
['aaa' 'bbb' 'ccc' 'ddd' 'eee']
[1 2 4]
[1 2 3 4]
- 计算两个数组交集
ndarray3 = np.arange(1, 10)
ndarray4 = np.arange(5, 15)
print(ndarray3)
print(ndarray4)
print(np.intersect1d(ndarray3, ndarray4))
[1 2 3 4 5 6 7 8 9]
[ 5 6 7 8 9 10 11 12 13 14]
[5 6 7 8 9]
- 计算两个数组并集
ndarray5 = np.arange(1, 10)
ndarray6 = np.arange(5, 15)
print(ndarray5)
print(ndarray6)
print(np.union1d(ndarray5, ndarray6))
[1 2 3 4 5 6 7 8 9]
[ 5 6 7 8 9 10 11 12 13 14]
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
- 数组中的元素是否在另一个数组中存在
ndarray7 = np.arange(1, 6)
ndarray8 = np.arange(3, 8)
print(ndarray7)
print(ndarray8)
print(np.in1d(ndarray7, ndarray8))
[1 2 3 4 5]
[3 4 5 6 7]
[False False True True True]
- 计算两个数组的差集
ndarray9 = np.arange(1, 6)
ndarray10 = np.arange(3, 8)
print(ndarray9)
print(ndarray10)
print(np.setdiff1d(ndarray9, ndarray10))
[1 2 3 4 5]
[3 4 5 6 7]
[1 2]
5.1 随机数生成函数
numpy.random模块对python内置的random进行了补充。我们使用numpy.random可以很方便根据需要产生大量样本值。而python内置的random模块则一次生成一个样本值.
参数 | 解释 |
---|---|
.rand(d0,d1,...,dn) | 创建d0-dn维度的均匀分布的随机数数组,浮点数,范围是0-1 |
.randn(d0,d1,...,dn) | 创建d0-dn维度的标准正态分布的随机数数组,浮点数,平均数是0,标准差是1 |
.randint(low,high,(shape)) | 从给定上下限范围选取随机数整数,范围是low-high,形状是shape |
.uniform(low,high,(size)) | 产生具有均匀分布的数组,low起始值,high结束值,size形状 |
.normal(loc,scale,(size)) | 从指定正态分布中随机抽取样本,分布中心是loc(概率分布的均值),标准差是scale,形状是size |
.seed(s) | 随机数种子,s是给定的种子值。因为计算机生成的是伪随机数,所以通过设定相同的随机数种子,可以每次生成相同的随机数 |
下图简单回忆一下均匀分布和正态分布
函数 | 说明 |
---|---|
permutation | 如果给的是数字,则生成指定个数随机数;如果给的是数组,则打乱数组返回(有返回值,原有数组顺序不变) |
shuffle | 打乱一个序列的原有顺序(无返回值,原有数组顺序打乱) |
randint | 从指定的上下限随机选取整数 |
import numpy as np
ndarray1 = np.arange(10)
print(np.random.permutation(5))
print(np.random.permutation(ndarray1))
np.random.shuffle(ndarray1)
print(ndarray1)
print(np.random.randint(10, 20))
print(np.random.randint(10, 20, 20))
print(np.random.randint(10, 20, (3, 4)))
[3 2 1 4 0]
[4 0 6 3 1 7 2 8 5 9]
[8 3 0 4 9 6 2 5 1 7]
15
[14 17 18 15 15 16 12 19 10 17 10 18 10 13 19 10 13 18 13 12]
[[19 19 19 18]
[16 13 13 15]
[17 11 17 13]]
6 数组排序函数
- 对一维数组排序
import numpy as np
ndarray1 = np.random.randint(1, 10, (1, 5))
print(ndarray1)
ndarray1.sort()
print(ndarray1)
[[7 5 1 4 1]]
[[1 1 4 5 7]]
- 对二维数组排序
ndarray2 = np.random.randint(1, 10, (5, 5))
print(ndarray2)
# 对每行数据进行排序
ndarray2.sort()
print(ndarray2)
# 对每列数据进行排序
ndarray2.sort(axis=0)
print(ndarray2)
ndarray3 = np.sort(ndarray2) # 返回排序副本,源数据不变
print(ndarray3)
[[6 6 5 6 4]
[8 5 2 2 1]
[2 1 5 3 6]
[3 5 4 5 9]
[7 5 8 4 3]]
[[4 5 6 6 6]
[1 2 2 5 8]
[1 2 3 5 6]
[3 4 5 5 9]
[3 4 5 7 8]]
[[1 2 2 5 6]
[1 2 3 5 6]
[3 4 5 5 8]
[3 4 5 6 8]
[4 5 6 7 9]]
[[1 2 2 5 6]
[1 2 3 5 6]
[3 4 5 5 8]
[3 4 5 6 8]
[4 5 6 7 9]]
- argsort函数(很重要) argsort函数返回的是数组值从小到大的索引值
import numpy as np
x = np.arange(10)
print(x)
np.random.shuffle(x)
print(x)
print(np.argsort(x))
[0 1 2 3 4 5 6 7 8 9]
[2 9 7 6 8 3 0 1 5 4]
[6 7 0 5 9 8 3 2 4 1]
7 广播
广播(broadcasting)指的是不同形状的数组之间的算术运算的执行方式。它是一种非常强大的功能,但也容易令人误解,即使是经验丰富的老手也是如此。将标量值跟数组合并时就会发生最简单的广播:
import numpy as np
arr = np.arange(5)
print(arr)
print(arr * 4)
[0 1 2 3 4]
[ 0 4 8 12 16]
这里我们说:在这个乘法运算中,标量值4被广播到了其他所有的元素上。
看一个例子,我们可以通过减去列平均值的方式对数组的每一列进行距平化处理。这个问题解决起来非常简单:
arr = np.random.randn(4, 3)
print(arr)
print(arr.mean(0))
demeaned = arr - arr.mean(0)
print(demeaned)
print(demeaned.mean(0))
[[ 0.80034723 1.32912842 -0.68108329]
[ 0.36787938 1.46662177 -0.0787044 ]
[ 0.20019622 -0.52519415 -1.22136111]
[ 0.00911344 0.36198295 -0.80211758]]
[ 0.34438407 0.65813475 -0.69581659]
[[ 0.45596316 0.67099367 0.0147333 ]
[ 0.02349531 0.80848702 0.6171122 ]
[-0.14418785 -1.18332889 -0.52554451]
[-0.33527063 -0.2961518 -0.10630099]]
[ 2.77555756e-17 -4.16333634e-17 8.32667268e-17]
下图形象地展示了该过程。用广播的方式对行进行距平化处理会稍微麻烦一些。幸运的是,只要遵循一定的规则,低维度的值是可以被广播到数组的任意维度的(比如对二维数组各列减去行平均值)。
画张图并想想广播的原则。再来看一下最后那个例子,假设你希望对各行减去那个平均值。由于arr.mean(0)的长度为3,所以它可以在0轴向上进行广播:因为arr的后缘维度是3,所以它们是兼容的。根据该原则,要在1轴向上做减法(即各行减去行平均值),较小的那个数组的形状必须是(4,1):
print(arr)
row_means = arr.mean(1)
print(row_means)
print(row_means.shape)
print(row_means.reshape((4, 1)))
demeaned = arr - row_means.reshape((4, 1))
print(demeaned)
print(demeaned.mean(1))
[[-1.09267495 0.26367303 -1.62088787]
[-0.7179292 0.70332572 -1.02413652]
[-0.31145341 0.12550098 0.78108721]
[ 1.76536402 -0.06664222 0.3858461 ]]
[-0.81662993 -0.34624667 0.19837826 0.69485597]
(4,)
[[-0.81662993]
[-0.34624667]
[ 0.19837826]
[ 0.69485597]]
[[-0.27604502 1.08030296 -0.80425794]
[-0.37168253 1.04957238 -0.67788985]
[-0.50983167 -0.07287728 0.58270895]
[ 1.07050805 -0.76149819 -0.30900986]]
[ 0.00000000e+00 3.70074342e-17 -3.70074342e-17 1.85037171e-17]
- 下图说明了该运算的过程
下图展示了另外一种情况,这次是在一个三维数组上沿0轴向加上一个二维数组。