NumPy 是 Python 科学计算的基础包。它是一个 Python 库,提供了一个多维数组对象、各种派生对象(例如掩码数组和矩阵),以及用于对数组进行快速操作的各种例程,包括数学、逻辑、形状操作、排序、选择、I/O 、离散傅立叶变换、基本线性代数、基本统计运算、随机模拟等等。
NumPy 包的核心是 ndarray 对象
。其封装了同类型数据的 n维数组,许多操作在编译代码中执行以提高性能。NumPy 数组和标准 Python 序列之间有几个重要的区别:
关于序列大小和速度的要点在科学计算中尤为重要。作为一个简单的例子,考虑将一维序列中的每个元素与另一个相同长度序列中的相应元素相乘的情况。如果数据存储在两个 Python 列表a和 中b,我们可以遍历每个元素:
c = []
for i in range(len(a)):
c.append(a[i]*b[i])
得出了正确的答案,但如果a和b每一个都包含数以百万计的数字,我们会因为 Python 中循环的的低效率而付出代价。我们可以在 C 中通过编写代码更快地完成相同的任务(为清楚起见,我们忽略了变量声明和初始化、内存分配等):
for (i = 0; i < rows; i++): {
c[i] = a[i]*b[i];
}
节省了解释 Python 代码和操作 Python 对象所涉及的所有开销,但代价是牺牲了使用 Python 编码所带来的好处。此外,所需的编码工作随着数据维度的增加而增加。例如,在二维数组的情况下,C 代码(如前所述缩写)扩展为:
for (i = 0; i < rows; i++): {
for (j = 0; j < columns; j++): {
c[i][j] = a[i][j]*b[i][j];
}
}
NumPy 为我们提供了两全其美的方法:当涉及 ndarray 时,逐元素操作是"默认模式",但逐元素操作由预编译的 C 代码快速执行。 在 NumPy 中:
c = a * b
以接近 C 的速度执行前面的示例所做的事情,而且代码的简单性又符合我们希望和 Python 一样。事实上,NumPy 习惯用法更简单!最后一个示例说明了 NumPy 的两个功能,这两个功能是其大部分强大功能的基础:矢量化和广播。
矢量化描述了代码中没有任何显式循环、索引等。当然,这些事情只是在优化、预编译 C 代码的"幕后"发生的。矢量化代码有很多优点,其中包括:
Pythonic
代码。如果没有矢量化,我们的代码将充斥着低效且难以阅读的for循环。广播是用于描述操作的隐式逐元素行为的术语;一般而言,在 NumPy 中,所有操作,不仅是算术运算,还有逻辑、按位、函数等,都以这种隐式的逐元素方式表现,即它们正在广播。此外,在上面的例子中,a和b可以是相同形状的多维数组,或者一个标量和一个数组,甚至两个不同形状的数组,前提是较小的数组可以"扩展"
到较大的数组中,由此产生的广播是明确的。
NumPy 完全支持面向对象的方法,再次从 ndarray 开始。例如,ndarray是一个类,拥有许多方法和属性。它的许多方法由最外层 NumPy 命名空间中的函数镜像,允许程序员以他们喜欢的任何样式进行编码。这种灵活性使 NumPy 数组方言和NumPy ndarray
类成为 Python 中使用的多维数据交换的事实上的语言。
安装 NumPy 的唯一先决条件是 Python 本身。如果你还没有 Python 并且想要以最简单的方式开始,我们建议使用 Anaconda Distribution
– 它包括 Python、NumPy 和许多其他用于科学计算和数据科学的常用包。
NumPy 可以通过conda、pip、 macOS 和 Linux 上的包管理器安装,或者从来源处安装。有关更详细的说明,请参阅下面的Python 和 NumPy 安装指南。
如果使用conda(Conda 是一个开源的软件包管理系统和环境管理系统,Conda 是为 Python 程序创建的,适用于 Linux,OS X 和Windows,也可以打包和分发其他软件),则可以用defaults或conda-forge 方法安装 NumPy :
# Best practice, use an environment rather than install in the base env
conda create -n my-env
conda activate my-env
# If you want to install from conda-forge
conda config --env --add channels conda-forge
# The actual install command
conda install numpy
如果使用pip,则可以使用以下命令安装 NumPy:
pip install numpy
此外,在使用 pip
时,最好使用虚拟环境 - 请参阅下面的可重复安装了解有关使用虚拟环境的详细信息。
在 Python 中安装和管理包很复杂,对于大多数任务,有许多替代解决方案。本指南将会帮助读者了解最佳(或最受欢迎)的解决方案,并给出明确的建议。它侧重于Python、NumPy 和 PyData(或数值计算)堆栈在常见操作系统和硬件上的用户。
我们将从基于用户体验水平和感兴趣的操作系统的建议开始。如果您介于"初级"和"高级"之间,且您只是要简单的了解一下,请选择"初级";如果您想根据未来走得更远的最佳实践工作,请选择"高级"。
在所有 Windows、macOS 和 Linux 上:
如果你觉得稍微过时的软件包没问题,并且更喜欢稳定性,可以不用直接去使用最新版本的库:
pip install somepackage --user
.如果使用 GPU:
管理包是一个具有挑战性的问题,因此有很多工具。对于 Web 和通用 Python 开发,有大量与 pip 互补的工具。对于高性能计算(HPC), Spack 值得考虑。不过对于大多数 NumPy 用户来说,conda 和 pip 是两个最流行的工具。
安装 Python 包的两个主要工具是 pip 和 conda。它们的功能部分重叠(例如,两者都可以安装numpy),但是,它们也可以一起工作。我们将在这里讨论 pip 和 conda 之间的主要区别 。如果想有效地管理包,这对于理解这一点很重要。
第一个区别是 conda 是跨语言的,它可以安装 Python,而 pip 是为系统上的特定 Python 安装的,并且仅将其他软件包安装到同一个 Python 安装中。这也意味着 conda 可以安装您可能需要的非 Python 库和工具(例如编译器、CUDA、HDF5),而 pip 不能。
第二个区别是 pip 从 Python Packaging Index (PyPI) 安装,而 conda 从它自己的渠道安装(通常是"defaults"或"conda-forge")。PyPI 是迄今为止最大的软件包集合,但是,所有流行的软件包也可用于 conda。
第三个区别是 conda 是用于管理包、依赖项和环境的集成解决方案,而使用 pip 您可能需要另一个工具(有很多!)来处理环境或复杂的依赖项。
对于那些从个人喜好或者了解了上面关于 conda 和 pip 之间的主要区别,确定自己更喜欢基于 pip/PyPI 的解决方案的用户,我们建议:
随着库的更新,运行代码的结果可能会发生变化,或者您的代码可能会完全中断。能够重建您正在使用的软件包和版本集非常重要。最佳做法是:
每个都有自己的元数据格式:
environment.yml
requirements.txt
pyproject.toml
NumPy 不依赖于任何其他 Python 包,但是,它依赖于加速线性代数库,通常是 Intel MKL或 OpenBLAS。用户不必担心安装这些(它们会自动包含在所有 NumPy 安装方法中)。高级用户可能仍然想知道详细信息,因为使用的 BLAS 会影响磁盘上的性能、行为和大小:
除了安装尺寸、性能和稳健性之外,还有两件事情需要考虑:
如果安装失败并显示以下消息,请参阅疑难解答 ImportError。
IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!
Importing the numpy c-extensions failed. This error can happen for
different reasons, often due to issues with your setup.
NumPy 的主要对象是同构多维数组。它是一个元素表(通常是数字),所有类型都相同,由非负整数元组索引。在 NumPy 中,维度称为轴。 例如,3D 空间中一个点的坐标[1, 2, 1]只有一个轴。该轴有 3 个元素,因此我们说它的长度为 3。在下图中的示例中,数组有 2 个轴。第一个轴的长度为 2,第二个轴的长度为 3。
[[1., 0., 0.],
[0., 1., 2.]].
NumPy 的数组类称为ndarray。它也被称为别名 array。请注意,numpy.array
这与标准 Python 库类不同array.array
,后者仅处理一维数组并提供较少的功能。ndarray对象更重要的属性是:
ndarray.ndim
数组的轴数(维度)。ndarray.shape
数组的维度。这是一个整数元组,象征着每个维度中数组的大小。对于具有n行和m列的矩阵,shape将为(n,m)
。因此,元组shape的长度是轴的数量,ndim。ndarray.dtype
描述数组中元素类型的对象。可以使用标准 Python 类型创建或指定 dtype。此外,NumPy 提供了自己的类型。numpy.int32
、numpy.int16
和 numpy.float64
是一些示例。ndarray.itemsize
数组每个元素的大小(以字节为单位)。例如,一个类型元素的数组float64有itemsize8
个(=64/8),而一个类型的元素complex32
有itemsize4
个(=32/8)。它相当于ndarray.dtype.itemsize
。ndarray.data
包含数组实际元素的缓冲区。通常,我们不需要使用此属性,因为我们将使用索引工具访问数组中的元素。import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'>
>>> import numpy as np
>>> a= np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> a.reshape(5,2)
array([[0, 1],
[2, 3],
[4, 5],
[6, 7],
[8, 9]])
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> a=a.reshape(2,5)
>>> a
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
>>> a=a.reshape(4,3) # 不能等分会报错
Traceback (most recent call last):
File "" , line 1, in <module>
ValueError: cannot reshape array of size 10 into shape (4,3)
>>> b=np.arange(10)
>>> b
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> b=b.reshape(4,3)
Traceback (most recent call last):
File "" , line 1, in <module>
ValueError: cannot reshape array of size 10 into shape (4,3)
>>> c=np.arange(12)
>>> c
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
>>> c=c.reshape(4,3)
>>> c
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
>>>
有几种方法可以创建数组。
例如,可以使用array函数从常规 Python 列表或元组创建数组。结果数组的类型是从序列中元素的类型推导出来的。
>>> import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64')
# array() 只需要一个实参,且数据类型必须是元组或列表
>>> c=np.array(1,2,3)
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: array() takes from 1 to 2 positional arguments but 3 were given
>>> c=np.array((1,2,3))
>>> c
array([1, 2, 3])
一个常见的错误在于array使用多个参数调用,而不是提供单个序列作为参数。
>>> a = np.array(1, 2, 3, 4) # WRONG
Traceback (most recent call last):
...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
>>> a = np.array([1, 2, 3, 4]) # RIGHT
array 将序列的序列转换为二维数组,将序列的序列的序列转换为三维数组,依此类推。
>>> b = np.array([(1.5, 2, 3), (4, 5, 6)])
>>> b
array([[1.5, 2. , 3. ],
[4. , 5. , 6. ]])
>>> d=np.array(([1,2],[3,4],[5,6]))
>>> d
array([[1, 2],
[3, 4],
[5, 6]])
>>> e=np.array(([1,2],[3,4],[5])) #
<stdin>:1: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
>>>
数组的类型也可以在创建时显式指定:
>>> c = np.array([[1, 2], [3, 4]], dtype=complex)
>>> c
array([[1.+0.j, 2.+0.j],
[3.+0.j, 4.+0.j]])
通常,数组的元素最初是未知的,但其大小是已知的。因此,NumPy 提供了几个函数来创建具有初始占位符内容的数组。这些最大限度地减少了增长阵列的必要性,这是一项昂贵的操作。
该函数zeros创建一个全零数组,该函数 ones创建一个全1数组,该函数empty 创建一个初始内容随机且取决于内存状态的数组。默认情况下,创建的数组的 dtype 是 float64,但可以通过关键字参数指定dtype。
>>> np.zeros((3, 4))
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> np.ones((2, 3, 4), dtype=np.int16)
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]]], dtype=int16)
>>> np.empty((2, 3))
array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260], # may vary
[5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])
为了创建数字序列,NumPy 提供了arange类似于 Python 内置的函数range,但返回一个数组。
>>> np.arange(10, 30, 5)
array([10, 15, 20, 25])
>>> np.arange(0, 2, 0.3) # it accepts float arguments
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])
当arange与浮点参数一起使用时,由于浮点精度有限,通常无法预测获得的元素数量。出于这个原因,通常最好使用linspace接收我们想要的元素数量作为参数的函数,而不是步骤:
>>> from numpy import pi
>>> np.linspace(0, 2, 9) # 9 numbers from 0 to 2
array([0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])
>>> x = np.linspace(0, 2 * pi, 100) # useful to evaluate function at lots of points
>>> f = np.sin(x)
打印数组时,NumPy 以类似于嵌套列表的方式显示它,但具有以下布局:
然后将一维数组打印为行,将二维打印为矩阵,将三维打印为矩阵列表。
>>> a = np.arange(6) # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4, 3) # 2d array
>>> print(b)
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
>>>
# reshape(2, 3, 4) 中的2是打印时均分为2个,3指三行,4指4列
>>> c = np.arange(24).reshape(2, 3, 4) # 3d array
>>> print(c)
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
>>> import numpy as np
>>> d=np.arange(24)
>>> d
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])
>>> d=d.reshape(3,2,4)
>>> d
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]]])
>>>
请参阅下文以获取有关reshape的更多详细信息。
如果数组太大而无法打印,NumPy 会自动跳过数组的中心部分,只打印角落:
>>> print(np.arange(10000))
[ 0 1 2 ... 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100, 100))
[[ 0 1 2 ... 97 98 99]
[ 100 101 102 ... 197 198 199]
[ 200 201 202 ... 297 298 299]
...
[9700 9701 9702 ... 9797 9798 9799]
[9800 9801 9802 ... 9897 9898 9899]
[9900 9901 9902 ... 9997 9998 9999]]
想要禁用此行为并强制 NumPy 打印整个数组,您可以使用set_printoptions.
>>> np.set_printoptions(threshold=sys.maxsize) # sys module should be imported
数组上的算术运算符按元素应用。创建一个新数组并填充结果。
>>> import numpy as np
>>> a=np.array([20,30,40,50])
>>> a
array([20, 30, 40, 50])
>>> b=np.arange(4)
>>> b
array([0, 1, 2, 3])
>>> a+b # 加法
array([20, 31, 42, 53])
>>> a-b # 减法
array([20, 29, 38, 47])
>>> a*b # 乘法
array([ 0, 30, 80, 150])
>>> a/b # 除法
<stdin>:1: RuntimeWarning: divide by zero encountered in divide
array([ inf, 30. , 20. , 16.66666667])
>>> a//b
<stdin>:1: RuntimeWarning: divide by zero encountered in floor_divide
array([ 0, 30, 20, 16])
除法的几种形式
/
相当于np.divide()
//
相当于np.floor_divide()
>>> import numpy as np
>>> a=np.array([20,30,40,50])
>>> a
array([20, 30, 40, 50])
>>> b=np.arange(1,5)
>>> b
array([1, 2, 3, 4])
>>> a/b
array([20. , 15. , 13.33333333, 12.5 ])
>>> np.divide(a,b)
array([20. , 15. , 13.33333333, 12.5 ])
>>> a//b
array([20, 15, 13, 12])
>>> np.floor_divide(a,b)
array([20, 15, 13, 12])
>>> np.true_divide(a,b)
array([20. , 15. , 13.33333333, 12.5 ])
与许多矩阵语言不同,乘积运算符*在 NumPy 数组中按元素进行运算。可以使用@运算符(在python>=3.5中)或dot函数或方法来执行矩阵乘积,规则如下图:
实例:
>>> A = np.array([[1, 1],
... [0, 1]])
>>> B = np.array([[2, 0],
... [3, 4]])
>>> A * B # elementwise product
array([[2, 0],
[0, 4]])
>>> A @ B # matrix product
array([[5, 4],
[3, 4]])
>>> A.dot(B) # another matrix product
array([[5, 4],
[3, 4]])
+=
*=
-=
//=
此类操作修改现有数组而不是创建新数组
>>> import numpy as np
>>> a = np.ones((2, 3), dtype=int)
>>> a
array([[1, 1, 1],
[1, 1, 1]])
>>> rg = np.random.default_rng(1)
>>> rg
Generator(PCG64) at 0x1075C5F20
>>> b = rg.random((2, 3))
>>> b
array([[0.51182162, 0.9504637 , 0.14415961],
[0.94864945, 0.31183145, 0.42332645]])
>>> a *= 3
>>> a
array([[3, 3, 3],
[3, 3, 3]])
>>> a -= 1
>>> a
array([[2, 2, 2],
[2, 2, 2]])
>>> b += a
>>> b
array([[2.51182162, 2.9504637 , 2.14415961],
[2.94864945, 2.31183145, 2.42332645]])
>>> a /= 2
Traceback (most recent call last):
File "" , line 1, in <module>
numpy.core._exceptions.UFuncTypeError: Cannot cast ufunc 'divide' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
>>> a //= 2
>>> a
array([[1, 1, 1],
[1, 1, 1]])
不同类型的数组不允许乘除
>>> a
array([[2, 2, 2],
[2, 2, 2]])
>>> a /= 2.0
Traceback (most recent call last):
File "" , line 1, in <module>
numpy.core._exceptions.UFuncTypeError: Cannot cast ufunc 'divide' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
>>> a *= 1.1
Traceback (most recent call last):
File "" , line 1, in <module>
numpy.core._exceptions.UFuncTypeError: Cannot cast ufunc 'multiply' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
当处理不同类型的数组时,结果数组的类型对应于更一般或更精确的类型(一种称为向上转换的行为)。
>>> import numpy as np
>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0, pi, 3)
Traceback (most recent call last):
File "" , line 1, in <module>
NameError: name 'pi' is not defined
>>> import math
>>> b = np.linspace(0, math.pi, 3)
>>> b
array([0. , 1.57079633, 3.14159265])
>>>
>>> a
array([1, 1, 1], dtype=int32)
>>> b.dtype.name
'float64'
>>> c = a+ b
>>> c
array([1. , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> d = np.exp(c * 1j) # np.exp(c* 1j) 复数运算
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
-0.54030231-0.84147098j])
>>> d.dtype.name
'complex128'
>>>
具体参考:https://blog.csdn.net/zl_1987/article/details/83238947
许多一元运算,例如计算数组中所有元素的总和,都是作为ndarray类的方法实现的。
>>> import numpy as np
>>> a = rg.random((2, 3))
Traceback (most recent call last):
File "" , line 1, in <module>
NameError: name 'rg' is not defined
>>> rg = np.random.default_rng(1)
File "" , line 1
rg = np.random.default_rng(1)
^
IndentationError: unexpected indent
>>> rg = np.random.default_rng(1)
>>> a = rg.random((2, 3))
>>> a
array([[0.51182162, 0.9504637 , 0.14415961],
[0.94864945, 0.31183145, 0.42332645]])
>>> a.sum()
3.290252281866131
>>> a.min()
0.14415961271963373
>>> a.max()
0.9504636963259353
通过指定axis 参数,可以沿数组的指定轴应用操作:
>>> import numpy as np
>>> b = np.arange(12).reshape(3, 4)
>>> b
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> b.sum(axis=0) # 列累加
array([12, 15, 18, 21])
>>> b.sum(axis=1) # 行累加
array([ 6, 22, 38])
>>> b.cumsum(axis=1) # 行当前元素与之前元素的累加
array([[ 0, 1, 3, 6],
[ 4, 9, 15, 22],
[ 8, 17, 27, 38]])
>>> b.cumsum(axis=0) # 列当前元素与之前元素的累加
array([[ 0, 1, 2, 3],
[ 4, 6, 8, 10],
[12, 15, 18, 21]])
>>>