CQF预备知识:Python相关库 -- NumPy 基础知识 - 数组创建
文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。
数组创建
另请参阅 数组创建例程
引言
创建数组有6种通用机制:
-
从其他Python结构转换(即列表和元组)。
-
使用NumPy内置的数组创建函数(例如
arange
、ones
、zeros
等)。 -
复制、连接或修改现有数组。
-
从磁盘读取数组,无论是标准格式还是自定义格式。
-
通过使用字符串或缓冲区从原始字节创建数组。
-
使用特殊库函数(例如
random
)。
您可以使用这些方法创建ndarray
或结构化数组。本文将涵盖ndarray
创建的一般方法。
1)将 Python 序列转换为 NumPy 数组
可以使用 Python 序列(如列表和元组)来定义 NumPy 数组。列表和元组分别使用[...]
和(...)
定义。列表和元组可用于定义ndarray
的创建:
-
数字列表将创建一维数组。
-
列表的列表将创建二维数组。
-
更多嵌套列表将创建更高维度的数组。一般来说,NumPy 中的任何数组对象都被称为
ndarray
。
>>> import numpy as np
>>> a1D = np.array([1, 2, 3, 4])
>>> a2D = np.array([[1, 2], [3, 4]])
>>> a3D = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
当您使用numpy.array
定义新数组时,您应该考虑数组中元素的dtype
,可以明确指定dtype
。此功能使您能够更好地控制底层数据结构以及在 C/C++ 函数中处理元素的方式。如果值超出范围且您使用了dtype
,NumPy 可能会引发错误:
>>> import numpy as np
>>> np.array([127, 128, 129], dtype=np.int8)
Traceback (most recent call last):
...
OverflowError: Python integer 128 out of bounds for int8
8 位有符号整数表示从 -128 到 127 的整数。将int8
数组分配给此范围之外的整数会导致溢出。此功能常常被误解。如果您使用不匹配的dtype
进行计算,可能会得到意外的结果,例如:
>>> import numpy as np
>>> a = np.array([2, 3, 4], dtype=np.uint32)
>>> b = np.array([5, 6, 7], dtype=np.uint32)
>>> c_unsigned32 = a - b
>>> print('unsigned c:', c_unsigned32, c_unsigned32.dtype)
unsigned c: [4294967293 4294967293 4294967293] uint32
>>> c_signed32 = a - b.astype(np.int32)
>>> print('signed c:', c_signed32, c_signed32.dtype)
signed c: [-3 -3 -3] int64
注意,当您使用相同dtype
的两个数组进行运算时:uint32
,结果数组也是相同类型。当您使用不同dtype
进行运算时,NumPy 将分配一个新类型,以满足计算中涉及的所有数组元素,在这里uint32
和int32
都可以表示为int64
。
NumPy 的默认行为是创建 32 位或 64 位有符号整数数组(取决于平台并匹配 C 中long
的大小)或双精度浮点数。如果您希望整数数组是特定类型,则需要在创建数组时指定dtype
。
2)NumPy 内置数组创建函数
NumPy 拥有超过40个内置函数用于创建数组,这些函数在数组创建例程中有详细说明。这些函数可以根据它们创建的数组维度大致分为三个类别:
-
一维数组
-
二维数组
-
多维数组(
ndarray
)
一维数组创建函数
例如numpy.linspace
和numpy.arange
这样的创建一维数组的函数通常至少需要两个输入参数:start
(起始值)和stop
(结束值)。
numpy.arange
用于创建具有规律性递增值的数组。更多详细信息和示例,请查阅文档。以下是一些示例:
>>> import numpy as np
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.arange(2, 10, dtype=float)
array([2., 3., 4., 5., 6., 7., 8., 9.])
>>> np.arange(2, 3, 0.1)
array([2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9])
注意:使用numpy.arange
的最佳实践是使用整数作为起始值、结束值和步长。关于dtype
有一些需要注意的地方。在第二个示例中,定义了dtype
。在第三个示例中,数组的dtype
为float
,以适应步长0.1
。由于舍入误差,有时会包含stop
值。
numpy.linspace
将创建具有指定数量元素的数组,并且这些元素在指定的起始值和结束值之间等间距分布。例如:
>>> import numpy as np
>>> np.linspace(1., 4., 6)
array([1. , 1.6, 2.2, 2.8, 3.4, 4. ])
这个创建函数的优势在于,您可以保证元素的数量以及起始值和结束值。而之前的arange(start, stop, step)
则不会包含stop
值。
二维数组创建函数
例如numpy.eye
、numpy.diag
和numpy.vander
这样的二维数组创建函数定义了特殊矩阵的属性,这些矩阵以二维数组的形式表示。
np.eye(n, m)
定义了一个二维单位矩阵。其中行索引和列索引相等(i=j
)的元素为1,其余元素为0,如下所示:
>>> import numpy as np
>>> np.eye(3)
array([[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]])
>>> np.eye(3, 5)
array([[1., 0., 0., 0., 0.],[0., 1., 0., 0., 0.],[0., 0., 1., 0., 0.]])
numpy.diag
可以根据给定的对角线值定义一个方阵,或者如果给定一个二维数组,则返回一个一维数组,该数组仅包含对角线元素。这两个数组创建函数在线性代数中非常有用,如下所示:
>>> import numpy as np
>>> np.diag([1, 2, 3])
array([[1, 0, 0],[0, 2, 0],[0, 0, 3]])
>>> np.diag([1, 2, 3], 1)
array([[0, 1, 0, 0],[0, 0, 2, 0],[0, 0, 0, 3],[0, 0, 0, 0]])
>>> a = np.array([[1, 2], [3, 4]])
>>> np.diag(a)
array([1, 4])
vander(x, n)
定义了一个范德蒙矩阵,作为二维 NumPy 数组。范德蒙矩阵的每一列都是输入一维数组或元组x
的递减幂次,其中最高多项式阶数为n-1
。这个数组创建例程有助于生成线性最小二乘模型,如下所示:
>>> import numpy as np
>>> np.vander(np.linspace(0, 2, 5), 2)
array([[0. , 1. ],[0.5, 1. ],[1. , 1. ],[1.5, 1. ],[2. , 1. ]])
>>> np.vander([1, 2, 3, 4], 2)
array([[1, 1],[2, 1],[3, 1],[4, 1]])
>>> np.vander((1, 2, 3, 4), 4)
array([[ 1, 1, 1, 1],[ 8, 4, 2, 1],[27, 9, 3, 1],[64, 16, 4, 1]])
通用ndarray
创建函数
例如numpy.ones
、numpy.zeros
和random
这样的ndarray
创建函数可以根据所需的形状定义数组。通过在元组或列表中指定维度数量和每个维度的长度,这些ndarray
创建函数可以创建任意维度的数组。
numpy.zeros
将创建一个指定形状的数组,其中填充了0值。默认的dtype
是float64
:
>>> import numpy as np
>>> np.zeros((2, 3))
array([[0., 0., 0.],[0., 0., 0.]])
>>> np.zeros((2, 3, 2))
array([[[0., 0.],[0., 0.],[0., 0.]],[[0., 0.],[0., 0.],[0., 0.]]])
numpy.ones
将创建一个填充了1值的数组。它在所有其他方面都与zeros
相同,如下所示:
>>> import numpy as np
>>> np.ones((2, 3))
array([[1., 1., 1.],[1., 1., 1.]])
>>> np.ones((2, 3, 2))
array([[[1., 1.],[1., 1.],[1., 1.]],[[1., 1.],[1., 1.],[1., 1.]]])
random
方法(来自default_rng
的结果)将创建一个填充了介于0和1之间的随机值的数组。它包含在numpy.random
库中。下面分别创建了形状为(2,3)
和(2,3,2)
的两个数组。设置种子为42,以便您可以重现这些伪随机数:
>>> import numpy as np
>>> from numpy.random import default_rng
>>> default_rng(42).random((2,3))
array([[0.77395605, 0.43887844, 0.85859792],[0.69736803, 0.09417735, 0.97562235]])
>>> default_rng(42).random((2,3,2))
array([[[0.77395605, 0.43887844],[0.85859792, 0.69736803],[0.09417735, 0.97562235]],[[0.7611397 , 0.78606431],[0.12811363, 0.45038594],[0.37079802, 0.92676499]]])
numpy.indices
将创建一组数组(作为一个更高维度的数组堆叠在一起),每个维度一个,每个数组代表该维度的变化:
>>> import numpy as np
>>> np.indices((3,3))
array([[[0, 0, 0],[1, 1, 1],[2, 2, 2]],[[0, 1, 2],[0, 1, 2],[0, 1, 2]]])
这在评估多维函数的规则网格上特别有用。
3)复制、连接或修改现有数组
一旦创建了数组,您可以通过复制、连接或修改现有数组来创建新数组。当您将数组或其元素赋值给一个新变量时,您需要显式地使用numpy.copy
来复制数组,否则该变量只是原始数组的一个“视图”。考虑以下示例:
>>> import numpy as np
>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> b = a[:2]
>>> b += 1
>>> print('a =', a, '; b =', b)
a = [2 3 3 4 5 6] ; b = [2 3]
在这个示例中,您并没有创建一个新数组。您创建了一个变量b
,它只是查看了a
的前两个元素。当您给b
加1时,您会得到与给a[:2]
加1相同的结果。如果您想创建一个新的数组,请使用numpy.copy
数组创建例程,如下所示:
>>> import numpy as np
>>> a = np.array([1, 2, 3, 4])
>>> b = a[:2].copy()
>>> b += 1
>>> print('a = ', a, 'b = ', b)
a = [1 2 3 4] b = [2 3]
有关更多信息和示例,请查看副本和视图。
有许多例程可以连接现有数组,例如numpy.vstack
、numpy.hstack
和numpy.block
。以下是一个使用block
将四个2×2的数组连接成一个4×4的数组的示例:
>>> import numpy as np
>>> A = np.ones((2, 2))
>>> B = np.eye(2, 2)
>>> C = np.zeros((2, 2))
>>> D = np.diag((-3, -4))
>>> np.block([[A, B], [C, D]])
array([[ 1., 1., 1., 0.],[ 1., 1., 0., 1.],[ 0., 0., -3., 0.],[ 0., 0., 0., -4.]])
其他例程使用类似的语法来连接ndarray
。请查看相关例程的文档以获取更多示例和语法。
4)从磁盘读取数组,无论是标准格式还是自定义格式
这是创建大型数组的最常见情况。细节在很大程度上取决于磁盘上数据的格式。本节提供有关如何处理各种格式的一般指导。有关更详细的输入/输出示例,请查看如何读取和写入文件。
标准二进制格式
各个领域都有数组数据的标准格式。以下列出了已知的 Python 库,这些库可以读取这些格式并返回 NumPy 数组(可能还有其他格式,可以通过读取并转换为 NumPy 数组,因此请查看最后一节内容)。
例如,一些格式不能直接读取,但很容易转换,这些格式由像 PIL(能够读取和写入许多图像格式,如 jpg、png 等)这样的库支持。
常见的 ASCII 格式
以逗号分隔值(csv)和制表符分隔值(tsv)文件为代表的分隔符文件,用于像 Excel 和 LabView 这样的程序。Python 函数可以逐行读取和解析这些文件。NumPy 有两个标准例程用于导入带有分隔符数据的文件,分别是numpy.loadtxt
和numpy.genfromtxt
。这些函数在读取和写入文件中有更复杂的用例。假设有一个名为simple.csv
的文件,内容如下:
$ cat simple.csv
x, y
0, 0
1, 1
2, 4
3, 9
使用numpy.loadtxt
导入simple.csv
文件:
>>> import numpy as np
>>> np.loadtxt('simple.csv', delimiter = ',', skiprows = 1)
array([[0., 0.],[1., 1.],[2., 4.],[3., 9.]])
更通用的 ASCII 文件可以使用scipy.io
和 Pandas 来读取。
5)通过使用字符串或缓冲区从原始字节创建数组
有多种方法可以使用。如果文件格式相对简单,那么可以编写一个简单的 I/O 库,并使用 NumPy 的fromfile()
函数和.tofile()
方法直接读取和写入 NumPy 数组(注意字节顺序!)。如果存在一个良好的 C 或 C++ 库可以读取数据,那么可以使用各种技术来包装该库,但这当然要多做很多工作,并且需要更高级的知识来与 C 或 C++ 进行接口。
6)使用特殊库函数(例如 SciPy、pandas 和 OpenCV)
NumPy 是 Python 科学计算堆栈中数组容器的基础库。许多 Python 库,包括 SciPy、Pandas 和 OpenCV,都使用 NumPy 的ndarray
作为数据交换的通用格式。这些库可以创建、操作和处理 NumPy 数组。
风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。