Numpy学习
ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。
用于对整组数据进行快速运算的标准数学函数(无需编写循环)。
用于读写磁盘数据的工具以及用于操作内存映射文件的工具。
线性代数、随机数生成以及傅里叶变换功能。
用于集成由C、C++、Fortran等语言编写的代码的A C API。
NumPy本身并没有提供多么高级的数据分析功能,理解NumPy数组以及面向数组的计算将有助于你更加高效地使用诸如pandas之类的工具。
对于大部分数据分析应用而言,我最关注的功能主要集中在:
用于数据整理和清理、子集构造和过滤、转换等快速的矢量化数组运算。
常用的数组算法,如排序、唯一化、集合运算等。
高效的描述统计和数据聚合/摘要运算。
用于异构数据集的合并/连接运算的数据对齐和关系型数据运算。
将条件逻辑表述为数组表达式(而不是带有if-elif-else分支的循环)。
数据的分组运算(聚合、转换、函数应用等)。。
NumPy之于数值计算特别重要的原因之一,是因为它可以高效处理大数组的数据。
import numpy as np
my_arr = np.arange(100000)
my_list = list(range(100000))
my_arr
array([ 0, 1, 2, ..., 99997, 99998, 99999])
%time for _ in range(10): my_arr2 = my_arr * 2
Wall time: 2 ms
%time for _ in range(10): my_list2 = [x * 2 for x in my_list]
Wall time: 94.3 ms
data = np.random.randn(2,3)
print(data)
[[-0.28954731 -0.29662115 0.58404305]
[-0.9211746 -0.53641503 0.76443805]]
print('data * 10: \n', data * 10)
data * 10:
[[-2.8954731 -2.96621145 5.84043046]
[-9.21174598 -5.36415029 7.64438049]]
print('data + data : \n', data + data)
data + data :
[[-0.57909462 -0.59324229 1.16808609]
[-1.8423492 -1.07283006 1.5288761 ]]
print(data.shape)
(2, 3)
创建ndarray
创建数组最简单的方式就是使用array函数
data = [3,5,6,2,4]
data
[3, 5, 6, 2, 4]
arr1 = np.array(data)
arr1
array([3, 5, 6, 2, 4])
嵌套序列,当嵌套比较长的数组的时候,将会被替换为一个多维数组
data2 = [[1.,3.,5.,54.],[5.,58.,3.,1.]]
arr2 = np.array(data2)
arr2
array([[ 1., 3., 5., 54.],
[ 5., 58., 3., 1.]])
arr2.ndim,arr2.shape
(2, (2, 4))
arr1.ndim
1
arr1.shape
(5,)
arr1.dtype,arr2.dtype
(dtype('int32'), dtype('float64'))
此处学习的知识类似于tensorflow的内容,只不过将tf. 换成了np. 类似相同的操作省略(PS:在学习tensorflow的时候说过,nump类与tensorflow但是numpy无法进行张量的计算,因此无法将numpy用于深度学习)
参考
作者:SeanCheney
链接:https://www.jianshu.com/p/a380222a3292
來源:简书
Numpy基础:数组和矢量计算
NumPy(Numerical Python的简称)是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。
NumPy的部分功能如下:
ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。
用于对整组数据进行快速运算的标准数学函数(无需编写循环)。
用于读写磁盘数据的工具以及用于操作内存映射文件的工具。
线性代数、随机数生成以及傅里叶变换功能。
用于集成由C、C++、Fortran等语言编写的代码的A C API。
NumPy本身并没有提供多么高级的数据分析功能,理解NumPy数组以及面向数组的计算将有助于你更加高效地使用诸如pandas之类的工具。
对于大部分数据分析应用而言,我最关注的功能主要集中在:
用于数据整理和清理、子集构造和过滤、转换等快速的矢量化数组运算。
常用的数组算法,如排序、唯一化、集合运算等。
高效的描述统计和数据聚合/摘要运算。
用于异构数据集的合并/连接运算的数据对齐和关系型数据运算。
将条件逻辑表述为数组表达式(而不是带有if-elif-else分支的循环)。
数据的分组运算(聚合、转换、函数应用等)。。
NumPy之于数值计算特别重要的原因之一,是因为它可以高效处理大数组的数据。
1
import numpy as np
2
3
my_arr = np.arange(1000000)
4
my_list = list(range(1000000))
%time for _ in range(10): my_arr2 = my_arr * 2
1
%time for _ in range(10): my_arr2 = my_arr * 2
CPU times: user 14.1 ms, sys: 4.9 ms, total: 19 ms
Wall time: 17.6 ms
%time for _ in range(10): my_list2 = [x * 2 for x in my_list]
1
%time for _ in range(10): my_list2 = [x * 2 for x in my_list]
CPU times: user 623 ms, sys: 155 ms, total: 778 ms
Wall time: 776 ms
基于NumPy的算法要比纯Python快10到100倍(甚至更快),并且使用的内存更少。
1.1 NumPy的ndarray:一种多维数组对象
NumPy最重要的一个特点就是其N维数组对象(即ndarray),该对象是一个快速而灵活的大数据集容器。你可以利用这种数组对整块数据执行一些数学运算,其语法跟标量元素之间的运算一样。
1
import numpy as np
2
3
Generate some random data
4
data = np.random.randn(2, 3)
5
print(data)
[[ 0.47143516 -1.19097569 1.43270697]
[-0.3126519 -0.72058873 0.88716294]]
数学运算:
print(‘data * 10: \n’, data * 10)
1
#所有元素都乘以10
2
print(‘data * 10: \n’, data * 10)
data * 10:
[[ 3.49964027 -9.34359692 -19.15551097]
[ 14.80999025 7.5237491 12.60281505]]
print(‘data + data : \n’, data + data)
1
#每个元素与自身相加
2
print(‘data + data : \n’, data + data)
data + data :
[[ 0.69992805 -1.86871938 -3.83110219]
[ 2.96199805 1.50474982 2.52056301]]
ndarray所有元素必须是相同类型的。每个数组都有一个shape(一个表示各维度大小的元组)和一个dtype(一个用于说明数组数据类型的对象):
1
print(‘data shape:’, data.shape)
2
print(‘data dtype:’, data.dtype)
data shape: (2, 3)
data dtype: float64
1.1.1 创建ndarray
回目录
创建数组最简单的办法就是使用array函数。
1
data1 = [6, 7.5, 8, 0, 1]
2
arr1 = np.array(data1)
3
print(arr1)
[ 6. 7.5 8. 0. 1. ]
嵌套序列(比如由一组等长列表组成的列表)将会被转换为一个多维数组:
1
data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
2
arr2 = np.array(data2)
3
print(arr2)
[[1 2 3 4]
[5 6 7 8]]
因为data2是列表的列表,NumPy数组arr2的两个维度的shape是从data2引入的。可以用属性ndim和shape验证:
1
print(arr2.ndim)
2
print(arr2.shape)
2
(2, 4)
除非特别说明(稍后将会详细介绍),np.array会尝试为新建的这个数组推断出一个较为合适的数据类型。数据类型保存在一个特殊的dtype对象中。比如说,在上面的两个例子中,我们有:
1
print(arr1.dtype)
2
print(arr2.dtype)
float64
int64
除np.array之外,还有一些函数也可以新建数组。比如,zeros和ones分别可以创建指定长度或形状的全0或全1数组。empty可以创建一个没有任何具体值的数组。要用这些方法创建多维数组,只需传入一个表示形状的元组即可:
1
print(np.zeros(10))
2
print(np.zeros((3, 6)))
3
print(np.empty((2, 3, 2)))
[ 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.]]
[[[ 4.64908297e-310 0.00000000e+000]
[ 0.00000000e+000 0.00000000e+000]
[ 0.00000000e+000 0.00000000e+000]]
[[ 0.00000000e+000 0.00000000e+000]
[ 0.00000000e+000 0.00000000e+000]
[ 0.00000000e+000 0.00000000e+000]]]
注意:认为np.empty会返回全0数组的想法是不安全的。很多情况下(如前所示),它返回的都是一些未初始化的垃圾值。
arange是Python内置函数range的数组版:
1
print(np.arange(15))
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
下图列出了一些数组创建函数。由于NumPy关注的是数值计算,因此,如果没有特别指定,数据类型基本都是float64(浮点数)。
GitHub
empty, empty_like 说明更正: 分配内存空间产生随机值,不进行初始化
1.1.2 ndarray的数据类型
回目录
dtype(数据类型)是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息:
1
arr1 = np.array([1, 2, 3], dtype=np.float64)
2
arr2 = np.array([1, 2, 3], dtype=np.int32)
3
4
print(arr1.dtype)
5
print(arr2.dtype)
float64
int32
笔记:记不住这些NumPy的dtype也没关系,新手更是如此。通常只需要知道你所处理的数据的大致类型是浮点数、复数、整数、布尔值、字符串,还是普通的Python对象即可。当你需要控制数据在内存和磁盘中的存储方式时(尤其是对大数据集),那就得了解如何控制存储类型
GitHub
GitHub
你可以通过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
arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
print(arr)
print(arr.astype(np.int32))
[ 3.7 -1.2 -2.6 0.5 12.9 10.1]
[ 3 -1 -2 0 12 10]
numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
print(numeric_strings.astype(float))
[ 1.25 -9.6 42. ]
empty_uint32 = np.empty(8, dtype='u4')
print(empty_uint32)
print(empty_uint32.dtype)
[3885965360 47 3916807904 47 0 0
0 0]
uint32
numpy数据的运算
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.]]
1.1.4 数组的广播
如果两个数组的维数不相同,则元素到元素的操作是不可能的。 然而,在 NumPy 中仍然可以对形状不相似的数组进行操作,因为它拥有广播功能。 较小的数组会广播到较大数组的大小,以便使它们的形状可兼容。
如果满足以下规则,可以进行广播:
ndim较小的数组会在前面追加一个长度为 1 的维度。
输出数组的每个维度的大小是输入数组该维度大小的最大值。
如果输入在每个维度中的大小与输出大小匹配,或其值正好为 1,则可以在计算中使用该输入。
如果输入的某个维度大小为 1,则该维度中的第一个数据元素将用于该维度的所有计算。
如果上述规则产生有效结果,并且满足以下条件之一,那么数组被称为可广播的。
数组拥有相同形状。
数组拥有相同的维数,每个维度拥有相同长度,或者长度为 1。
数组拥有极少的维度,可以在其前面追加长度为 1 的维度,使上述条件成立。
下面的例称展示了广播的示例。
import numpy as np
a = np.array([[0.0,0.0,0.0],[10.0,10.0,10.0],[20.0,20.0,20.0],[30.0,30.0,30.0]])
b = np.array([1.0,2.0,3.0])
print ('第一个数组:')
print (a)
print ('\n第二个数组:')
print (b)
print ('\n第一个数组加第二个数组:')
print (a + b)
第一个数组:
[[ 0. 0. 0.]
[10. 10. 10.]
[20. 20. 20.]
[30. 30. 30.]]
第二个数组:
[1. 2. 3.]
第一个数组加第二个数组:
[[ 1. 2. 3.]
[11. 12. 13.]
[21. 22. 23.]
[31. 32. 33.]]
基本的索引和切片
NumPy数组的索引是一个内容丰富的主题,因为选取数据子集或单个元素的方式有很多。一维数组很简单。从表面上看,它们跟Python列表的功能差不多
arr = np.arange(10)
print (arr)
#arr的第6个元素
print (arr[5])
#arr的第6-8个元素,[5:8]左闭右开
print (arr[5:8])
arr[5:8] = 12
print (arr)
[0 1 2 3 4 5 6 7 8 9]
5
[5 6 7]
[ 0 1 2 3 4 12 12 12 8 9]
切片索引
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = np.random.randn(7, 4)
print(names)
print(data)
['Bob' 'Joe' 'Will' 'Bob' 'Will' 'Joe' 'Joe']
[[ 0.3997765 -1.53068864 -1.09492903 -0.554898 ]
[ 1.25732523 -0.0449066 0.37326997 1.86182256]
[ 0.17921655 -0.06763216 -1.29227148 0.73513092]
[ 1.3832531 1.0755744 -0.1874024 -0.63690547]
[-0.15871331 -1.28097077 -0.86279605 -1.06246442]
[ 0.27578069 1.04421658 -0.27471847 0.50724348]
[ 0.15435092 -0.02022786 0.71175442 -2.34395191]]
print(names == 'Bob')
[ True False False True False False False]
print(data[names == 'Bob'])
[[ 0.3997765 -1.53068864 -1.09492903 -0.554898 ]
[ 1.3832531 1.0755744 -0.1874024 -0.63690547]]
print(data[names == 'Bob', 2:])
print('===========分割线============')
print(data[names == 'Bob', 3])
[[-1.09492903 -0.554898 ]
[-0.1874024 -0.63690547]]
===========分割线============
[-0.554898 -0.63690547]
print(names != 'Bob')
print(data[~(names == 'Bob')])
[False True True False True True True]
[[ 1.25732523 -0.0449066 0.37326997 1.86182256]
[ 0.17921655 -0.06763216 -1.29227148 0.73513092]
[-0.15871331 -1.28097077 -0.86279605 -1.06246442]
[ 0.27578069 1.04421658 -0.27471847 0.50724348]
[ 0.15435092 -0.02022786 0.71175442 -2.34395191]]
cond = names == 'Bob'
print(data[~cond])
[[ 1.25732523 -0.0449066 0.37326997 1.86182256]
[ 0.17921655 -0.06763216 -1.29227148 0.73513092]
[-0.15871331 -1.28097077 -0.86279605 -1.06246442]
[ 0.27578069 1.04421658 -0.27471847 0.50724348]
[ 0.15435092 -0.02022786 0.71175442 -2.34395191]]
print(cond)
[ True False False True False False False]
mask = (names == 'Bob') | (names == 'Will')
print(mask)
print(data[mask])
[ True False True True True False False]
[[ 0.3997765 -1.53068864 -1.09492903 -0.554898 ]
[ 0.17921655 -0.06763216 -1.29227148 0.73513092]
[ 1.3832531 1.0755744 -0.1874024 -0.63690547]
[-0.15871331 -1.28097077 -0.86279605 -1.06246442]]
data[data < 0] = 0
print (data)
[[0.3997765 0. 0. 0. ]
[1.25732523 0. 0.37326997 1.86182256]
[0.17921655 0. 0. 0.73513092]
[1.3832531 1.0755744 0. 0. ]
[0. 0. 0. 0. ]
[0.27578069 1.04421658 0. 0.50724348]
[0.15435092 0. 0.71175442 0. ]]
data[names != 'Joe'] = 7
print(data)
[[7. 7. 7. 7. ]
[1.25732523 0. 0.37326997 1.86182256]
[7. 7. 7. 7. ]
[7. 7. 7. 7. ]
[7. 7. 7. 7. ]
[0.27578069 1.04421658 0. 0.50724348]
[0.15435092 0. 0.71175442 0. ]]
花式索引
花式索引(Fancy indexing)是一个NumPy术语,它指的是利用整数数组进行索引。假设我们有一个8×4数组:
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.]]
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(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)。无论数组是多少维的,花式索引总是一维的。
这个花式索引的行为可能会跟某些用户的预期不一样(包括我在内),选取矩阵的行列子集应该是矩形区域的形式才对。下面是得到该结果的一个办法:
记住,花式索引跟切片不一样,它总是将数据复制到新数组中。
arr = np.arange(16).reshape((2, 2, 4))
print(arr)
print('==========分割线==============')
print(arr.transpose((1, 0, 2)))
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]
==========分割线==============
[[[ 0 1 2 3]
[ 8 9 10 11]]
[[ 4 5 6 7]
[12 13 14 15]]]
print(arr)
print('==========分割线==============')
print(arr.swapaxes(1, 2))
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]
==========分割线==============
[[[ 0 4]
[ 1 5]
[ 2 6]
[ 3 7]]
[[ 8 12]
[ 9 13]
[10 14]
[11 15]]]
通用函数:快速的元素级数组函数
通用函数(即ufunc)是一种对ndarray中的数据执行元素级运算的函数。你可以将其看做简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。
arr = np.arange(10)
print(arr)
print(np.sqrt(arr))
print(np.exp(arr))
[0 1 2 3 4 5 6 7 8 9]
[0. 1. 1.41421356 1.73205081 2. 2.23606798
2.44948974 2.64575131 2.82842712 3. ]
[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]
x = np.random.randn(8)
y = np.random.randn(8)
print(x)
print(y)
print(np.maximum(x, y))
[ 0.25235702 0.35686332 0.20121512 0.54969688 -0.05527285 -0.55720887
-1.44575045 0.05621583]
[-1.06303531 -0.32091128 0.98493833 -1.4605705 -0.6758761 -0.96513467
-0.06080536 -0.75003987]
[ 0.25235702 0.35686332 0.98493833 0.54969688 -0.05527285 -0.55720887
-0.06080536 0.05621583]
arr = np.random.randn(7) * 5
print(arr)
remainder, whole_part = np.modf(arr)
print(remainder)
print(whole_part)
[ 7.30127894 2.67207512 -3.01115804 -2.80883044 1.79368568 -4.05182552
-0.14486631]
[ 0.30127894 0.67207512 -0.01115804 -0.80883044 0.79368568 -0.05182552
-0.14486631]
[ 7. 2. -3. -2. 1. -4. -0.]
print(arr)
print(np.sqrt(arr))
print(np.sqrt(arr, arr))
print(arr)
[ 7.30127894 2.67207512 -3.01115804 -2.80883044 1.79368568 -4.05182552
-0.14486631]
[2.70208789 1.63464832 nan nan 1.33928551 nan
nan]
[2.70208789 1.63464832 nan nan 1.33928551 nan
nan]
[2.70208789 1.63464832 nan nan 1.33928551 nan
nan]
D:\anacoda\lib\site-packages\ipykernel_launcher.py:2: RuntimeWarning: invalid value encountered in sqrt
D:\anacoda\lib\site-packages\ipykernel_launcher.py:3: RuntimeWarning: invalid value encountered in sqrt
This is separate from the ipykernel package so we can avoid doing imports until
利用数组进行数据处理
NumPy数组使你可以将许多种数据处理任务表述为简洁的数组表达式(否则需要编写循环)。用数组表达式代替循环的做法,通常被称为矢量化。
作为简单的例子,假设我们想要在一组值(网格型)上计算函数
√
x
2
+
y
2
。np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中所有的(x,y)对):
points = np.arange(-5, 5, 0.01) # 1000 equally spaced points
xs, ys = np.meshgrid(points, points)
print(ys)
[[-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)
print(z)
[[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]]
import matplotlib.pyplot as plt
%matplotlib inline
plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
Text(0.5,1,'Image plot of $\\sqrt{x^2 + y^2}$ for a grid of values')
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kK9FWXjn-1587560232189)(output_72_1.png)]
将条件逻辑表述为数组运算
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)]
print(result)
[1.1, 2.2, 1.3, 1.4, 2.5]
result = np.where(cond, xarr, yarr)
print(result)
[1.1 2.2 1.3 1.4 2.5]
np.where的第二个和第三个参数不必是数组,它们都可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新的数组。假设有一个由随机数据组成的矩阵,你希望将所有正值替换为2,将所有负值替换为-2。若利用np.where,则会非常简单:
arr = np.random.randn(4, 4)
print(arr)
print(arr > 0)
print(np.where(arr > 0, 2, -2))
[[ 2.68281773 -0.43397201 0.49127715 -0.10659935]
[ 1.20704553 -0.55379862 0.19870646 -1.69793851]
[-0.7808266 -0.47681634 0.33456219 0.24913557]
[ 1.43053078 -0.24786729 -0.80754771 0.03962654]]
[[ True False True False]
[ True False True False]
[False False True True]
[ True False False True]]
[[ 2 -2 2 -2]
[ 2 -2 2 -2]
[-2 -2 2 2]
[ 2 -2 -2 2]]
print(np.where(arr > 0, 2, arr))
[[ 2. -0.43397201 2. -0.10659935]
[ 2. -0.55379862 2. -1.69793851]
[-0.7808266 -0.47681634 2. 2. ]
[ 2. -0.24786729 -0.80754771 2. ]]
数学和统计方法
可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算。sum、mean以及标准差std等聚合计算(aggregation,通常叫做约简(reduction))既可以当做数组的实例方法调用,也可以当做顶级NumPy函数使用。
这里,我生成了一些正态分布随机数据,然后做了聚类统计:
arr = np.random.randn(5, 4)
print(arr)
print(arr.mean())
print(np.mean(arr))
print(arr.sum())
[[ 0.99930364 1.23763046 0.79594841 0.3483037 ]
[-0.42317289 -0.8192446 0.07223938 -0.478605 ]
[ 1.76104094 0.93452814 0.42460679 2.43097909]
[ 0.39252476 0.00484597 0.47047221 -0.39745436]
[ 2.01072761 0.64520802 -1.28723348 1.24992831]]
0.5186288542085322
0.5186288542085322
10.372577084170645
mean和sum这类的函数可以接受一个axis选项参数,用于计算该轴向上的统计值,最终结果是一个少一维的数组:
print(arr.mean(axis=1))
print(arr.sum(axis=0))
[ 0.84529655 -0.41219578 1.38778874 0.11759715 0.65465762]
[4.74042405 2.00296799 0.47603332 3.15315173]
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
print(arr.cumsum())
[ 0 1 3 6 10 15 21 28]
其他如cumsum和cumprod之类的方法则不聚合,而是产生一个由中间结果组成的数组:
在多维数组中,累加函数(如cumsum)返回的是同样大小的数组,但是会根据每个低维的切片沿着标记轴计算部分聚类:
arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(arr)
print(arr.cumsum(axis=0))#累加
print(arr.cumprod(axis=1))#累乘
[[0 1 2]
[3 4 5]
[6 7 8]]
[[ 0 1 2]
[ 3 5 7]
[ 9 12 15]]
[[ 0 0 0]
[ 3 12 60]
[ 6 42 336]]
列出了全部的基本数组统计方法
在上面这些方法中,布尔值会被强制转换为1(True)和0(False)。因此,sum经常被用来对布尔型数组中的True值计数:
arr = np.random.randn(100)
print((arr > 0).sum())
57
arr
array([ 7.10901085e-01, 1.77622306e-01, -1.91198443e+00, 1.61504555e-01,
1.25888396e+00, -9.52603547e-01, -2.66610103e-01, -1.11596420e+00,
1.47749668e+00, 1.85798755e+00, -4.15420603e-01, 2.04147891e-01,
9.06687543e-01, -4.55577442e-02, 3.20289058e-01, -9.80937700e-01,
3.68079683e-01, -1.38492201e+00, 1.58834590e-01, -8.86635705e-01,
-3.18074311e-01, 1.01916303e+00, 1.48966706e-01, 9.92930779e-01,
7.03820771e-01, -5.09525339e-01, 4.14174313e-01, 1.98137568e-01,
-4.23298792e-01, 1.92041534e-01, 3.41135646e-01, -1.74396340e+00,
7.14754239e-01, -7.65791663e-02, 2.70027046e-02, -2.01886683e-01,
9.98872392e-01, -2.62496461e+00, -1.71401346e-01, -1.50810761e+00,
9.99124880e-01, 1.42273567e+00, -8.70551431e-01, -1.78196745e+00,
1.30770571e-02, -1.11611226e+00, 1.32801542e+00, 2.44464010e-02,
2.68716735e-01, -1.21566185e+00, 1.77925631e+00, 2.24527246e+00,
1.50294800e+00, -2.12703422e+00, -1.64680780e+00, 1.71952650e+00,
6.28574209e-01, -3.10130199e-01, 2.36964581e+00, 4.93815045e-01,
1.80454370e+00, 5.21551657e-01, 7.79036918e-01, -7.97883369e-01,
2.47653632e-01, 5.01085277e-01, -1.61804941e+00, 7.77101844e-01,
1.93004880e-01, 6.16916144e-01, 2.62360728e-01, -3.94399507e-01,
-9.45857106e-01, -9.58680460e-01, 1.35879187e+00, 1.50581176e+00,
-1.43326991e-01, -4.69343760e-01, -1.90310621e+00, 5.37978936e-01,
6.17251543e-01, 3.42605452e-01, 1.83733706e-01, 1.65498310e+00,
8.34219058e-01, -2.73210747e-01, 9.86652619e-01, -1.02144173e-01,
1.19955885e+00, -2.48838962e-03, -1.18998414e+00, -1.05944636e-01,
-2.50698959e+00, -7.57903621e-01, -7.84615227e-01, 1.20479665e+00,
1.26198845e+00, -6.07932762e-01, -2.03491489e+00, 3.53821707e-01])
另外还有两个方法any和all,它们对布尔型数组非常有用。any用于测试数组中是否存在一个或多个True,而all则检查数组中所有值是否都是True:
bools = np.array([False, False, True, False])
print(bools.any())
print(bools.all())
True
False
排序
umPy中提供了各种排序相关功能。 这些排序函数实现不同的排序算法,每个排序算法的特征在于执行速度,最坏情况性能,所需的工作空间和算法的稳定性。 下表显示了三种排序算法的比较。
quicksort’(快速排序) 1 O(n^2) 0 否
‘mergesort’(归并排序) 2 O(nlog(n)) ~n/2 是
‘heapsort’(堆排序) 3 O(nlog(n)) 0 否
sort()函数返回输入数组的排序副本。 它有以下参数:
numpy.sort(a, axis, kind, order)
1 a 要排序的数组
2 axis 沿着它排序数组的轴,如果没有数组会被展开,沿着最后的轴排序
3 kind 默认为’quicksort’(快速排序)
4 order 如果数组包含字段,则是要排序的字段
import numpy as np
a = np.array([[3,7],[9,1]])
print ('我们的数组是:')
print (a)
print ('\n')
print ('调用 sort() 函数:')
print (np.sort(a))
print ('\n')
print ('沿轴 0 排序:')
print (np.sort(a, axis = 0))
print ('\n')
# 在 sort 函数中排序字段
dt = np.dtype([('name', 'S10'),('age', int)])
a = np.array([("raju",21),("anil",25),("ravi", 17), ("amar",27)], dtype = dt)
print ('我们的数组是:')
print (a)
print ('\n')
print ('按 name 排序:')
print (np.sort(a, order = 'name'))
我们的数组是:
[[3 7]
[9 1]]
调用 sort() 函数:
[[3 7]
[1 9]]
沿轴 0 排序:
[[3 1]
[9 7]]
我们的数组是:
[(b'raju', 21) (b'anil', 25) (b'ravi', 17) (b'amar', 27)]
按 name 排序:
[(b'amar', 27) (b'anil', 25) (b'raju', 21) (b'ravi', 17)]
numpy.argsort()函数对输入数组沿给定轴执行间接排序,并使用指定排序类型返回数据的索引数组。 这个索引数组用于构造排序后的数组
arr = np.random.randn(5, 3)
print(arr)
arr.sort(1)
print(arr)
[[ 1.24674822 0.49028899 -0.14890733]
[ 0.95758372 -0.44144965 -0.66760137]
[ 0.2497239 -1.69269346 0.29456968]
[-0.97020605 0.45252851 1.74130043]
[ 0.80024861 -0.52047589 0.42323737]]
[[-0.14890733 0.49028899 1.24674822]
[-0.66760137 -0.44144965 0.95758372]
[-1.69269346 0.2497239 0.29456968]
[-0.97020605 0.45252851 1.74130043]
[-0.52047589 0.42323737 0.80024861]]
import numpy as np
x = np.array([3, 1, 2])
print ('我们的数组是:')
print (x)
print ('\n')
print ('对 x 调用 argsort() 函数:' )
y = np.argsort(x)
print (y)
print ('\n')
print ('以排序后的顺序重构原数组:')
print (x[y])
print ('\n')
print ('使用循环重构原数组:')
for i in y:
print (x[i])
我们的数组是:
[3 1 2]
对 x 调用 argsort() 函数:
[1 2 0]
以排序后的顺序重构原数组:
[1 2 3]
使用循环重构原数组:
1
2
3
numpy.lexsort()函数使用键序列执行间接排序。 键可以看作是电子表格中的一列。 该函数返回一个索引数组,使用它可以获得排序数据。 注意,最后一个键恰好是 sort 的主键。
import numpy as np
nm = ('raju','anil','ravi','amar')
dv = ('f.y.', 's.y.', 's.y.', 'f.y.')
ind = np.lexsort((dv,nm))
print ('调用 lexsort() 函数:' )
print (ind)
print ('\n')
print ('使用这个索引来获取排序后的数据:')
print ([nm[i] + ", " + dv[i] for i in ind])
调用 lexsort() 函数:
[3 1 0 2]
使用这个索引来获取排序后的数据:
['amar, f.y.', 'anil, s.y.', 'raju, f.y.', 'ravi, s.y.']
唯一化以及其它的集合逻辑NumPy提供了一些针对一维ndarray的基本集合运算。最常用的可能要数np.unique了,它用于找出数组中的唯一值并返回已排序的结果:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
print(np.unique(names))
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
print(np.unique(ints))
['Bob' 'Joe' 'Will']
[1 2 3 4]
拿跟np.unique等价的纯Python代码来对比一下:
print(sorted(set(names)))
['Bob', 'Joe', 'Will']
values = np.array([6, 0, 0, 3, 2, 5, 6])
print(np.in1d(values, [2, 3, 6]))
[ True False False True True False True]
用于数组的文件输入输出NumPy能够读写磁盘上的文本数据或二进制数据。np.save和np.load是读写磁盘数组数据的两个主要函数。默认情况下,数组是以未压缩的原始二进制格式保存在扩展名为.npy的文件中的:
arr = np.arange(10)
np.save("F:\jupyter",'some_array', arr)
print(np.load('some_array.npy'))
[0 1 2 3 4 5 6 7 8 9]
np.savez('array_archive.npz', a=arr, b=arr)
arch = np.load('array_archive.npz')
print(arch['b'])
[0 1 2 3 4 5 6 7 8 9]
np.savez_compressed('arrays_compressed.npz', a=arr, b=arr)
线性代数
线性代数(如矩阵乘法、矩阵分解、行列式以及其他方阵数学等)是任何数组库的重要组成部分。不像某些语言(如MATLAB),通过*对两个二维数组相乘得到的是一个元素级的积,而不是一个矩阵点积。因此,NumPy提供了一个用于矩阵乘法的dot函数(既是一个数组方法也是numpy命名空间中的一个函数):
import numpy as np
x = np.array([[1., 2., 3.], [4., 5., 6.]])
y = np.array([[6., 23.], [-1, 7], [8, 9]])
print(x)
print(y)
print(x.dot(y))
[[1. 2. 3.]
[4. 5. 6.]]
[[ 6. 23.]
[-1. 7.]
[ 8. 9.]]
[[ 28. 64.]
[ 67. 181.]]
print(np.dot(x, y))
[[ 28. 64.]
[ 67. 181.]]
print(np.ones(3))
print(np.dot(x, np.ones(3)))
[1. 1. 1.]
[ 6. 15.]
print (x @ np.ones(3))
[ 6. 15.]
from numpy.linalg import inv, qr
X = np.random.randn(5, 5)
mat = X.T.dot(X)
print(inv(mat))
print(mat.dot(inv(mat)))
q, r = qr(mat)
print(r)
[[271.47906831 274.37534474 -21.51365307 287.55690654 53.93798959]
[274.37534474 277.70009831 -21.59647147 290.81037838 54.57215398]
[-21.51365307 -21.59647147 1.88866027 -22.74917074 -4.21886149]
[287.55690654 290.81037838 -22.74917074 304.99863895 57.1233809 ]
[ 53.93798959 54.57215398 -4.21886149 57.1233809 10.87129913]]
[[ 1.00000000e+00 -6.10053666e-14 -2.21901530e-15 3.56189253e-14
9.47685420e-15]
[-6.17924772e-15 1.00000000e+00 9.52732834e-15 9.56210576e-15
-6.77825946e-15]
[-2.32271404e-14 -1.44327086e-13 1.00000000e+00 -2.39653849e-14
-2.41656822e-14]
[-1.69035523e-14 -2.28322791e-15 -6.25713917e-15 1.00000000e+00
4.56791333e-15]
[-7.74431985e-14 -3.84555791e-14 4.09719175e-15 -1.71566980e-13
1.00000000e+00]]
[[-7.26219436 4.7369373 -7.99229446 1.08955493 3.4458952 ]
[ 0. -4.46715238 2.5411863 3.75327886 3.72535848]
[ 0. 0. -4.89311672 -0.85566433 2.61729891]
[ 0. 0. 0. -1.01228884 5.39799857]
[ 0. 0. 0. 0. 0.01037706]]
随机数生成
numpy.random模块对Python内置的random进行了补充,增加了一些用于高效生成多种概率分布的样本值的函数。例如,你可以用normal来得到一个标准正态分布的4×4样本数组:
samples = np.random.normal(size=(4, 4))
print(samples)
[[-0.86782005 -0.11154011 0.62110773 1.44956341]
[-0.16616554 -0.24154141 -0.97800009 -0.25915035]
[ 0.46880547 0.01894926 0.59637654 -0.95527057]
[ 1.28874819 -0.78741081 -0.11987204 0.01750292]]
from random import normalvariate
N = 1000000
%timeit samples = [normalvariate(0, 1) for _ in range(N)]
%timeit np.random.normal(size=N)
1.13 s ± 52.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
42.9 ms ± 3.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
np.random.seed(1234)
rng = np.random.RandomState(1234)
print(rng.randn(10))
[ 0.47143516 -1.19097569 1.43270697 -0.3126519 -0.72058873 0.88716294
0.85958841 -0.6365235 0.01569637 -2.24268495]
随机漫步
我们通过模拟随机漫步来说明如何运用数组运算。先来看一个简单的随机漫步的例子:从0开始,步长1和-1出现的概率相等。
下面是一个通过内置的random模块以纯Python的方式实现1000步的随机漫步
import random
import matplotlib.pyplot as plt
%matplotlib inline
position = 0
walk = [position]
steps = 1000
for i in range(steps):
step = 1 if random.randint(0, 1) else -1
position += step
walk.append(position)
plt.plot(walk[:100])
[<matplotlib.lines.Line2D at 0x2ff0bc05f8>]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qNr4p2tz-1587560232196)(output_130_1.png)]
nsteps = 1000
draws = np.random.randint(0, 2, size=nsteps)
steps = np.where(draws > 0, 1, -1)
walk = steps.cumsum()
print(walk.min())
print(walk.max())
-9
60
print((np.abs(walk) >= 10).argmax())
297