NumPy-1-26-中文文档-十七-
NumPy 1.26 中文文档(十七)
随机抽样(numpy.random)
快速开始
numpy.random 模块实现了伪随机数生成器(PRNGs 或 RNGs)的能力,可以从各种概率分布中抽取样本。一般来说,用户会使用 default_rng 创建一个 Generator 实例,并调用其中的各种方法来从不同的分布中获取样本。
>>> import numpy as np
>>> rng = np.random.default_rng()
# Generate one random float uniformly distributed over the range [0, 1)
>>> rng.random()
0.06369197489564249 # may vary
# Generate an array of 10 numbers according to a unit Gaussian distribution.
>>> rng.standard_normal(10)
array([-0.31018314, -1.8922078 , -0.3628523 , -0.63526532, 0.43181166, # may vary
0.51640373, 1.25693945, 0.07779185, 0.84090247, -2.13406828])
# Generate an array of 5 integers uniformly over the range [0, 10).
>>> rng.integers(low=0, high=10, size=5)
array([8, 7, 6, 2, 0]) # may vary
我们的 RNGs 是确定性序列,可以通过指定一个种子整数来重现其初始状态。默认情况下,如果没有提供种子,default_rng 将从操作系统的非确定性数据中创建种子 RNG,因此每次生成不同的数字。伪随机序列在所有实际目的上都是独立的,至少对于我们一开始就很好的伪随机性来说。
>>> rng1 = np.random.default_rng()
>>> rng1.random()
0.6596288841243357 # may vary
>>> rng2 = np.random.default_rng()
>>> rng2.random()
0.11885628817151628 # may vary
警告
本模块实现的伪随机数生成器设计用于统计建模和模拟。不适用于安全或加密目的。有关此类用例,请参阅标准库中的 secrets 模块。
种子应为大的正整数。default_rng 可以接受任意大小的正整数。我们建议使用非常大且唯一的数字,以确保您的种子与其他人的不同。这是一个良好的实践,以确保您的结果在统计上独立于他们的结果,除非您有意尝试复制他们的结果。获取这样一个种子数字的便捷方法是使用 secrets.randbits 来获取一个任意的 128 位整数。
>>> import secrets
>>> import numpy as np
>>> secrets.randbits(128)
122807528840384100672342137672332424406 # may vary
>>> rng1 = np.random.default_rng(122807528840384100672342137672332424406)
>>> rng1.random()
0.5363922081269535
>>> rng2 = np.random.default_rng(122807528840384100672342137672332424406)
>>> rng2.random()
0.5363922081269535
有关在专门情况下控制种子的更高级选项,请参阅 default_rng 和 SeedSequence 的文档。
Generator及其相关基础设施是在 NumPy 版本 1.17.0 中引入的。仍然有很多代码使用旧的RandomState和numpy.random中的函数。虽然目前没有计划删除它们,但我们建议尽快过渡到Generator。这些算法更快,更灵活,并且将在未来得到更多改进。在很大程度上,Generator可以用作RandomState的替代品。查看 Legacy Random Generation 以获取有关旧基础设施的信息,What’s New or Different 以获取有关过渡的信息,以及NEP 19以获取过渡的一些原因。
设计
用户主要与Generator实例进行交互。每个Generator实例拥有一个实现核心随机数生成算法的BitGenerator实例。BitGenerator具有有限的职责范围。它管理状态并提供函数来生成随机双精度数和随机无符号 32 位和 64 位值。
Generator接收由比特生成器提供的流,并将它们转换为更有用的分布,例如模拟正态随机值。这种结构允许使用替代比特生成器而几乎没有代码重复。
NumPy 实现了几种不同的 BitGenerator 类,实现了不同的 RNG 算法。default_rng 目前使用 PCG64 作为默认的 BitGenerator。它具有比传统的 RandomState 中使用的 MT19937 算法更好的统计特性和性能。有关支持的 BitGenerators 的更多详细信息,请参见 Bit Generators。
default_rng 和 BitGenerators 将种子转换为 RNG 状态的工作委托给内部的 SeedSequence。SeedSequence 实现了一个复杂的算法,介于用户输入和每个 BitGenerator 算法的内部实现细节之间,每个算法可能需要不同数量的位来表示其状态。重要的是,它允许您使用任意大小的整数和这些整数的任意序列混合在一起形成 RNG 状态。这是构建灵活的并行 RNG 流模式的有用基元。
为了向后兼容,我们仍然维护传统的 RandomState 类。它继续默认使用 MT19937 算法,并且旧的种子仍然可以产生相同的结果。方便的 numpy.random 中的函数仍然是单个全局 RandomState 实例上方法的别名。有关完整详细信息,请参见传统随机生成。有关 Generator 和 RandomState 之间的详细比较,请参见新功能或不同之处。
并行生成
包含的生成器可以在多种方式的并行分布式应用中使用:
-
种子序列生成
-
整数种子序列
-
独立流
-
跳跃比特生成器状态
具有大量并行性的用户将希望查阅 升级 PCG64 为 PCG64DXSM。
概念
-
随机生成器
-
旧的生成器(RandomState)
-
比特生成器
-
种子和熵
-
升级 PCG64 为 PCG64DXSM
-
兼容性策略
特性
-
并行应用程序
-
SeedSequence的衍生 -
整数种子序列
-
独立流
-
跳跃比特生成器状态
-
-
多线程生成
-
新功能或不同之处
-
性能比较
-
建议
-
时间
-
在不同操作系统上的性能
-
-
用于随机数的 C API
-
使用 Numba、Cython、CFFI 的示例
-
Numba
-
Cython
-
CFFI
-
新的比特生成器
-
示例
-
生成器和比特生成器的原始来源
该软件包是独立于 NumPy 开发的,并在版本 1.17.0 中集成。原始存储库位于 github.com/bashtage/randomgen。
快速入门
numpy.random 模块实现了伪随机数生成器(PRNGs 或 RNGs,简称)的能力,可以从各种概率分布中提取样本。一般来说,用户将使用 default_rng 创建一个 Generator 实例,并调用其中的各种方法来从不同的分布中获取样本。
>>> import numpy as np
>>> rng = np.random.default_rng()
# Generate one random float uniformly distributed over the range [0, 1)
>>> rng.random()
0.06369197489564249 # may vary
# Generate an array of 10 numbers according to a unit Gaussian distribution.
>>> rng.standard_normal(10)
array([-0.31018314, -1.8922078 , -0.3628523 , -0.63526532, 0.43181166, # may vary
0.51640373, 1.25693945, 0.07779185, 0.84090247, -2.13406828])
# Generate an array of 5 integers uniformly over the range [0, 10).
>>> rng.integers(low=0, high=10, size=5)
array([8, 7, 6, 2, 0]) # may vary
我们的随机数生成器是确定性序列,可以通过指定一个种子整数来生成其初始状态。默认情况下,如果没有提供种子,default_rng 将使用操作系统中的非确定性数据来生成随机数,因此每次生成的数字都会不同。为了所有实际目的,伪随机序列将是独立的,至少对于我们一开始目的良好的伪随机性来说。
>>> rng1 = np.random.default_rng()
>>> rng1.random()
0.6596288841243357 # may vary
>>> rng2 = np.random.default_rng()
>>> rng2.random()
0.11885628817151628 # may vary
警告
本模块实现的伪随机数生成器设计用于统计建模和模拟。它们不适用于安全或加密目的。请参见标准库中的 secrets 模块,了解此类用例。
随机种子应是大的正整数。default_rng 可以接受任意大小的正整数。我们建议使用非常大、唯一的数字,以确保您的种子与其他人的不同。这是一种好的做法,可以确保您的结果在统计上与他们的结果独立,除非您有意尝试复制他们的结果。获取这样的种子数字的便捷方法是使用 secrets.randbits 获取一个任意的 128 位整数。
>>> import secrets
>>> import numpy as np
>>> secrets.randbits(128)
122807528840384100672342137672332424406 # may vary
>>> rng1 = np.random.default_rng(122807528840384100672342137672332424406)
>>> rng1.random()
0.5363922081269535
>>> rng2 = np.random.default_rng(122807528840384100672342137672332424406)
>>> rng2.random()
0.5363922081269535
查看有关 default_rng 和 SeedSequence 的文档,了解在专业场景下控制种子的更高级选项。
生成器 及其相关基础设施是在 NumPy 版本 1.17.0 中引入的。目前仍有许多代码使用旧的 RandomState 和 numpy.random 中的函数。虽然目前没有计划删除它们,但我们建议尽快过渡到 Generator。这些算法更快、更灵活,并将在未来得到更多的改进。大部分情况下,Generator 可以用作 RandomState 的替代品。参见 Legacy Random Generation 了解旧基础设施的信息,What’s New or Different 了解过渡信息,以及 NEP 19 了解过渡理由。
设计
用户主要与Generator实例进行交互。每个Generator实例拥有一个实现核心随机数生成算法的BitGenerator实例。BitGenerator具有有限的责任范围。它管理状态并提供函数来生成随机双精度数和随机无符号 32 位和 64 位值。
Generator获取由比特生成器提供的流,并将其转换为更有用的分布,例如模拟正态随机值。这种结构允许使用替代比特生成器而几乎不产生重复代码。
NumPy 实现了几种不同的BitGenerator类,实现了不同的随机数生成算法。default_rng目前使用PCG64作为默认的BitGenerator。它比传统的RandomState中使用的MT19937算法具有更好的统计特性和性能。有关支持的比特生成器的更多详细信息,请参见比特生成器。
default_rng和比特生成器将种子转换为随机数生成器状态的过程委托给内部的SeedSequence。SeedSequence实现了一个复杂的算法,它在用户输入和每个BitGenerator算法的内部实现细节之间进行中介,每个算法可能需要不同数量的位来表示其状态。重要的是,它允许您使用任意大小的整数和这些整数的任意序列来混合到随机数生成器状态中。这是构建灵活的并行随机数生成流模式的有用基元。
为了向后兼容,我们仍然维护传统的RandomState类。它仍然默认使用MT19937算法,并且旧种子仍将产生相同的结果。方便的 numpy.random 中的函数仍然是一个全局RandomState实例上方法的别名。详见传统随机生成以获取完整的细节。请查看新功能或不同之处以详细比较Generator和RandomState之间的区别。
并行生成
包含的生成器可以以多种方式用于并行、分布式应用程序:
-
种子序列生成
-
整数种子序列
-
独立流
-
跳过位生成器状态
拥有大量并行性的用户将想要参考将 PCG64 升级为 PCG64DXSM。
并行生成
包含的生成器可以以多种方式用于并行、分布式应用程序:
-
种子序列生成
-
整数种子序列
-
独立流
-
跳过位生成器状态
拥有大量并行性的用户将要咨询将 PCG64 升级为 PCG64DXSM。
概念
-
随机生成器
-
传统生成器(RandomState)
-
位生成器
-
种子和熵
-
将 PCG64 升级为 PCG64DXSM
-
兼容性政策
特性
-
并行应用
-
SeedSequence生成 -
整数种子序列
-
独立流
-
跳过位生成器状态
-
-
多线程生成
-
新功能或不同之处
-
性能比较
-
推荐
-
定时
-
不同操作系统上的性能
-
-
随机数 C API
-
使用 Numba、Cython、CFFI 的示例
-
Numba
-
Cython
-
CFFI
-
新的 Bit Generators
-
示例
-
生成器和 BitGenerators 的原始来源
这个包是独立于 NumPy 开发的,在版本 1.17.0 中被集成。原始仓库位于 github.com/bashtage/randomgen。
生成器和 BitGenerators 的原始来源
这个包是独立于 NumPy 开发的,在版本 1.17.0 中被集成。原始仓库位于 github.com/bashtage/randomgen。
随机生成器
Generator 提供对各种分布的访问,并作为 RandomState 的替代品。两者的主要区别在于 Generator 依赖额外的 BitGenerator 来管理状态并生成随机位,然后将这些随机位转换为有用分布的随机值。Generator 使用的默认 BitGenerator 是 PCG64。可以通过将实例化的 BitGenerator 传递给 Generator 来更改 BitGenerator。
numpy.random.default_rng(seed=None)
使用默认的 BitGenerator(PCG64)构造一个新的 Generator。
参数:
种子, 可选
用于初始化 BitGenerator 的种子。如果为 None,则将从操作系统中获取新鲜、不可预测的熵。如果传递了一个 int 或 array_like[ints],那么它将传递给 SeedSequence 来派生初始的 BitGenerator 状态。还可以传入一个 SeedSequence 实例。此外,当传递一个 BitGenerator 时,它将被 Generator 包装。如果传递一个 Generator,则它将不经修改地返回。
返回:
生成器
初始化的生成器对象。
注意
如果 seed 不是 BitGenerator 或 Generator,则会实例化一个新的 BitGenerator。此函数不管理默认全局实例。
有关种子和熵的更多信息,请参见 Seeding and Entropy。
示例
default_rng 是随机数类 Generator 的推荐构造函数。以下是使用 default_rng 和 Generator 类构造随机数生成器的几种方式。
在这里,我们使用 default_rng 生成一个随机浮点数:
>>> import numpy as np
>>> rng = np.random.default_rng(12345)
>>> print(rng)
Generator(PCG64)
>>> rfloat = rng.random()
>>> rfloat
0.22733602246716966
>>> type(rfloat)
<class 'float'>
在这里,我们使用default_rng生成 3 个介于 0(包括)和 10(不包括)之间的随机整数:
>>> import numpy as np
>>> rng = np.random.default_rng(12345)
>>> rints = rng.integers(low=0, high=10, size=3)
>>> rints
array([6, 2, 7])
>>> type(rints[0])
<class 'numpy.int64'>
在这里,我们指定了一个种子,以便我们有可重现的结果:
>>> import numpy as np
>>> rng = np.random.default_rng(seed=42)
>>> print(rng)
Generator(PCG64)
>>> arr1 = rng.random((3, 3))
>>> arr1
array([[0.77395605, 0.43887844, 0.85859792],
[0.69736803, 0.09417735, 0.97562235],
[0.7611397 , 0.78606431, 0.12811363]])
如果我们退出并重新启动 Python 解释器,我们会看到再次生成相同的随机数:
>>> import numpy as np
>>> rng = np.random.default_rng(seed=42)
>>> arr2 = rng.random((3, 3))
>>> arr2
array([[0.77395605, 0.43887844, 0.85859792],
[0.69736803, 0.09417735, 0.97562235],
[0.7611397 , 0.78606431, 0.12811363]])
class numpy.random.Generator(bit_generator)
BitGenerators 的容器。
Generator公开了许多方法,用于从各种概率分布中生成随机数。除了与分布特定参数一起传递外,每个方法还接受一个名为size的关键字参数,默认值为None。如果size是None,则生成并返回单个值。如果size是整数,则返回一个填充有生成值的一维数组。如果size是元组,则填充并返回具有该形状的数组。
函数numpy.random.default_rng将使用 NumPy 默认的BitGenerator实例化一个Generator。
没有兼容性保证
Generator 不提供版本兼容性保证。特别是随着更好的算法的发展,比特流可能会发生变化。
参数:
bit_generator BitGenerator
用作核心生成器的 BitGenerator。
另请参阅
default_rng
推荐用于Generator的构造函数。
注意
Python 标准库模块random包含一个伪随机数生成器,具有许多与Generator中可用方法相似的方法。 它使用 Mersenne Twister,可以使用MT19937访问此比特生成器。 Generator除了是 NumPy 感知之外,还具有提供更多的概率分布可供选择的优势��
示例
>>> from numpy.random import Generator, PCG64
>>> rng = Generator(PCG64())
>>> rng.standard_normal()
-0.203 # random
访问 BitGenerator 和生成
bit_generator |
获取生成器使用的比特生成器实例 |
|---|---|
spawn(n_children) |
创建新的独立的子生成器。 |
简单随机数据
integers(low[, high, size, dtype, endpoint]) |
从low(包括)到high(不包括)返回随机整数,或者如果 endpoint=True,则从low(包括)到high(包括)返回。 |
|---|---|
random([size, dtype, out]) |
返回半开区间 0.0, 1.0)内的随机浮点数。 |
[choice(a[, size, replace, p, axis, shuffle]) |
从给定数组中生成一个随机样本 |
bytes(length) |
返回随机字节。 |
排列
用于随机排列序列的方法有
shuffle(x[, axis]) |
通过对内容进行随机排列在原地修改数组或序列。 |
|---|---|
permutation(x[, axis]) |
随机排列一个序列,或返回一个排列后的范围。 |
permuted(x[, axis, out]) |
沿着轴 axis 随机排列 x 。 |
下表总结了这些方法的行为。
| 方法 | 复制/原地操作 | 轴处理 |
|---|---|---|
| shuffle | 原地操作 | 类似于 1 维 |
| permutation | 复制 | 类似于 1 维 |
| permuted | either (use ‘out’ for in-place) | axis independent |
以下小节提供了有关这些差异的更多细节。
原地操作 vs. 复制
Generator.shuffle 和 Generator.permutation 的主要区别在于,Generator.shuffle 是原地操作,而 Generator.permutation 返回一个副本。
默认情况下,Generator.permuted 返回一个副本。要在原地操作 Generator.permuted ,请将同一个数组作为第一个参数和作为out参数的值传递。例如,
>>> rng = np.random.default_rng()
>>> x = np.arange(0, 15).reshape(3, 5)
>>> x
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> y = rng.permuted(x, axis=1, out=x)
>>> x
array([[ 1, 0, 2, 4, 3], # random
[ 6, 7, 8, 9, 5],
[10, 14, 11, 13, 12]])
注意当给定out时,返回值为out:
>>> y is x
True
处理 axis 参数
这些方法的一个重要区别是它们如何处理axis参数。Generator.shuffle和Generator.permutation都将输入视为一维序列,而axis参数确定要使用的输入数组的哪个维度作为序列。对于二维数组,axis=0将重新排列数组的行,axis=1将重新排列列。例如
>>> rng = np.random.default_rng()
>>> x = np.arange(0, 15).reshape(3, 5)
>>> x
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> rng.permutation(x, axis=1)
array([[ 1, 3, 2, 0, 4], # random
[ 6, 8, 7, 5, 9],
[11, 13, 12, 10, 14]])
注意列已被“一次性”重新排列:每列内的值未发生改变。
方法Generator.permuted处理axis参数的方式类似于numpy.sort处理它的方式。给定轴上的每个切片都与其他切片独立地进行洗牌。将Generator.permuted的使用示例与上面Generator.permutation的使用示例进行比较:
>>> rng.permuted(x, axis=1)
array([[ 1, 0, 2, 4, 3], # random
[ 5, 7, 6, 9, 8],
[10, 14, 12, 13, 11]])
在这个示例中,每行内的值(即axis=1上的值)被独立地进行了洗牌。这不是对列的“一次性”洗牌。
对非 NumPy 序列进行洗牌
Generator.shuffle适用于非 NumPy 序列。也就是说,如果给定的序列不是 NumPy 数组,则会原地对该序列进行洗牌。例如,
>>> rng = np.random.default_rng()
>>> a = ['A', 'B', 'C', 'D', 'E']
>>> rng.shuffle(a) # shuffle the list in-place
>>> a
['B', 'D', 'A', 'E', 'C'] # random
分布
beta(a, b[, size]) |
从 Beta 分布中抽取样本。 |
|---|---|
binomial(n, p[, size]) |
从二项分布中抽取样本。 |
chisquare(df[, size]) |
从卡方分布中抽取样本。 |
dirichlet(alpha[, size]) |
从狄利克雷分布中抽取样本。 |
exponential([scale, size]) |
从指数分布中抽取样本。 |
f(dfnum, dfden[, size]) |
从 F 分布中抽取样本。 |
gamma(shape[, scale, size]) |
从 Gamma 分布中抽取样本。 |
geometric(p[, size]) |
从几何分布中抽取样本。 |
gumbel([loc, scale, size]) |
从 Gumbel 分布中抽取样本。 |
hypergeometric(ngood, nbad, nsample[, size]) |
从超几何分布中抽取样本。 |
laplace([loc, scale, size]) |
从拉普拉斯或双指数分布中抽取样本,指定位置(或均值)和尺度(衰减)。 |
logistic([loc, scale, size]) |
从 Logistic 分布中抽取样本。 |
lognormal([mean, sigma, size]) |
从对数正态分布中抽取样本。 |
logseries(p[, size]) |
从对数级数分布中抽取样本。 |
multinomial(n, pvals[, size]) |
从多项分布中抽取样本。 |
multivariate_hypergeometric(colors, nsample) |
从多元超几何分布中生成变量。 |
multivariate_normal(mean, cov[, size, ...]) |
从多元正态分布中随机抽取样本。 |
negative_binomial(n, p[, size]) |
从负二项分布中抽取样本。 |
noncentral_chisquare(df, nonc[, size]) |
从非中心卡方分布中抽取样本。 |
noncentral_f(dfnum, dfden, nonc[, size]) |
从非中心 F 分布中抽取样本。 |
normal([loc, scale, size]) |
从正态(高斯)分布中抽取随机样本。 |
pareto(a[, size]) |
从指定形状的 Pareto II 或 Lomax 分布中抽取样本。 |
poisson([lam, size]) |
从泊松分布中抽取样本。 |
power(a[, size]) |
从指数为正的幂分布中抽取[0, 1]范围内的样本。 |
rayleigh([scale, size]) |
从瑞利分布中抽取样本。 |
standard_cauchy([size]) |
从模式=0 的标准柯西分布中抽取样本。 |
standard_exponential([size, dtype, method, out]) |
从标准指数分布中抽取样本。 |
standard_gamma(shape[, size, dtype, out]) |
从标准伽玛分布中抽取样本。 |
standard_normal([size, dtype, out]) |
从标准正态分布(平均值=0,标准差=1)中抽取样本。 |
standard_t(df[, size]) |
从具有df自由度的标准学生 t 分布中抽取样本。 |
triangular(left, mode, right[, size]) |
从区间[left, right]上的三角分布中抽取样本。 |
uniform([low, high, size]) |
从均匀分布中抽取样本。 |
vonmises(mu, kappa[, size]) |
从 von Mises 分布中抽取样本。 |
wald(mean, scale[, size]) |
从 Wald 或反高斯分布中抽取样本。 |
weibull(a[, size]) |
从威布尔分布中抽取样本。 |
zipf(a[, size]) |
从齐普夫分布中抽取样本。 |
访问比特生成器和生成
bit_generator |
获取生成器使用的比特生成器实例 |
|---|---|
spawn(n_children) |
创建新的独立子生成器。 |
简单随机数据
integers(low[, high, size, dtype, endpoint]) |
返回从low(包括)到high(不包括)的随机整数,或者如果 endpoint=True,则为从low(包括)到high(包括)的整数。 |
|---|---|
random([size, dtype, out]) |
返回半开区间 0.0, 1.0)内的随机浮点数。 |
[choice(a[, size, replace, p, axis, shuffle]) |
从给定数组中生成随机样本 |
bytes(length) |
返回随机字节。 |
排列
随机对序列进行排列的方法有
shuffle(x[, axis]) |
通过对其内容进行混洗,原地修改一个数组或序列。 |
|---|---|
permutation(x[, axis]) |
随机排列一个序列,或返回一个排列范围。 |
permuted(x[, axis, out]) |
沿着轴axis随机排列x。 |
以下表格总结了这些方法的行为。
| 方法 | 复制/就地操作 | 轴处理方式 |
|---|---|---|
| shuffle | 就地操作 | 如同 1d |
| permutation | 复制 | 如同 1d |
| permuted | 任意(使用'out'进行就地操作) | 轴独立 |
以下各小节提供了更多关于区别的细节。
就地操作 vs. 复制
Generator.shuffle和Generator.permutation的主要区别在于Generator.shuffle是就地操作,而Generator.permutation返回一个副本。
默认情况下,Generator.permuted返回一个副本。要在原地操作Generator.permuted,将相同的数组作为第一个参数和作为out参数的值传递。例如,
>>> rng = np.random.default_rng()
>>> x = np.arange(0, 15).reshape(3, 5)
>>> x
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> y = rng.permuted(x, axis=1, out=x)
>>> x
array([[ 1, 0, 2, 4, 3], # random
[ 6, 7, 8, 9, 5],
[10, 14, 11, 13, 12]])
注意,当给定out时,返回值就是out:
>>> y is x
True
处理axis参数
这些方法的一个重要区别是它们如何处理axis参数。Generator.shuffle和Generator.permutation都将输入视为一维序列,而axis参数确定了要使用输入数组的哪个维度作为序列。在二维数组的情况下,axis=0实际上重新排列了数组的行,而axis=1重新排列了列。例如
>>> rng = np.random.default_rng()
>>> x = np.arange(0, 15).reshape(3, 5)
>>> x
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> rng.permutation(x, axis=1)
array([[ 1, 3, 2, 0, 4], # random
[ 6, 8, 7, 5, 9],
[11, 13, 12, 10, 14]])
注意,列是“批量”重新排列的:每列内的值没有改变。
方法Generator.permuted对待axis参数与numpy.sort对待它的方式类似。沿着给定轴的每个切片都独立地洗牌。将Generator.permuted的使用示例与上面Generator.permutation的示例进行比较:
>>> rng.permuted(x, axis=1)
array([[ 1, 0, 2, 4, 3], # random
[ 5, 7, 6, 9, 8],
[10, 14, 12, 13, 11]])
- 在这个例子中,每行内的数值(即沿
axis=1的数值)被独立洗牌。这不是对列的“整体”洗牌。
- 对非 NumPy 序列进行洗牌
Generator.shuffle可对非 NumPy 序列进行操作。也就是说,如果给定一个非 NumPy 数组的序列,它会直接在原序列上进行洗牌。例如,
>>> rng = np.random.default_rng()
>>> a = ['A', 'B', 'C', 'D', 'E']
>>> rng.shuffle(a) # shuffle the list in-place
>>> a
['B', 'D', 'A', 'E', 'C'] # random
- 就地操作 vs. 复制
-
Generator.shuffle与Generator.permutation的主要区别在于,Generator.shuffle是就地操作,而Generator.permutation则返回复制品。 -
默认情况下,
Generator.permuted返回一个复制品。要就地操作Generator.permuted,需要将同一数组作为第一个参数以及out参数的值传递。例如,
>>> rng = np.random.default_rng()
>>> x = np.arange(0, 15).reshape(3, 5)
>>> x
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> y = rng.permuted(x, axis=1, out=x)
>>> x
array([[ 1, 0, 2, 4, 3], # random
[ 6, 7, 8, 9, 5],
[10, 14, 11, 13, 12]])
- 当给定
out时,返回值即为out:
>>> y is x
True
-
处理
axis参数 -
这些方法的重要区别在于它们如何处理
axis参数。Generator.shuffle和Generator.permutation都将输入视为一维序列,axis参数决定了输入数组的哪个维度被用作序列。对于二维数组,axis=0实际上会重新排列数组的行,而axis=1会重新排列列。例如
>>> rng = np.random.default_rng()
>>> x = np.arange(0, 15).reshape(3, 5)
>>> x
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> rng.permutation(x, axis=1)
array([[ 1, 3, 2, 0, 4], # random
[ 6, 8, 7, 5, 9],
[11, 13, 12, 10, 14]])
- 注意到列被“整体”重新排列:每列内的数值未发生变化。
方法Generator.permuted处理axis参数的方式类似于numpy.sort处理它的方式。给定轴上的每个切片都独立地重排。将Generator.permuted的用法示例与上面的Generator.permutation的用法示例进行比较:
>>> rng.permuted(x, axis=1)
array([[ 1, 0, 2, 4, 3], # random
[ 5, 7, 6, 9, 8],
[10, 14, 12, 13, 11]])
在这个例子中,每行内的值(即axis=1处的值)都已独立地进行了洗牌。这不是对列的“大批量”洗牌。
对非 NumPy 序列进行洗牌
Generator.shuffle 对非 NumPy 序列起作用。也就是说,如果给出的序列不是 NumPy 数组,它会就地对该序列进行重排。例如,
>>> rng = np.random.default_rng()
>>> a = ['A', 'B', 'C', 'D', 'E']
>>> rng.shuffle(a) # shuffle the list in-place
>>> a
['B', 'D', 'A', 'E', 'C'] # random
分布
beta(a, b[, size]) |
从 Beta 分布中抽取样本。 |
|---|---|
binomial(n, p[, size]) |
从二项分布中抽取样本。 |
chisquare(df[, size]) |
从卡方分布中抽取样本。 |
dirichlet(alpha[, size]) |
从狄利克雷分布中抽取样本。 |
exponential([scale, size]) |
从指数分布中抽取样本。 |
f(dfnum, dfden[, size]) |
从 F 分布中抽取样本。 |
gamma(shape[, scale, size]) |
从 Gamma 分布中抽取样本。 |
geometric(p[, size]) |
从几何分布中抽取样本。 |
gumbel([loc, scale, size]) |
从冈伯尔分布中抽取样本。 |
hypergeometric(ngood, nbad, nsample[, size]) |
从超几何分布中抽取样本。 |
laplace([loc, scale, size]) |
从拉普拉斯或双曲线分布中抽取样本,具有指定的位置(或均值)和比例(衰减)。 |
logistic([loc, scale, size]) |
从逻辑分布中抽取样本。 |
lognormal([mean, sigma, size]) |
从对数正态分布中抽取样本。 |
logseries(p[, size]) |
从对数级数分布中抽取样本。 |
multinomial(n, pvals[, size]) |
从多项式分布中抽取样本。 |
multivariate_hypergeometric(colors, nsample) |
从多元超几何分布中生成变量。 |
multivariate_normal(mean, cov[, size, ...]) |
从多元正态分布中抽取随机样本。 |
negative_binomial(n, p[, size]) |
从负二项分布中抽取样本。 |
noncentral_chisquare(df, nonc[, size]) |
从非中心卡方分布中抽取样本。 |
noncentral_f(dfnum, dfden, nonc[, size]) |
从非中心 F 分布中抽取样本。 |
normal([loc, scale, size]) |
从正态(高斯)分布中抽取随机样本。 |
pareto(a[, size]) |
从具有指定形状的帕累托 II 或 Lomax 分布中抽取样本。 |
poisson([lam, size]) |
从泊松分布中抽取样本。 |
power(a[, size]) |
从具有正指数 a - 1 的幂分布中抽取 [0, 1] 范围内的样本。 |
rayleigh([scale, size]) |
从 Rayleigh 分布中抽取样本。 |
standard_cauchy([size]) |
从具有 mode=0 的标准柯西分布中抽取样本。 |
standard_exponential([size, dtype, method, out]) |
从标准指数分布中抽取样本。 |
standard_gamma(shape[, size, dtype, out]) |
从标准 Gamma 分布中抽取样本。 |
standard_normal([size, dtype, out]) |
从标准正态分布(均值为 0,标准差为 1)中抽取样本。 |
standard_t(df[, size]) |
从具有 df 自由度的标准学生 t 分布中抽取样本。 |
triangular(left, mode, right[, size]) |
在区间 [left, right] 上从三角形分布中抽取样本。 |
uniform([low, high, size]) |
从均匀分布中抽取样本。 |
vonmises(mu, kappa[, size]) |
从 von Mises 分布中抽取样本。 |
wald(mean, scale[, size]) |
从 Wald 分布或反高斯分布中抽取样本。 |
weibull(a[, size]) |
从 Weibull 分布中抽取样本。 |
zipf(a[, size]) |
从 Zipf 分布中抽取样本。 |
遗留随机生成
RandomState 提供对遗留生成器的访问。这个生成器被认为是冻结的,将不再有进一步的改进。它被保证会产生与 NumPy v1.16 最终版本相同的值。这些都依赖于 Box-Muller 正态分布或逆 CDF 指数分布或 gamma 分布。只有在必须要得到与以前版本的 NumPy 所产生的随机数相同的情况下,才应该使用这个类。
RandomState 在使用 Box-Muller 正态分布时增加了一些状态所需的额外信息,因为这些值是成对产生的。重要的是要使用 RandomState.get_state 而不是底层的比特生成器state来访问状态,这样就可以保存这些额外的值。
尽管我们提供了MT19937 独立使用的 BitGenerator,但请注意其默认的种子生成使用的是 SeedSequence 而不是遗留的种子生成算法。RandomState 将使用遗留的种子生成算法。目前使用遗留种子生成算法的方法是私有的,因为使用它们的主要原因只是为了实现RandomState。但是,可以使用RandomState的状态来重置MT19937的状态:
from numpy.random import MT19937
from numpy.random import RandomState
rs = RandomState(12345)
mt19937 = MT19937()
mt19937.state = rs.get_state()
rs2 = RandomState(mt19937)
# Same output
rs.standard_normal()
rs2.standard_normal()
rs.random()
rs2.random()
rs.standard_exponential()
rs2.standard_exponential()
class numpy.random.RandomState(seed=None)
用于慢的 Mersenne Twister 伪随机数生成器的容器。考虑使用不同的 BitGenerator 与 Generator 容器。
RandomState 和 Generator 提供了许多方法,用于从各种概率分布中生成随机数。除了特定于分布的参数外,每个方法都带有一个关键字参数size,默认为None。如果size为None,那么将生成并返回单个值。如果size是整数,则返回填充了生成值的 1-D 数组。如果size是一个元组,则填充并返回一个具有该形状的数组。
兼容性保证
使用固定种子和对相同参数使用RandomState方法的固定系列调用的固定比特生成器将始终产生相同的结果,直到舍入误差,除非值不正确。RandomState实际上是冻结的,只会接收 Numpy 内部变化所需的更新。更重大的变化,包括算法改进,保留给Generator。
参数:
seed, optional
用于初始化伪随机数生成器或实例化的比特生成器的随机种子。如果是整数或数组,则用作 MT19937 比特生成器的种子。值可以是 0 到 2**32 - 1 之间的任何整数,这样的整数数组(或其他序列),或None(默认值)。如果seed为None,那么MT19937比特生成器将通过从/dev/urandom(或 Windows 的类似物)读取数据(如果可用)或否则从时钟种子初始化。
另请参阅
Generator
MT19937
numpy.random.BitGenerator
注意
Python 标准库模块“random”还包含一个梅森旋转伪随机数生成器,具有一些与RandomState中可用方法类似的方法。除了具有 NumPy 意识外,RandomState的优势在于它提供了更多的概率分布可供选择。
播种和状态
get_state([legacy]) |
返回表示生成器内部状态的元组。 |
|---|---|
set_state(state) |
从元组设置生成器的内部状态。 |
seed([seed]) |
重新播种传统 MT19937 比特生成器 |
简单的随机数据
rand(d0, d1, ..., dn) |
给定形状的随机值。 |
|---|---|
randn(d0, d1, ..., dn) |
从“标准正态”分布中返回一个样本(或多个样本)。 |
randint(low[, high, size, dtype]) |
返回从 low(包括)到 high(不包括)的随机整数。 |
random_integers(low[, high, size]) |
返回 np.int_ 类型的随机整数,范围在 low 和 high 之间(包括边界)。 |
random_sample([size]) |
返回半开区间 0.0, 1.0) 中的随机浮点数。 |
[choice(a[, size, replace, p]) |
从给定的 1-D 数组中生成随机样本。 |
bytes(length) |
返回随机字节。 |
排列
shuffle(x) |
通过对其内容进行洗牌来就地修改序列。 |
|---|---|
permutation(x) |
随机排列一个序列,或返回一个排列后的范围。 |
分布
beta(a, b[, size]) |
从 Beta 分布中抽取样本。 |
|---|---|
binomial(n, p[, size]) |
从二项分布中抽取样本。 |
chisquare(df[, size]) |
从卡方分布中抽取样本。 |
dirichlet(alpha[, size]) |
从 Dirichlet 分布中抽取样本。 |
exponential([scale, size]) |
从指数分布中抽取样本。 |
f(dfnum, dfden[, size]) |
从 F 分布中抽取样本。 |
gamma(shape[, scale, size]) |
从伽马分布中绘制样本。 |
geometric(p[, size]) |
从几何分布中绘制样本。 |
gumbel([loc, scale, size]) |
从古贝尔分布中绘制样本。 |
hypergeometric(ngood, nbad, nsample[, size]) |
从超几何分布中绘制样本。 |
laplace([loc, scale, size]) |
从拉普拉斯或双指数分布中绘制样本,并指定位置(或平均值)和规模(衰减)。 |
logistic([loc, scale, size]) |
从逻辑分布中绘制样本。 |
lognormal([mean, sigma, size]) |
从对数正态分布中绘制样本。 |
logseries(p[, size]) |
从对数序列分布中绘制样本。 |
multinomial(n, pvals[, size]) |
从多项分布中绘制样本。 |
multivariate_normal(mean, cov[, size, ...]) |
从多元正态分布中随机抽取样本。 |
negative_binomial(n, p[, size]) |
从负二项分布中抽取样本。 |
noncentral_chisquare(df, nonc[, size]) |
从非中心卡方分布中绘制样本。 |
noncentral_f(dfnum, dfden, nonc[, size]) |
从非中心 F 分布中绘制样本。 |
normal([loc, scale, size]) |
从正态(高斯)分布中抽取随机样本。 |
pareto(a[, size]) |
从具有指定形状的 Pareto II 或 Lomax 分布中抽取样本。 |
poisson([lam, size]) |
从泊松分布中抽取样本。 |
power(a[, size]) |
从具有正指数 a - 1 的幂分布中在 [0, 1] 中抽取样本。 |
rayleigh([scale, size]) |
从 Rayleigh 分布中抽取样本。 |
standard_cauchy([size]) |
从具有模式=0 的标准柯西分布中抽取样本。 |
standard_exponential([size]) |
从标准指数分布中抽取样本。 |
standard_gamma(shape[, size]) |
从标准 Gamma 分布中抽取样本。 |
standard_normal([size]) |
从标准正态分布(均值=0,标准差=1)中抽取样本。 |
standard_t(df[, size]) |
从具有 df 自由度的标准学生 t 分布中抽取样本。 |
triangular(left, mode, right[, size]) |
从区间 [left, right] 上的三角形分布中抽取样本。 |
uniform([low, high, size]) |
从均匀分布中抽取样本。 |
vonmises(mu, kappa[, size]) |
从 von Mises 分布中抽取样本。 |
wald(mean, scale[, size]) |
从 Wald 或反高斯分布中抽取样本。 |
weibull(a[, size]) |
从 Weibull 分布中抽取样本。 |
zipf(a[, size]) |
从 Zipf 分布中抽取样本。 |
Functions in numpy.random
上述 RandomState 方法中的许多方法以函数的形式导出到 numpy.random 这种用法是不推荐的,因为它是通过一个全局的 RandomState 实例实现的,这在两个方面都是不建议的:
-
它使用全局状态,这意味着结果会随着代码的更改而变化。
-
它使用一个
RandomState而不是更现代的Generator。
出于向后兼容的传统原因,我们不会更改这个。
beta(a, b[, size]) |
从 Beta 分布中抽取样本。 |
|---|---|
binomial(n, p[, size]) |
从二项分布中抽取样本。 |
bytes(length) |
返回随机字节。 |
chisquare(df[, size]) |
从卡方分布中抽取样本。 |
choice(a[, size, replace, p]) |
从给定的 1-D 数组中生成随机样本。 |
dirichlet(alpha[, size]) |
从 Dirichlet 分布中抽取样本。 |
exponential([scale, size]) |
从指数分布中抽取样本。 |
f(dfnum, dfden[, size]) |
从 F 分布中抽取样本。 |
gamma(shape[, scale, size]) |
从 Gamma 分布中抽取样本。 |
geometric(p[, size]) |
从几何分布中抽取样本。 |
get_state([legacy]) |
返回代表生成器内部状态的元组。 |
gumbel([loc, scale, size]) |
从 Gumbel 分布中抽取样本。 |
hypergeometric(ngood, nbad, nsample[, size]) |
从超几何分布中抽取样本。 |
laplace([loc, scale, size]) |
从具有指定位置(或均值)和尺度(衰减)的拉普拉斯或双指数分布中抽取样本。 |
logistic([loc, scale, size]) |
从 logistic 分布中抽取样本。 |
lognormal([mean, sigma, size]) |
从对数正态分布中抽取样本。 |
logseries(p[, size]) |
从对数级数分布中抽取样本。 |
multinomial(n, pvals[, size]) |
从多项分布中抽取样本。 |
multivariate_normal(mean, cov[, size, ...]) |
从多变量正态分布中抽取随机样本。 |
negative_binomial(n, p[, size]) |
从负二项分布中抽取样本。 |
noncentral_chisquare(df, nonc[, size]) |
从非中心卡方分布中抽取样本。 |
noncentral_f(dfnum, dfden, nonc[, size]) |
从非中心 F 分布中抽取样本。 |
normal([loc, scale, size]) |
从正态(高斯)分布中抽取随机样本。 |
pareto(a[, size]) |
从参数化 Pareto II 或 Lomax 分布中抽取样本。 |
permutation(x) |
随机排列一个序列,或返回一个随机排列的范围。 |
poisson([lam, size]) |
从泊松分布中抽取样本。 |
power(a[, size]) |
从具有正指数 a-1 的幂分布中抽取[0, 1]范围内的样本。 |
rand(d0, d1, ..., dn) |
返回给定形状的随机值。 |
randint(low[, high, size, dtype]) |
返回从low(包含)到high(不含)之间的随机整数。 |
randn(d0, d1, ..., dn) |
从“标准正态”分布中返回样本(或样本)。 |
random([size]) |
返回半开区间 0.0, 1.0)内的随机浮点数。 |
[random_integers(low[, high, size]) |
返回类型为np.int_的随机整数,范围在low和high之间(包含边界)。 |
random_sample([size]) |
返回半开区间 0.0, 1.0)内的随机浮点数。 |
[ranf |
这是random_sample的别名。 |
rayleigh([scale, size]) |
从瑞利分布中抽取样本。 |
sample |
这是random_sample的别名。 |
seed([seed]) |
重新设置单例 RandomState 实例的种子。 |
set_state(state) |
从元组中设置生成器的内部状态。 |
shuffle(x) |
通过对其内容进行洗牌而就地修改序列。 |
standard_cauchy([size]) |
从标准柯西分布中抽取样本,其中模式=0。 |
standard_exponential([size]) |
从标准指数分布中抽取样本。 |
standard_gamma(shape[, size]) |
从标准伽玛分布中抽取样本。 |
standard_normal([size]) |
从标准正态分布(均值=0,标准差=1)中抽取样本。 |
standard_t(df[, size]) |
从具有df自由度的标准学生 t 分布中抽取样本。 |
triangular(left, mode, right[, size]) |
从区间[left, right]上的三角分布中抽取样本。 |
uniform([low, high, size]) |
从均匀分布中抽取样本。 |
vonmises(mu, kappa[, size]) |
从冯·米塞斯分布中抽取样本。 |
wald(mean, scale[, size]) |
从瓦尔德分布或逆高斯分布中抽取样本。 |
weibull(a[, size]) |
从威布尔分布中抽取样本。 |
zipf(a[, size]) |
从齐普夫分布中抽取样本。 |
种子和状态
get_state([legacy]) |
返回表示生成器内部状态的元组。 |
|---|---|
set_state(state) |
从元组中设置生成器的内部状态。 |
seed([seed]) |
重新设置传统的 MT19937 BitGenerator 的种子 |
简单随机数据
rand(d0, d1, ..., dn) |
给定形状的随机值。 |
|---|---|
randn(d0, d1, ..., dn) |
从“标准正态”分布中返回一个样本(或多个样本)。 |
randint(low[, high, size, dtype]) |
从low(包括)到high(不包括)之间返回随机整数。 |
random_integers(low[, high, size]) |
在low和high之间(包括两端)生成类型为np.int_的随机整数。 |
random_sample([size]) |
返回半开区间 0.0, 1.0)内的随机浮点数。 |
[choice(a[, size, replace, p]) |
从给定的 1-D 数组中生成随机样本 |
bytes(length) |
返回随机字节。 |
排列
shuffle(x) |
通过对其内容进行洗牌来就地修改一个序列。 |
|---|---|
permutation(x) |
随机排列一个序列,或返回一个排列后的范围。 |
分布
beta(a, b[, size]) |
从 Beta 分布中抽取样本。 |
|---|---|
binomial(n, p[, size]) |
从二项分布中抽取样本。 |
chisquare(df[, size]) |
从卡方分布中抽取样本。 |
dirichlet(alpha[, size]) |
从 Dirichlet 分布中抽取样本。 |
exponential([scale, size]) |
从指数分布中抽取样本。 |
f(dfnum, dfden[, size]) |
从 F 分布中抽取样本。 |
gamma(shape[, scale, size]) |
从 Gamma 分布中抽取样本。 |
geometric(p[, size]) |
从几何分布中抽取样本。 |
gumbel([loc, scale, size]) |
从 Gumbel 分布中抽取样本。 |
hypergeometric(ngood, nbad, nsample[, size]) |
从超几何分布中抽取样本。 |
laplace([loc, scale, size]) |
从指定位置(或均值)和尺度(衰减)的拉普拉斯或双指数分布中抽取样本。 |
logistic([loc, scale, size]) |
从 logistic 分布中抽取样本。 |
lognormal([mean, sigma, size]) |
从对数正态分布中抽取样本。 |
logseries(p[, size]) |
从对数级数分布中抽取样本。 |
multinomial(n, pvals[, size]) |
从多项分布中抽取样本。 |
multivariate_normal(mean, cov[, size, ...]) |
从多元正态分布中抽取随机样本。 |
negative_binomial(n, p[, size]) |
从负二项分布中抽取样本。 |
noncentral_chisquare(df, nonc[, size]) |
从非中心卡方分布中抽取样本。 |
noncentral_f(dfnum, dfden, nonc[, size]) |
从非中心 F 分布中抽取样本。 |
normal([loc, scale, size]) |
从正态(高斯)分布中抽取随机样本。 |
pareto(a[, size]) |
从指定形状的 Pareto II 或 Lomax 分布中抽取样本。 |
poisson([lam, size]) |
从泊松分布中抽取样本。 |
power(a[, size]) |
从具有正指数 a-1 的幂分布中的[0, 1]中抽取样本。 |
rayleigh([scale, size]) |
从瑞利分布中抽取样本。 |
standard_cauchy([size]) |
从具有模=0 的标准柯西分布中抽取样本。 |
standard_exponential([size]) |
从标准指数分布中抽取样本。 |
standard_gamma(shape[, size]) |
从标准伽玛分布中抽取样本。 |
standard_normal([size]) |
从标准正态分布(均值=0,标准差=1)中抽取样本。 |
standard_t(df[, size]) |
从具有df自由度的标准学生 t 分布中抽取样本。 |
triangular(left, mode, right[, size]) |
从区间[left, right]上的三角分布抽取样本。 |
uniform([low, high, size]) |
从均匀分布中抽取样本。 |
vonmises(mu, kappa[, size]) |
从 von Mises 分布中抽取样本。 |
wald(mean, scale[, size]) |
从 Wald 或逆高斯分布中抽取样本。 |
weibull(a[, size]) |
从威布尔分布中抽取样本。 |
zipf(a[, size]) |
从 Zipf 分布中抽取样本。 |
在 numpy.random中的函数
Many of the RandomState methods above are exported as functions in numpy.random这种用法是不鼓励的,因为它是通过一个全局的RandomState实例来实现的,这在两个方面都不被建议:
-
它使用全局状态,这意味着结果将随着代码的更改而改变
-
它使用
RandomState而不是更现代的Generator。
由于向后兼容的遗留原因,我们不会对此进行更改。
beta(a, b[, size]) |
从 Beta 分布中抽取样本。 |
|---|---|
binomial(n, p[, size]) |
从二项分布中抽取样本。 |
bytes(length) |
返回随机字节。 |
chisquare(df[, size]) |
从卡方分布中抽取样本。 |
choice(a[, size, replace, p]) |
从给定的 1-D 数组生成随机样本 |
dirichlet(alpha[, size]) |
从 Dirichlet 分布中抽取样本。 |
exponential([scale, size]) |
从指数分布中抽取样本。 |
f(dfnum, dfden[, size]) |
从 F 分布中抽取样本。 |
gamma(shape[, scale, size]) |
从 Gamma 分布中抽取样本。 |
geometric(p[, size]) |
从几何分布中抽取样本。 |
get_state([legacy]) |
返回表示生成器内部状态的元组。 |
gumbel([loc, scale, size]) |
从 Gumbel 分布中抽取样本。 |
hypergeometric(ngood, nbad, nsample[, size]) |
从超几何分布中抽取样本。 |
laplace([loc, scale, size]) |
从具有指定位置(或均值)和尺度(衰减)的拉普拉斯或双指数分布中抽取样本。 |
logistic([loc, scale, size]) |
从 logistic 分布中抽取样本。 |
lognormal([mean, sigma, size]) |
从对数正态分布中抽取样本。 |
logseries(p[, size]) |
从对数序列分布中抽取样本。 |
multinomial(n, pvals[, size]) |
从多项式分布中抽取样本。 |
multivariate_normal(mean, cov[, size, ...]) |
从多元正态分布中抽取随机样本。 |
negative_binomial(n, p[, size]) |
从负二项分布中抽取样本。 |
noncentral_chisquare(df, nonc[, size]) |
从非中心卡方分布中抽取样本。 |
noncentral_f(dfnum, dfden, nonc[, size]) |
从非中心 F 分布中抽取样本。 |
normal |
从正态(高斯)分布中抽取随机样本。 |
pareto(a[, size]) |
从具有指定形状的 Pareto II 或 Lomax 分布中抽取样本。 |
permutation(x) |
随机排列一个序列,或返回一个排列范围。 |
poisson([lam, size]) |
从泊松分布中抽取样本。 |
power(a[, size]) |
从具有正指数 a-1 的幂分布中抽取样本,范围在 [0, 1]。 |
rand(d0, d1, ..., dn) |
给定形状中的随机值。 |
randint(low[, high, size, dtype]) |
返回从 low(包括)到 high(不包括)的随机整数。 |
| randn(d0, d1, ..., dn) | 从 "标准正态" 分布中返回样本(或样本)。 |
| 随机数([size]) | 返回半开区间 0.0, 1.0) 中的随机浮点数。 |
| [随机整数(low[, high, size]) | 生成类型为 np.int_ 的介于 low 和 high 之间的随机整数,包括 high。 |
| 随机样本([size]) | 返回半开区间 0.0, 1.0) 中的随机浮点数。 |
| [ranf | 这是 random_sample 的别名。 |
| 瑞利分布([scale, size]) | 从瑞利分布中抽取样本。 |
| 样本 | 这是 random_sample 的别名。 |
| 种子([seed]) | 重新播种单例 RandomState 实例。 |
| 设置状态(state) | 从元组中设置生成器的内部状态。 |
| 洗牌(x) | 通过重排其内容就地修改序列。 |
| 标准柯西分布([size]) | 从以 mode = 0 的标准柯西分布中抽取样本。 |
| 标准指数分布 | 从标准指数分布中抽取样本。 |
| 标准 Gamma 分布(shape[, size]) | 从标准 Gamma 分布中抽取样本。 |
| 标准正态分布([size]) | 从标准正态分布(平均值=0,标准差=1)中抽取样本。 |
| 标准 t 分布(df[, size]) | 从具有 df 自由度的标准学生 t 分布中抽取样本。 |
三角(left, mode, right[, size]) |
从区间[left, right]上的三角分布中抽取样本。 |
uniform([low, high, size]) |
从均匀分布中抽取样本。 |
vonmises(mu, kappa[, size]) |
从 von Mises 分布中抽取样本。 |
wald(mean, scale[, size]) |
从 Wald 或逆高斯分布中抽取样本。 |
weibull(a[, size]) |
从 Weibull 分布中抽取样本。 |
zipf(a[, size]) |
从 Zipf 分布中抽取样本。 |
比特生成器
原文:
numpy.org/doc/1.26/reference/random/bit_generators/index.html
由Generator生成的随机值源自一个比特生成器。比特生成器不直接提供随机数,仅包含用于播种、获取或设置状态、跳转或前进状态以及访问低级包装器的方法,以供能够高效访问提供的函数的代码使用,例如 numba。
支持的比特生成器
包含的比特生成器有:
-
PCG-64 - 默认值。一种快速生成器,可以通过任意数量进行推进。请参阅
advance的文档。PCG-64 的周期为 (2^{128})。有关此类 PRNG 的更多详细信息,请参阅 PCG 作者页面。 -
PCG-64 DXSM - PCG-64 的升级版本,在并行环境中具有更好的统计特性。有关这些改进的更多信息,请参阅 使用 PCG64DXSM 升级 PCG64。
-
MT19937 - 标准的 Python 比特生成器。添加了一个
MT19937.jumped函数,返回一个新的生成器,其状态就像已经进行了 (2^{128}) 次抽样。 -
菲洛克斯 - 一种基于计数器的生成器,可以通过任意数量的步骤进行推进或生成独立流。查看 Random123 页面以获取有关这类比特生成器的更多详细信息。
-
SFC64 - 基于随机可逆映射的快速生成器。通常是这四种生成器中最快的。查看 SFC 作者页面 以获取(一点)更多关于这类比特生成器的详细信息。
BitGenerator([seed]) |
通用比特生成器的基类,基于不同的算法提供随机比特流。 |
|---|
-
MT19937
-
PCG64
-
PCG64DXSM
-
菲洛克斯
-
SFC64
支持的比特生成器
包含的比特生成器有:
-
PCG-64 - 默认值。一种快速生成器,可以通过任意数量进行推进。请参阅
advance的文档。PCG-64 的周期为 (2^{128})。有关此类 PRNG 的更多详细信息,请参阅 PCG 作者页面。 -
PCG-64 DXSM - PCG-64 的升级版本,在并行环境中具有更好的统计特性。有关这些改进的更多信息,请参阅 使用 PCG64DXSM 升级 PCG64。
-
MT19937 - 标准的 Python 比特生成器。添加了一个
MT19937.jumped函数,它返回一个具有与假设 (2^{128}) 次抽取相同状态的新生成器。 -
Philox - 一个基于计数器的生成器,能够被任意数量的步骤推进或生成独立流。关于这类比特生成器的更多详情,请参见Random123页面。
-
SFC64 - 一种基于随机可逆映射的快速生成器。通常是四种中最快的生成器。关于这个生成器的(少许)更多细节,请参见SFC 作者页面。
BitGenerator([seed]) |
泛型比特生成器的基类,根据不同的算法提供一系列基于不同算法的随机比特流。 |
|---|
-
MT19937
-
PCG64
-
PCG64DXSM
-
Philox
-
SFC64
种子和熵
一个比特生成器(BitGenerator)提供一系列随机值。为了生成可复现的流,比特生成器支持通过种子设置其初始状态。所有提供的比特生成器都将任意大小的非负整数或此类整数列表作为种子。比特生成器需要获取这些输入并将它们处理成高质量的内部状态以供比特生成器使用。numpy 中的所有比特生成器都将该任务委托给SeedSequence,后者使用哈希技术确保即使是低质量的种子也能生成高质量的初始状态。
from numpy.random import PCG64
bg = PCG64(12345678903141592653589793)
SeedSequence被设计为实现最佳实践的便利工具。我们建议一个随机程序默认使用操作系统提供的熵,以确保每次运行都不同。该程序应该打印或记录该熵。为了重现过去的值,程序应该允许用户通过某种机制提供该值,通常是命令行参数,以便用户可以重新输入该熵以重现结果。SeedSequence可以处理除了与用户通信之外的所有内容,这取决于你。
from numpy.random import PCG64, SeedSequence
# Get the user's seed somehow, maybe through `argparse`.
# If the user did not provide a seed, it should return `None`.
seed = get_user_seed()
ss = SeedSequence(seed)
print('seed = {}'.format(ss.entropy))
bg = PCG64(ss)
我们默认使用从操作系统收集的 128 位整数作为熵。这是初始化 numpy 中所有生成器所需的良好熵量。我们不建议在一般情况下使用小于 32 位的小种子。只使用少量种子来实例化较大的状态空间意味着有些初始状态是不可能到达的。如果每个人都使用这样的值,这会产生一些偏差。
结果本身不会有任何问题,即使种子为 0 也可以,这要归功于SeedSequence的处理。 如果您只是需要一些固定值进行单元测试或调试,可以随意使用任何种子。 但如果您希望从结果中推断或发布结果,则从更大的种子集中抽取是一种良好的做法。
如果您需要“离线”生成一个好的种子,则SeedSequence().entropy或使用标准库中的secrets.randbits(128)都是方便的方法。
如果您需要并行运行几次随机模拟,最佳实践是为每个模拟构建一个随机生成器实例。 确保随机流具有不同的初始状态,您可以使用SeedSequence的spawn方法。 例如,这里我们构造了 12 个实例的列表:
from numpy.random import PCG64, SeedSequence
# High quality initial entropy
entropy = 0x87351080e25cb0fad77a44a3be03b491
base_seq = SeedSequence(entropy)
child_seqs = base_seq.spawn(12) # a list of 12 SeedSequences
generators = [PCG64(seq) for seq in child_seqs]
如果您已经有了初始的随机生成器实例,您可以使用spawn方法简化以上操作:
from numpy.random import PCG64, SeedSequence
# High quality initial entropy
entropy = 0x87351080e25cb0fad77a44a3be03b491
base_bitgen = PCG64(entropy)
generators = base_bitgen.spawn(12)
另一种方法是利用事实,即SeedSequence可以通过一组元素进行初始化。 这里我们使用基础熵值和整数worker_id
from numpy.random import PCG64, SeedSequence
# High quality initial entropy
entropy = 0x87351080e25cb0fad77a44a3be03b491
sequences = [SeedSequence((entropy, worker_id)) for worker_id in range(12)]
generators = [PCG64(seq) for seq in sequences]
请注意,通过后一种方法产生的序列将与通过spawn构造的序列不同。
SeedSequence([entropy, spawn_key, pool_size]) |
SeedSequence 以可复现的方式混合熵源,为独立且很可能不重叠的 BitGenerators 设置初始状态。 |
|---|
升级 PCG64 为 PCG64DXSM
在大规模并行上下文中使用PCG64 BitGenerator 已经显示出了统计上的弱点,在 numpy 1.17 发布时并不明显。大多数用户永远不会观察到这个弱点,并且可以继续安全使用 PCG64. 我们引入了一个新的 PCG64DXSM BitGenerator,它最终将成为将来版本中由 default_rng 使用的新默认 BitGenerator 实现。 PCG64DXSM 解决了统计上的弱点,同时保留了 PCG64 的性能和特性。
这会对我产生影响吗?
如果您
- 只使用单个
Generator实例,- 只使用
RandomState或numpy.random中的函数,- 只使用
PCG64.jumped方法来生成并行流,- 明确使用
BitGenerator而非PCG64,
那么这个弱点对你没有任何影响。继续进行。
如果您使用 default_rng或 SeedSequence.spawn 创建的适量并行流,在 1000s 范围内,那么观察到这个弱点的机会是微乎其微的。您可以继续舒适地使用 PCG64。
如果您使用了大量的并行流,数量达到百万级,并且从每个流中抽取了大量的数字,那么观察到这种弱点的机会就会变得不可忽略,尽管仍然很小。这样的用例示例可能是一个非常大的分布式强化学习问题,每个问题都有数百万次长的蒙特卡洛模拟,每个模拟都生成了数十亿次的随机数。这种用例应该明确地考虑使用PCG64DXSM或者另一个现代的BitGenerator,比如SFC64或Philox,但是几乎可以肯定你可能计算过的任何旧结果都是无效的。无论如何,这种弱点属于一种生日悖论的碰撞。也就是说,几百万个并行流中的一对,考虑在一起,可能不会通过一套严格的随机性检验。其余数百万个流都会完全正常,而在大多数应用中,坏对在整个计算中剩余流的影响很可能会被淹没。
技术细节
像许多伪随机数生成算法一样,PCG64是由一个转换函数构建的,这个函数会推进一个 128 位的状态,以及一个输出函数,将 128 位的状态混合成一个 64 位整数输出。PCG 系列伪随机数生成器的一个指导设计原则之一是在转换函数和输出函数之间平衡计算成本(和伪随机性强度)。转换函数是一个 128 位线性同余生成器(LCG),它包括将 128 位状态与固定乘法常数相乘,然后加上用户选择的增量,在 128 位模算术中。尽管 128 位 LCG 本身足够大,可以通过简单的输出函数在自身上通过严格的统计测试,但 LCG 是已知有弱点的伪随机数生成器。PCG64的输出函数旨在通过“恰到好处”的位混合来修补一些已知的弱点,以帮助在不增加太多计算成本的情况下提高统计特性。
已知这个随机数发生器(LCG)的一个弱点是,使用步数为 2 的 N 次方(bg.advance(2**N))会使低位的 N 个 bit 与上一状态相同。对于一个逐个生成的单一流而言,这只会产生微不足道的影响。剩下的(128-N)个 bit 会提供足够的伪随机性,对于在单一流中观察到的任何实际 N 而言,这些 bit 都会被混合在一起。这就是为什么如果你的应用中只使用单一流,那你就不用担心这个问题。类似地,PCG64.jumped方法使用了经过精心选择的步数来避免这些冲突。然而,一旦你开始创建“随机初始化”的并行流,要么通过多次调用default_rng来使用操作系统熵,要么通过使用SeedSequence.spawn,那么我们就需要考虑有多少低位需要“冲突”才能创建一对糟糕的流,然后评估创建这种冲突的概率。根据经验确定,如果共享状态的低 58 位和共享增量,那么当这对流相互交错时,在绘制了数 GB 的数据后,会在合理的时间内使PractRand失败。根据生日悖论的标准计算,对于 58 位的冲突,我们可以看到我们可以创建(2^{29}),或者大约五亿个流,这是冲突概率变得很高的时候。五亿个流是相当多的,而且每个流需要绘制大量的数据才能使统计相关性显现在甚至是严格的PractRand测试中。但是,对于分布式强化学习等非常大型的应用程序来说,这些都是可能会出现的问题。我们有理由期待,即使在这些应用程序中,冲突可能也不会对最终结果产生实际影响,因为统计问题只局限于发生冲突的那一对。
现在,让我们考虑增量不受限制的情况。我们的PCG64实现同时对状态和增量进行种子化;也就是说,两次调用default_rng(几乎肯定)具有不同的状态和增量。在我们第一次发布时,我们认为有种子增量会提供一定程度的额外保护,即在一对流中观察到相关性(PractRand失败)需要在状态空间和增量空间中都“接近”。如果这是真的,那么碰撞的“瓶颈”就是SeedSequence内部的 128 位熵池大小(128 位的碰撞属于“天方夜谭般不可能发生”类别)。不幸的是,这不是真的。
LCG(线性同余发生器)的一个已知特性是不同的增量会创建不同的流,但它们之间存在已知关系。每个 LCG 都有一个遍历所有(2^{128})个不同的 128 位状态的轨道。具有不同增量的两个 LCG 之间是相关的,因为可以“旋转”第一个 LCG 的轨道(将其前进若干步,这些步数可以从两个增量计算出来),以便两个 LCG 始终具有相同的状态,同时可能会有一个加法常数和比特的反转。然后,如果同时迭代两个流,状态将始终由相同的加法常数(和反转,如果存在的话)相关联。请记住,PCG64由转移函数(LCG)和输出函数构成。人们预期输出函数的混淆效果足以使不同的流在实践中独立(即“通过PractRand测试”),除非两个增量在路径上出现了病态关系(例如 1 和 3)。我们在PCG64中实现的当时标准的 PCG 算法的输出函数 XSL-RR 事实证明对掩盖我们上述描述的基础 LCG 的 58 位冲突太弱。对于任何给定的增量对,状态的“冲突”空间的大小是相同的,因此对于这种弱点,增量提供的额外独立性并不能转化为对PractRand可以检测到的统计相关性的额外保护。
幸运的是,加强输出功能能够纠正这个弱点,并确实将不同的增量提供的额外差异转化为对低位碰撞的额外保护。 要归功于PCG 作者,在新的BitGenerator系统的长期诞生过程中,她根据相关讨论开发了更为强大的输出功能。在那个时候,我们 NumPy 开发人员选择“保守”,并使用了经过更长时间测试的 XSL-RR 变体。 DXSM 输出功能采用了在强整数哈希中使用的“xorshift-multiply”构造,其雪崩特性比 XSL-RR 输出功能好得多。虽然存在能够诱导“坏”加法常数的“病态”增量对,使得两个流相关联,但绝大多数对会诱导“好”加法常数,使得 LCG 状态的仅有差异流几乎成为独立的输出流。事实上,现在我们曾经关于PCG64所做的声明实际上也适用于PCG64DXSM:碰撞是可能的,但两个流必须同时在 128 位状态空间中“接近”且在 127 位增量空间中“接近”,因此这比在 128 位内部SeedSequence池中发生碰撞的微不足道的机会更不太可能。 DXSM 输出功能比 XSL-RR 更具计算密集性,在大多数机器上,LCG 中的一些优化可以弥补性能损失,因此PCG64DXSM是一个很好、安全的升级。当然,还有无数更强大的输出功能可以考虑,但大多数会有更大的计算成本,而 DXSM 输出功能现在已经通过PractRand进行了大量的 CPU 周期测试。
这会影响我吗?
如果您
- 只使用单个
Generator实例,- 只使用
RandomState或numpy.random中的函数,- 只使用
PCG64.jumped方法生成并行流,- 如果明确使用
BitGenerator而不是PCG64,
那么这个弱点对你完全没有影响。继续进行。
如果您使用了适度数量的由default_rng或SeedSequence.spawn创建的并行流,数量在 1000 左右,那么观察到这个弱点的机会几乎可以忽略不计。您可以继续舒适地使用PCG64。
如果您使用了非常大数量的并行流,数量在百万级,并且每个流中抽取了大量的数字,那么观察到这个弱点的机会可能会变得不可忽略,尽管仍然很小。这样一个使用案例的例子是一个非常大的分布式强化学习问题,每个都生成了数亿次随机数抽取的长蒙特卡洛模拟。这样的使用情况应该明确考虑使用PCG64DXSM或另一个现代BitGenerator,如SFC64或Philox,但是您可能计算的任何旧结果都不太可能无效。无论如何,这个弱点是一种生日悖论碰撞。也就是说,数百万个并行流中的一对考虑在一起,可能无法通过一组严格的随机性检验。剩下的数百万个流都是完全正常的,并且在大多数应用中,剩余流的影响很可能会淹没整个计算中的坏对的效果。
技术细节
像许多 PRNG 算法一样,PCG64是由一个转换函数构造的,它推进一个 128 位状态,以及一个输出函数,将 128 位状态混合成一个 64 位整数进行输出。PCG 系列 PRNG 的一个指导性设计原则之一是在转换函数和输出函数之间平衡计算成本(和伪随机强度)。转换函数是一个 128 位线性同余生成器(LCG),它包括将 128 位状态与固定乘法常数相乘,然后在 128 位模算术中加上用户选择的增量。LCGs 是经过深入分析的 PRNG,已知存在一些弱点,尽管 128 位 LCGs 足够大,足以单独通过严格的统计测试,仅使用微不足道的输出函数。PCG64的输出函数旨在通过“恰到好处”的比特位混淆,修复一些已知的弱点,以协助统计特性,而不增加过多的计算成本。
其中一个已知的弱点是,通过步数为 2 的幂(bg.advance(2**N))推进 LCG 的状态将使刚离开的较低的 N 位与当前状态相同。对于按顺序抽取的单个流,这影响不大。剩下的 (128-N) 位提供了足够的伪随机性,对于任何在单个流中可以观察到的实际 N 来说,都会混合其中,这就是为什么如果你的应用程序只使用单个流,则不需要担心这一点。同样地,PCG64.jumped 方法使用了精心选择的步数,以避免创建这些冲突。但是,一旦开始创建“随机初始化”的并行流,无论是通过反复调用 default_rng 使用操作系统熵,还是使用 SeedSequence.spawn,那么我们就需要考虑需要“碰撞”多少较低的位,以创建一对糟糕的流,然后评估创建这种碰撞的概率。经验上,已经确定如果共享状态的较低 58 位并共享增量,则这对流,在交错时,将在抽取几吉字节的数据后合理时间内失败 PractRand 测试。根据 58 位碰撞的生日悖论标准计算,我们可以看到可以创建 (2^{29}),或约半十亿个流,这是碰撞概率变高的时候。半十亿个流是非常多的,甚至在严格的 PractRand 测试中,每个流需要抽取的数据量变得明显相关之前,统计学上的相关性变得明显之前。但是这是对于非常大的应用程序(如分布式强化学习)的前景。有理由期望,即使在这些应用中,碰撞可能对总体结果产生实际影响,因为统计问题仅限于碰撞对。
现在,让我们考虑当增量不受限制时的情况。我们的PCG64的实现同时对状态和增量进行种子化;也就是说,两次调用default_rng(几乎可以确定)具有不同的状态和增量。在我们的第一个发布版本中,我们认为拥有种子增量会提供一定程度的额外保护,即必须在状态空间和增量空间中“接近”,才能观察到一对流的相关性(PractRand失败)。如果这是真的,那么碰撞的“瓶颈”将是SeedSequence内的 128 位熵池大小(128 位碰撞属于“不可思议的罕见”类别)。不幸的是,这并不是真的。
LCG 的已知特性之一是不同的增量会创建不同的流,但具有已知的关系。每个 LCG 都有一个轨道,遍历所有(2^{128})个不同的 128 位状态。具有不同增量的两个 LCG 相关联,因为可以“旋转”第一个 LCG 的轨道(将其前进一个我们可以从两个增量计算出的步骤数),以便然后两个 LCG 将始终具有相同的状态,最多一个加法常数和可能位的反转。然后,如果同时迭代两个流,则状态将始终保持由相同的加法常数(如果存在)和位反转相关。请记住,PCG64由转换函数(LCG)和输出函数构成。人们预期输出函数的混淆效果足够强大,使得不同的流在实践上相互独立(即“通过PractRand测试”),除非两个增量与彼此病态相关(例如 1 和 3)。我们在PCG64中实现的当时标准 PCG 算法的输出函数 XSL-RR 事实证明对上述我们描述的基础 LCG 的 58 位碰撞太弱。对于任何给定的增量对,状态的“碰撞”空间大小是相同的,因此对于这种弱点,增量提供的额外差异性并不能转化为PractRand能够检测到的统计相关性的额外保护。
幸运的是,加强输出功能能够纠正这一弱点,确实将由不同增量提供的额外清晰度转化为对这些低位冲突的额外保护。要赞扬PCG 作者的是,她在新的BitGenerator系统漫长的诞生过程中,根据相关讨论开发了一个更强大的输出功能。我们 NumPy 开发人员选择“保守”,使用了在那个时候经过更长时间测试的 XSL-RR 变体。DXSM 输出功能采用了在强整数哈希中使用的“xorshift-multiply”构造,其具有比 XSL-RR 输出功能更好的雪崩特性。虽然有一些“病态”的增量对,会引发与两个流相关的“不好”的加法常量,但绝大多数对会引发“好”的加法常量,使简单区分的 LCG 状态流变成实际上独立的输出流。事实上,我们曾经关于PCG64提出的声明现在实际上也适用于PCG64DXSM:碰撞是可能的,但两个流必须同时在 128 位状态空间和127 位增量空间内“接近”,这比在 128 位内部SeedSequence池内碰撞的微不足道的机会更不太可能。DXSM 输出功能比 XSL-RR 更耗计算资源,但在大多数机器上通过 LCG 的一些优化弥补了性能损失,因此PCG64DXSM是一个不错、安全的升级选择。当然,有无限多种更强大的输出功能可供考虑,但大多数将具有更大的计算成本,而 DXSM 输出功能目前已经通过PractRand获得了许多 CPU 周期的测试。
兼容性政策
numpy.random的兼容性政策比 NumPy 的其他部分要严格一些。伪随机性的用户通常需要能够复现相同种子在细节上的运行情况(所谓的“流兼容性”),因此我们试图在这些需求与增强算法的灵活性之间取得平衡。NEP 19描述了这一政策的发展。
我们强制执行的主要兼容性类型是在某些条件下从运行到运行的流兼容性。如果您使用相同的BitGenerator,相同的种子创建一个Generator ,在相同的numpy版本的相同环境的相同机器上使用相同的参数执行相同的方法调用序列,您应该得到相同的数字流。请注意,这些条件非常严格。NumPy 无法控制许多限制我们能够保证的事情。例如,不同的 CPU 以不同方式实现浮点运算,这可能导致某些边缘情况的差异并传播到剩余的数字流。另一个例子,Generator.multivariate_normal 使用了来自numpy.linalg的矩阵分解。即使在同一平台上,numpy的不同版本可能使用来自其链接的 LAPACK 的不同版本的矩阵分解算法,导致Generator.multivariate_normal 返回完全不同(但同样有效!)的结果。我们努力倾向于更能抵抗这些影响的算法,但这总是不完美的。
注意
大多数Generator方法允许您从分布中绘制多个值作为数组。该数组的请求大小是一个参数,符合以上策略的目的。调用rng.random() 5 次并不能 保证 给出与rng.random(5)相同的数字。我们保留决定对不同大小的块使用不同算法的能力。实际上,这种情况很少发生。
与 NumPy 的其余部分一样,我们通常会保持版本之间的 API 源兼容性。如果我们必须进行破坏 API 的更改,那么我们将会在适当的弃用期和警告下进行,根据 general NumPy policy。
为了引入新功能或提高Generator 或 default_rng 的性能,我们谨慎地允许破坏流兼容性。此类更改将被视为功能,因此它们不会比特性的标准发布节奏更快(即在X.Y上的发布,而不是X.Y.Z)。为此目的,慢性不会被视为缺陷。破坏流兼容性的正确性 bug 修复可以在 bug 修复版本中发生,正如惯例,但开发人员应考虑是否可以等待下一个功能发布。我们鼓励开发人员在改进与兼容性破坏造成的用户痛苦之间进行权衡。一个值得改进的例子是改变算法以显著提高性能,例如从Box-Muller transform的高斯变量生成方法转移到更快的 Ziggurat algorithm。一个不鼓励的改进例子将是微调 Ziggurat 表,仅仅为了小幅性能提升。
注意
特别是,允许default_rng 更改它使用的默认 BitGenerator(同样,需要谨慎和充分的提前警告)。
一般来说,BitGenerator 类拥有更强的版本之间流兼容性的保证。这使它们成为下游用户的更牢固的构建模块,需要使用者。它们有限的 API 界面使它们更容易地在版本之间保持兼容性。查看每个BitGenerator 类的文档字符串,了解其个别的兼容性保证。
遗留的RandomState和相关方便的函数拥有更严格的版本兼容性保证。出于NEP 19中概述的原因,在 NumPy 的早期开发阶段,我们对它们的版本间稳定性做出了更强的承诺。仍然存在一些有限的使用情况需要这种兼容性(比如为测试生成数据),因此我们尽可能地保持兼容性。不会对RandomState进行任何修改,甚至不能修复正确性错误。我们可以在一些灰色地带进行一些微小的修复,以使RandomState在 NumPy 内部发生变化时仍能正常工作,以及一些文档字符串的修复。然而,之前提到的关于从机器到机器和构建到构建的变异性的注意事项同样适用于RandomState和Generator。
并行随机数生成
有四种主要策略可以用来在多个进程(本地或分布式)中产生可重复的伪随机数。
SeedSequence 的生成
NumPy 允许您通过它们的 spawn() 方法生成新的(非常高的概率)独立的BitGenerator和Generator实例。这种生成是通过用于初始化比特生成器随机流的SeedSequence实现的。
SeedSequence 实现了一个算法,用于处理用户提供的种子,通常作为某种大小的整数,并将其转换为BitGenerator的初始状态。它使用哈希技术确保低质量的种子被转换为高质量的初始状态(至少,有很高的概率)。
例如,MT19937 的状态由 624 个 uint32 整数组成。一个简单的方法是将一个 32 位整数种子设置为状态的最后一个元素,其余为 0。这对于MT19937来说是一个有效的状态,但不是一个好的状态。梅森旋转算法如果有太多的 0会出现问题。同样,相邻的两个 32 位整数种子(例如 12345 和 12346)会产生非常相似的流。
SeedSequence 通过使用具有良好的 雪崩效应特性 的整数哈希的连续数列,确保翻转输入中的任意位有大约 50%的机会翻转输出中的任意位,从而避免了这些问题。两个非常接近的输入种子将产生(在非常高的概率下)非常相距较远的初始状态。它还是这样构造的,您可以提供任意大小的整数或整数列表。 SeedSequence 将接受您提供的所有位并将它们混合在一起,以生成 BitGenerator 初始化所需的位数。
这些属性共同意味着我们可以安全地将通常由用户提供的种子与简单的递增计数器混合在一起,以获取 BitGenerator 状态,这些状态(在非常高的概率下)彼此独立。我们可以将这些包装成一个易于使用但难以误用的 API。
from numpy.random import SeedSequence, default_rng
ss = SeedSequence(12345)
# Spawn off 10 child SeedSequences to pass to child processes.
child_seeds = ss.spawn(10)
streams = [default_rng(s) for s in child_seeds]
为了方便起见,直接使用 SeedSequence 是不必要的。上述的 streams 可以直接通过父生成器通过 spawn 派生:
parent_rng = default_rng(12345)
streams = parent_rng.spawn(10)
子对象也可以生成子孙对象,依此类推。每个子对象都有一个 SeedSequence,其在生成的子对象树中的位置与用户提供的种子混合在一起,以生成(在非常高的概率下)独立的流。
grandchildren = streams[0].spawn(4)
此功能使您能够在进程之间无需协调的情况下对流进行本地决策,以及何时以及如何分割流。您无需预先分配空间以避免重叠,也无需从共享的全局服务请求流。这种通用的“树哈希”方案 不是 numpy 的独有特性,但尚未广泛传播。Python 提供了越来越灵活的并行化机制,而这种方案非常适合与此类用法配合使用。
使用这种方案,如果知道派生的流的数量,就可以估计碰撞的概率上限。SeedSequence默认情况下将其输入(种子和生成树路径)哈希到一个 128 位池中。在那个池中,悲观地估计碰撞的概率([1])约为(n²*2{-128}),其中*n*是生成的流的数量。如果一个程序使用了激进的百万流,约为(2),那么至少有一对它们相同的概率约为(2^{-88}),这已经是可以忽略不计的领域([2])。 ## 整数种子序列
如前一节所讨论的,SeedSequence不仅可以接受整数种子,还可以接受任意长度的(非负)整数序列。如果稍加小心,可以利用这个特性设计特设方案,以获得类似生成的安全并行 PRNG 流的安全保证。
例如,一个常见的用例是,一个工作进程为整个计算传递一个根种子整数,还有一个整数工作人员 ID(或者更精细的像作业 ID、批次 ID 或类似的东西)。如果这些 ID 是确定性且唯一地创建的,那么可以通过将 ID 和根种子整数组合成一个列表来派生可重现的并行 PRNG 流。
# default_rng() and each of the BitGenerators use SeedSequence underneath, so
# they all accept sequences of integers as seeds the same way.
from numpy.random import default_rng
def worker(root_seed, worker_id):
rng = default_rng([worker_id, root_seed])
# Do work ...
root_seed = 0x8c3c010cb4754c905776bdac5ee7501
results = [worker(root_seed, worker_id) for worker_id in range(10)]
这可以用来替换过去使用的一些不安全策略,这些策略试图将根种子和 ID 组合成单个整数种子值。例如,通常会看到用户将工作人员 ID 添加到根种子中,特别是在传统的RandomState代码中。
# UNSAFE! Do not do this!
worker_seed = root_seed + worker_id
rng = np.random.RandomState(worker_seed)
对于以这种方式构建的并行程序的任何一次运行,每个工作人员将具有不同的流。然而,很可能在不同种子的多次调用程序中获得重叠的工作人员种子集。在进行这些重复运行时,仅仅通过增加一两个根种子是很常见的(作者的亲身经历)。如果工作人员种子也是通过工作人员 ID 的小增量派生的,那么工作者的子集将返回相同的结果,导致整体结果集中的偏差。
将工作人员 ID 和根种子作为整数列表组合可以消除这种风险。懒惰的播种实践仍然是相当安全的。
此方案要求额外的 ID 必须是唯一的并且是确定性创建的。这可能需要在工作进程之间进行协调。建议将变化的 ID 放在 不变的根种子之前。spawn 在用户提供的种子后 追加 整数,因此如果您可能同时使用这种 临时 机制和生成,或者将您的对象传递给可能正在生成的库代码,那么最好在前面而不是在后面添加您的工作 ID,以避免碰撞。
# Good.
worker_seed = [worker_id, root_seed]
# Less good. It will *work*, but it's less flexible.
worker_seed = [root_seed, worker_id]
在考虑这些注意事项的情况下,针对碰撞的安全保证与前一节讨论的生成相同。算法机制也是相同的。 ## 独立流
Philox 是基于计数器的 RNG,通过使用弱加密原语对递增计数器进行加密来生成值。种子确定了用于加密的密钥。唯一的密钥创建了唯一的、独立的流。Philox 允许您绕过种子算法,直接设置 128 位密钥。类似但不同的密钥仍将创建独立的流。
import secrets
from numpy.random import Philox
# 128-bit number as a seed
root_seed = secrets.getrandbits(128)
streams = [Philox(key=root_seed + stream_id) for stream_id in range(10)]
此方案要求避免重复使用流 ID。这可能需要并行进程之间的协调。 ## 推进位生成器状态
jumped 推进位生成器的状态,好像已经抽取了大量的随机数,并返回具有此状态的新实例。具体的抽取次数因位生成器而异,范围从 (2^{64}) 到 (2^{128})。此外,好像抽取还取决于特定位生成器产生的默认无符号随机数的大小。支持 jumped 的位生成器,以及位生成器的周期、跳跃大小和默认无符号随机数的位数如下所示。
| 位生成器 | 周期 | 跳跃大小 | 每次抽取的位数 |
|---|---|---|---|
| MT19937 | (2^{19937}-1) | (2^{128}) | 32 |
| PCG64 | (2^{128}) | (~2^{127}) ([3]) | 64 |
| PCG64DXSM | (2^{128}) | (~2^{127}) ([3]) | 64 |
| Philox | (2^{256}) | (2^{128}) | 64 |
jumped 可用于生成长块,应足够长以避免重叠。
import secrets
from numpy.random import PCG64
seed = secrets.getrandbits(128)
blocked_rng = []
rng = PCG64(seed)
for i in range(10):
blocked_rng.append(rng.jumped(i))
使用jumped时,必须注意不要跳转到已经使用过的流。在上面的示例中,后续不能使用blocked_rng[0].jumped(),因为它会与blocked_rng[1]重叠。与独立流一样,如果主进程要通过跳跃来分割出 10 个以上的流,则需要从range(10, 20)开始,否则将重新创建相同的流。另一方面,如果您仔细构建这些流,则可以确保流不会重叠。##SeedSequence生成
NumPy 允许您通过其spawn()方法生成新的(高概率下的)相互独立的BitGenerator和Generator实例。这种生成由用于初始化比特生成器随机流的SeedSequence实现。
SeedSequence实现了一种算法,用于处理用户提供的种子,通常是某种大小的整数,并将其转换为BitGenerator的初始状态。它使用散列技术确保低质量的种子以非常高的概率被转换为高质量的初始状态。
例如,MT19937 的状态由 624 个 uint32 整数组成。一种朴素的方法是将一个 32 位整数种子设置为状态的最后一个元素,并将其余元素设置为 0。这是 MT19937 的一个有效状态,但不是一个好的状态。梅森旋转算法suffers if there are too many 0s。同理,相邻的两个 32 位整数种子(即 12345 和 12346)会产生非常相似的序列。
SeedSequence通过使用具有良好雪崩效应的整数哈希的连续性来避免这些问题,以确保在输入中翻转任何位的约 50%的机会会翻转输出中的任何位。两个非常接近的输入种子将产生非常远的初始状态(在非常高的概率下)。它还以一种构造方式构建,以便您可以提供任意大小的整数或整数列表。SeedSequence将获取您提供的所有位并将它们混合在一起,以生成BitGenerator初始化所需的位数。
这些属性共同意味着我们可以安全地将通常由用户提供的种子与简单的递增计数器混合在一起,以获得BitGenerator状态,这些状态(在非常高的概率下)彼此独立。我们可以将这些封装成一个易于使用且难以误用的 API。
from numpy.random import SeedSequence, default_rng
ss = SeedSequence(12345)
# Spawn off 10 child SeedSequences to pass to child processes.
child_seeds = ss.spawn(10)
streams = [default_rng(s) for s in child_seeds]
为了方便起见,不需要直接使用SeedSequence。上述的streams可以直接通过spawn从父生成器生成:
parent_rng = default_rng(12345)
streams = parent_rng.spawn(10)
子对象也可以生成子孙,依此类推。每个子对象都有一个带有其在生成的子对象树中位置的SeedSequence,将其与用户提供的种子混合在一起以生成独立的(在非常高的概率下)流。
grandchildren = streams[0].spawn(4)
这个特性让你可以在进程之间无需协调的情况下做出关于何时以及如何拆分流的本地决策。你不必预先分配空间以避免重叠或从一个共同的全局服务请求流。这种通用的“树哈希”方案并非仅限于 numpy,但尚未广泛传播。Python 提供了越来越灵活的并行化机制,并且这种方案非常适合这种用途。
使用这种方案,如果知道您派生的流的数量,可以估计碰撞的概率上限。SeedSequence默认情况下将其输入(种子和生成树路径)哈希到一个 128 位池中。在那个池中,悲观地估计碰撞的概率([1])将约为(n²*2{-128}),其中*n*是生成的流的数量。如果一个程序使用了激进的百万流,约为(2),那么至少有一对它们相同的概率约为(2^{-88}),这在可忽略的范围内([2])。
整数种子序列
如前一节所讨论的,SeedSequence不仅可以接受整数种子,还可以接受任意长度的(非负)整数序列。如果稍加注意,可以利用这个特性设计类似生成的安全并行 PRNG 流的临时方案,具有类似生成的安全保证。
例如,一个常见的用例是,一个工作进程被传递一个整数根种子用于整个计算,还有一个整数工作人员 ID(或者更精细的像作业 ID、批次 ID 或类似的东西)。如果这些 ID 是确定性地且唯一地创建的,那么可以通过将 ID 和根种子整数组合成列表来派生可重现的并行 PRNG 流。
# default_rng() and each of the BitGenerators use SeedSequence underneath, so
# they all accept sequences of integers as seeds the same way.
from numpy.random import default_rng
def worker(root_seed, worker_id):
rng = default_rng([worker_id, root_seed])
# Do work ...
root_seed = 0x8c3c010cb4754c905776bdac5ee7501
results = [worker(root_seed, worker_id) for worker_id in range(10)]
这可以用来替代过去使用的一些不安全策略,这些策略试图将根种子和 ID 合并为单个整数种子值。例如,通常会看到用户将工作人员 ID 添加到根种子中,特别是在传统的RandomState代码中。
# UNSAFE! Do not do this!
worker_seed = root_seed + worker_id
rng = np.random.RandomState(worker_seed)
对于以这种方式构建的并行程序的任何一次运行,每个工作人员将具有不同的流。然而,很可能在使用不同种子多次调用程序时,会得到重叠的工作人员种子集。改变根种子仅仅增加一两个时并不罕见(作者的自身经验)。如果工作人员种子也是通过工作人员 ID 的小增量派生的,那么工作人员的子集将返回相同的结果,导致整体结果集中的偏差。
将工作人员 ID 和根种子组合为整数列表可以消除这种风险。懒惰的播种实践仍然是相当安全的。
此方案要求额外的 ID 必须是唯一的,并且是确定性创建的。这可能需要协调工作进程之间的关系。建议将变化的 ID放在不变的根种子之前。生成 追加用户提供的种子之后的整数,所以如果可能同时使用这临时机制和生成,或者将对象传递给可能在生成中生成的库代码,那么更安全的做法是在你的工作进程 ID 之前添加而不是追加,以避免冲突。
# Good.
worker_seed = [worker_id, root_seed]
# Less good. It will *work*, but it's less flexible.
worker_seed = [root_seed, worker_id]
在考虑这些注意事项的情况下,确保避免冲突的安全保证与前面讨论的生成相同。算法机制也是相同的。
独立流
Philox是基于计数器的随机数生成器,通过使用弱密码原语对递增计数器进行加密来生成值。种子确定了用于加密的密钥。唯一的密钥创建唯一的独立流。Philox允许您绕过种子算法直接设置 128 位密钥。相似但不同的密钥仍将创建独立的流。
import secrets
from numpy.random import Philox
# 128-bit number as a seed
root_seed = secrets.getrandbits(128)
streams = [Philox(key=root_seed + stream_id) for stream_id in range(10)]
此方案确实要求避免重用流 ID。这可能需要在并行进程之间协调。
跳转 BitGenerator 状态
jumped会推进 BitGenerator 的状态,好像已经抽取了大量的随机数,并返回一个具有此状态的新实例。具体的抽取次数因 BitGenerator 而异,范围从(2{64})到(2)不等。此外,好像抽取还取决于特定 BitGenerator 产生的默认无符号随机数的大小。支持jumped的 BitGenerators 以及 BitGenerator 的周期、跳跃的大小和默认无符号随机数的比特数如下所示。
| BitGenerator | 周期 | 跳跃大小 | 每次抽取的比特数 |
|---|---|---|---|
| MT19937 | (2^{19937}-1) | (2^{128}) | 32 |
| PCG64 | (2^{128}) | (~2^{127}) ([3]) | 64 |
| PCG64DXSM | (2^{128}) | (~2^{127}) ([3]) | 64 |
| Philox | (2^{256}) | (2^{128}) | 64 |
可以使用jumped生成不会重叠的长代码块。
import secrets
from numpy.random import PCG64
seed = secrets.getrandbits(128)
blocked_rng = []
rng = PCG64(seed)
for i in range(10):
blocked_rng.append(rng.jumped(i))
使用jumped时,确实需要注意不要跳转到已经使用过的流。在上面的例子中,之后不能使用blocked_rng[0].jumped(),因为它会与blocked_rng[1]重叠。与独立流类似,如果此处的主进程想通过跳转拆分出 10 个以上的流,则需要从range(10, 20)开始,否则会重新创建相同的流。另一方面,如果仔细构造流,那么就确保了不会重叠的流。
多线程生成
四个核心分布(random,standard_normal,standard_exponential和standard_gamma)都允许使用out这个关键字参数来填充现有的数组。现有的数组需要是连续的和良好的(可写入和对齐)。在正常情况下,使用常见构造函数创建的数组,比如numpy.empty,都会满足这些要求。
这个示例使用了 Python 3 中的concurrent.futures来使用多个线程填充一个数组。线程是长寿命的,所以重复调用不需要额外的线程创建开销。
生成的随机数是可复现的,也就是说相同的种子会产生相同的输出,前提是线程的数量不变。
from numpy.random import default_rng, SeedSequence
import multiprocessing
import concurrent.futures
import numpy as np
class MultithreadedRNG:
def __init__(self, n, seed=None, threads=None):
if threads is None:
threads = multiprocessing.cpu_count()
self.threads = threads
seq = SeedSequence(seed)
self._random_generators = [default_rng(s)
for s in seq.spawn(threads)]
self.n = n
self.executor = concurrent.futures.ThreadPoolExecutor(threads)
self.values = np.empty(n)
self.step = np.ceil(n / threads).astype(np.int_)
def fill(self):
def _fill(random_state, out, first, last):
random_state.standard_normal(out=out[first:last])
futures = {}
for i in range(self.threads):
args = (_fill,
self._random_generators[i],
self.values,
i * self.step,
(i + 1) * self.step)
futures[self.executor.submit(*args)] = i
concurrent.futures.wait(futures)
def __del__(self):
self.executor.shutdown(False)
多线程随机数发生器可以用来填充一个数组。values属性显示了填充前的零值和填充后的随机值。
In [2]: mrng = MultithreadedRNG(10000000, seed=12345)
...: print(mrng.values[-1])
Out[2]: 0.0
In [3]: mrng.fill()
...: print(mrng.values[-1])
Out[3]: 2.4545724517479104
用多个线程生成所需的时间可以与使用单个线程生成所需的时间进行比较。
In [4]: print(mrng.threads)
...: %timeit mrng.fill()
Out[4]: 4
...: 32.8 ms ± 2.71 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
单线程调用直接使用了 BitGenerator。
In [5]: values = np.empty(10000000)
...: rg = default_rng()
...: %timeit rg.standard_normal(out=values)
Out[5]: 99.6 ms ± 222 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
收益可观,即使对于只是适度大的数组,也会有合理的增益。与不使用现有数组进行调用相比,当存在数组创建开销时,收益甚至更大。
In [6]: rg = default_rng()
...: %timeit rg.standard_normal(10000000)
Out[6]: 125 ms ± 309 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
注意,如果threads未被用户设置,它将由multiprocessing.cpu_count()确定。
In [7]: # simulate the behavior for `threads=None`, if the machine had only one thread
...: mrng = MultithreadedRNG(10000000, seed=12345, threads=1)
...: print(mrng.values[-1])
Out[7]: 1.1800150052158556
新功能或不同之处
原文:
numpy.org/doc/1.26/reference/random/new-or-different.html
NumPy 1.17.0 引入了Generator作为传统 RandomState的改进替代品。以下快速比较这两种实现。
| 特征 | 旧等价物 | 注释 |
|---|---|---|
Generator |
RandomState |
Generator需要一个称为BitGenerator 的流源。这里提供了许多 BitGenerator。默认情况下,RandomState使用 Mersenne Twister MT19937,但也可以用任何 BitGenerator 来实例化。 |
random |
random_sample, rand |
存取 BitGenerator 中的值,在区间0.0, 1.0)中转换为float64。除了size关键字参数,现在还支持dtype='d'或dtype='f',以及out关键字参数来填充用户提供的数组。还支持许多其他分布。 |
integers |
randint, random_integers |
使用endpoint关键字参数来调整high区间端点的包含或排除。 |
- 正态、指数和伽马生成器使用了 256 步的 Ziggurat 方法,比 NumPy 默认实现的[
standard_normal、standard_exponential或standard_gamma快 2-10 倍。由于算法的改变,使用Generator无法复现这些分布的确切随机值或依赖于它们的任何分布方法。
In [1]: import numpy.random
In [2]: rng = np.random.default_rng()
In [3]: %timeit -n 1 rng.standard_normal(100000)
...: %timeit -n 1 numpy.random.standard_normal(100000)
...:
1.22 ms +- 17.9 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
2.19 ms +- 12.5 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
In [4]: %timeit -n 1 rng.standard_exponential(100000)
...: %timeit -n 1 numpy.random.standard_exponential(100000)
...:
670 us +- 16.2 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
1.62 ms +- 17.8 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
In [5]: %timeit -n 1 rng.standard_gamma(3.0, 100000)
...: %timeit -n 1 numpy.random.standard_gamma(3.0, 100000)
...:
2.46 ms +- 13 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
4.42 ms +- 7.76 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
-
integers现在是从离散均匀分布生成整数随机数的正式方法。这取代了randint和被弃用的random_integers。 -
rand和randn方法只能通过传统的RandomState来使用。 -
Generator.random现在是生成浮点随机数的规范方式,取代了RandomState.random_sample、sample和ranf,所有这些都是别名。 这与 Python 的random.random一致。 -
所有比特生成器可以通过 CTypes(
ctypes)和 CFFI(cffi)生成双精度、uint64 和 uint32。 这使得这些比特生成器可以在 numba 中使用。 -
比特生成器可以通过 Cython 在下游项目中使用。
-
所有比特生成器都使用
SeedSequence来将种子整数转换为初始化状态。 -
可选的
dtype参数接受np.float32或np.float64,以生成选择分布的单精度或双精度均匀随机变量。integers接受带有任何有符号或无符号整数 dtype 的dtype参数。-
均匀分布(
random和integers) -
标准正态分布(
standard_normal) -
标准γ分布(
standard_gamma) -
标准指数分布(
standard_exponential)
-
In [6]: rng = np.random.default_rng()
In [7]: rng.random(3, dtype=np.float64)
Out[7]: array([0.32742445, 0.00929327, 0.97225134])
In [8]: rng.random(3, dtype=np.float32)
Out[8]: array([0.67851496, 0.9865629 , 0.23022616], dtype=float32)
In [9]: rng.integers(0, 256, size=3, dtype=np.uint8)
Out[9]: array([164, 54, 133], dtype=uint8)
-
可选的
out参数允许填充现有数组以选择分布。-
均匀分布(
random) -
标准正态分布(
standard_normal) -
标准 Gamma 分布(
standard_gamma) -
标准指数分布(
standard_exponential)
这允许使用适当的 BitGenerators 并行地通过多线程以块的方式填充大型数组。
-
In [10]: rng = np.random.default_rng()
In [11]: existing = np.zeros(4)
In [12]: rng.random(out=existing[:2])
Out[12]: array([0.83108158, 0.52678072])
In [13]: print(existing)
[0.83108158 0.52678072 0\. 0\. ]
- 方法可选的
axis参数,例如choice、permutation和shuffle,用于控制多维数组上的操作执行的轴。
In [14]: rng = np.random.default_rng()
In [15]: a = np.arange(12).reshape((3, 4))
In [16]: a
Out[16]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [17]: rng.choice(a, axis=1, size=5)
Out[17]:
array([[ 1, 1, 0, 3, 3],
[ 5, 5, 4, 7, 7],
[ 9, 9, 8, 11, 11]])
In [18]: rng.shuffle(a, axis=1) # Shuffle in-place
In [19]: a
Out[19]:
array([[ 2, 0, 3, 1],
[ 6, 4, 7, 5],
[10, 8, 11, 9]])
- 增加了从复数正态分布(complex_normal)中抽样的方法。
性能
推荐
通用用途推荐的生成器是PCG64或其升级版PCG64DXSM,适用于高度并行的用例。它们在大多数平台上具有统计学上的高质量、完整功能并且运行速度快,但在 32 位进程编译时略慢。有关何时使用PCG64DXSM进行重型并行的详细信息,请参阅使用 PCG64DXSM 升级 PCG64。
Philox相对较慢,但其统计属性非常高质量,通过使用唯一密钥可以轻松获得确定性独立的流。如果这是您希望用于并行流的样式,或者您正在从使用该样式的其他系统进行移植,那么Philox是您的选择。
SFC64具有统计上的高质量和非常快的运行速度。然而,它缺乏可跳跃性。如果您不使用该功能并且希望在 32 位进程上也能获得很高的速度,这是您的选择。
MT19937 未通过某些统计测试,与现代伪随机数生成器相比速度不是特别快。基于这些原因,我们大多不建议单独使用它,只通过遗留的RandomState来重现旧的结果。尽管如此,作为许多系统中的默认值,它有着非常悠久的历史。
时间
下面的时间是生成特定分布的 1 个随机值所需的时间(单位:纳秒)。由于需要 2 个 32 位值才能等于更快的生成器的输出,原始的MT19937生成器要慢得多。
整数性能具有类似的排序。
对于其他更复杂的发生器,其模式也类似。传统 RandomState 生成器的正常性能要低得多,因为它使用的是 Box-Muller 变换而不是 Ziggurat 方法。对于指数分布的性能差距也很大,这是由于计算对数函数以反演 CDF 的成本很高。标有 MT19973 的列使用与 RandomState 相同的 32 位生成器,但是利用 Generator 生成随机变量。
| MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 | RandomState | |
|---|---|---|---|---|---|---|
| 32 位无符号整数 | 3.3 | 1.9 | 2.0 | 3.3 | 1.8 | 3.1 |
| 64 位无符号整数 | 5.6 | 3.2 | 2.9 | 4.9 | 2.5 | 5.5 |
| 均匀分布 | 5.9 | 3.1 | 2.9 | 5.0 | 2.6 | 6.0 |
| 正态分布 | 13.9 | 10.8 | 10.5 | 12.0 | 8.3 | 56.8 |
| 指数 | 9.1 | 6.0 | 5.8 | 8.1 | 5.4 | 63.9 |
| 伽玛分布 | 37.2 | 30.8 | 28.9 | 34.0 | 27.5 | 77.0 |
| 二项分布 | 21.3 | 17.4 | 17.6 | 19.3 | 15.6 | 21.4 |
| 拉普拉斯分布 | 73.2 | 72.3 | 76.1 | 73.0 | 72.3 | 82.5 |
| 泊松分布 | 111.7 | 103.4 | 100.5 | 109.4 | 90.7 | 115.2 |
下一个表格显示了性能相对于传统生成器 RandomState(MT19937()) 生成值的百分比。整体性能是使用几何均值计算的。
| MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 | |
|---|---|---|---|---|---|
| 32 位无符号整数 | 96 | 162 | 160 | 96 | 175 |
| 64 位无符号整数 | 97 | 171 | 188 | 113 | 218 |
| 均匀分布 | 102 | 192 | 206 | 121 | 233 |
| 正态分布 | 409 | 526 | 541 | 471 | 684 |
| 指数 | 701 | 1071 | 1101 | 784 | 1179 |
| 伽玛分布 | 207 | 250 | 266 | 227 | 281 |
| 二项式分布 | 100 | 123 | 122 | 111 | 138 |
| 拉普拉斯分布 | 113 | 114 | 108 | 113 | 114 |
| 泊松分布 | 103 | 111 | 115 | 105 | 127 |
| 总体 | 159 | 219 | 225 | 174 | 251 |
注
所有时间均使用 AMD Ryzen 9 3900X 处理器在 Linux 上进行。
不同操作系统上的性能
不同平台的性能存在差异,这是因为编译器和硬件可用性(如寄存器宽度)的差异。默认比特生成器已被选为在 64 位平台上表现良好。32 位操作系统的性能则截然不同。
报告的值是相对于每个表中 MT19937 速度的归一化值。值为 100 表示性能与 MT19937 相匹配。较高的值表示性能提高。这些值不能跨表进行比较。
64 位 Linux
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位无符号整数 | 100 | 168 | 166 | 100 | 182 |
| 64 位无符号整数 | 100 | 176 | 193 | 116 | 224 |
| 均匀分布 | 100 | 188 | 202 | 118 | 228 |
| 正态分布 | 100 | 128 | 132 | 115 | 167 |
| 指数 | 100 | 152 | 157 | 111 | 168 |
| 总体 | 100 | 161 | 168 | 112 | 192 |
64 位 Windows
在 64 位 Linux 和 64 位 Windows 上的相对性能大致相似,但与 Philox 生成器明显不同。
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位 无符号整数 | 100 | 155 | 131 | 29 | 150 |
| 64 位 无符号整数 | 100 | 157 | 143 | 25 | 154 |
| 均匀分布 | 100 | 151 | 144 | 24 | 155 |
| 正态分布 | 100 | 129 | 128 | 37 | 150 |
| 指数分布 | 100 | 150 | 145 | 28 | 159 |
| 总体 | 100 | 148 | 138 | 28 | 154 |
32 位 Windows
64 位生成器在 32 位 Windows 上的性能远远低于 64 位操作系统,这是由于寄存器宽度。自 2005 年以来一直在 NumPy 中的生成器 MT19937 使用 32 位整数进行操作。
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位 无符号整数 | 100 | 24 | 34 | 14 | 57 |
| 64 位 无符号整数 | 100 | 21 | 32 | 14 | 74 |
| 均匀分布 | 100 | 21 | 34 | 16 | 73 |
| 正态分布 | 100 | 36 | 57 | 28 | 101 |
| 指数分布 | 100 | 28 | 44 | 20 | 88 |
| 总体 | 100 | 25 | 39 | 18 | 77 |
注意
Linux 时间使用 Ubuntu 20.04 和 GCC 9.3.0。Windows 时间使用 Windows 10 并使用 Microsoft C/C++ 优化编译器版本 19 (Visual Studio 2019)。所有时间均在 AMD Ryzen 9 3900X 处理器上生成。
推荐
用于一般用途的推荐生成器是 PCG64 或其升级变体 PCG64DXSM,用于密集并行使用情况。它们在统计上具有高质量、功能齐全,在大多数平台上运行速度快,但在编译为 32 位进程时略慢。有关何时指示使用 PCG64DXSM 的详细信息,请参见 将 PCG64 升级为 PCG64DXSM。
Philox 是相当慢的,但其统计特性非常高,使用唯一键可以轻松获得保证独立的流。如果这是您希望用于并行流的风格,或者您正在从使用该风格的其他系统进行移植,则 Philox 是您的选择。
SFC64 在统计上具有高质量并且非常快速。然而,它缺乏可跳跃性。如果您不使用该功能,并且想要大量速度,甚至在 32 位进程上,这是您的选择。
MT19937 在一些统计测试中失败,与现代 PRNG 相比也不特别快。因此,我们大多数情况下不推荐单独使用它,而是通过遗留的 RandomState 来复现旧的结果。尽管如此,在许多系统中,它作为默认选项存在已有很长时间的历史。
运行时间
以下计时是以纳秒为单位生成特定分布的 1 个随机值所需的时间。原始的 MT19937 生成器较慢,因为它需要 2 个 32 位值才能等于更快生成器的输出。
整数性能具有类似的顺序。
对于其他更复杂的生成器,模式是相似的。由于它使用的是 Box-Muller 方法而不是 Ziggurat 方法,旧的 RandomState 生成器的正常表现远低于其他生成器。指数分布的性能差距也很大,这是由于计算对数函数以求逆 CDF 的成本较高。标记为 MT19973 的列使用与 RandomState 相同的 32 位生成器,但使用 Generator 生成随机变量。
| MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 | RandomState | |
|---|---|---|---|---|---|---|
| 32 位无符号整数 | 3.3 | 1.9 | 2.0 | 3.3 | 1.8 | 3.1 |
| 64 位无符号整数 | 5.6 | 3.2 | 2.9 | 4.9 | 2.5 | 5.5 |
| 均匀分布 | 5.9 | 3.1 | 2.9 | 5.0 | 2.6 | 6.0 |
| 正态分布 | 13.9 | 10.8 | 10.5 | 12.0 | 8.3 | 56.8 |
| 指数分布 | 9.1 | 6.0 | 5.8 | 8.1 | 5.4 | 63.9 |
| Gammas | 37.2 | 30.8 | 28.9 | 34.0 | 27.5 | 77.0 |
| 二项分布 | 21.3 | 17.4 | 17.6 | 19.3 | 15.6 | 21.4 |
| Laplaces | 73.2 | 72.3 | 76.1 | 73.0 | 72.3 | 82.5 |
| 泊松分布 | 111.7 | 103.4 | 100.5 | 109.4 | 90.7 | 115.2 |
下表以相对于使用旧生成器 RandomState(MT19937()) 生成的值的百分比的形式展示了性能。整体性能是使用几何平均计算得出的。
| MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 | |
|---|---|---|---|---|---|
| 32 位无符号整数 | 96 | 162 | 160 | 96 | 175 |
| 64 位无符号整数 | 97 | 171 | 188 | 113 | 218 |
| 均匀分布 | 102 | 192 | 206 | 121 | 233 |
| 正态分布 | 409 | 526 | 541 | 471 | 684 |
| 指数分布 | 701 | 1071 | 1101 | 784 | 1179 |
| Gammas | 207 | 250 | 266 | 227 | 281 |
| 二项分布 | 100 | 123 | 122 | 111 | 138 |
| Laplaces | 113 | 114 | 108 | 113 | 114 |
| 泊松分布 | 103 | 111 | 115 | 105 | 127 |
| 总体性能 | 159 | 219 | 225 | 174 | 251 |
注意
所有的计时都是在 AMD Ryzen 9 3900X 处理器上使用 Linux 进行的。
不同操作系统上的性能
由于编译器和硬件可用性(例如,寄存器宽度)的不同,表现在不同平台上有所不同。默认比特生成器被选择为在 64 位平台上表现良好。在 32 位操作系统上性能差别非常大。
报告的数值是相对于每个表中 MT19937 速度的正常化值。数值为 100 表示性能与 MT19937 匹配。数值越高表示性能越好。这些数值不能在各表之间进行比较。
64 位 Linux
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位无符号整数 | 100 | 168 | 166 | 100 | 182 |
| 64 位无符号整数 | 100 | 176 | 193 | 116 | 224 |
| 均匀性 | 100 | 188 | 202 | 118 | 228 |
| 标准 | 100 | 128 | 132 | 115 | 167 |
| 指数 | 100 | 152 | 157 | 111 | 168 |
| 总体 | 100 | 161 | 168 | 112 | 192 |
64 位 Windows
相对于 64 位 Linux 和 64 位 Windows 的性能大致相似,但 Philox 生成器是个显著的例外。
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位无符号整数 | 100 | 155 | 131 | 29 | 150 |
| 64 位无符号整数 | 100 | 157 | 143 | 25 | 154 |
| 均匀性 | 100 | 151 | 144 | 24 | 155 |
| 标准 | 100 | 129 | 128 | 37 | 150 |
| 指数 | 100 | 150 | 145 | 28 | 159 |
| 总体 | 100 | 148 | 138 | 28 | 154 |
32 位 Windows
64 位生成器在 32 位 Windows 上的性能远低于 64 位操作系统,这是由于寄存器宽度引起的。自 2005 年以来一直存在于 NumPy 中的 MT19937 生成器使用 32 位整数。
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位无符号整数 | 100 | 24 | 34 | 14 | 57 |
| 64 位无符号整数 | 100 | 21 | 32 | 14 | 74 |
| 均匀性 | 100 | 21 | 34 | 16 | 73 |
| 标准 | 100 | 36 | 57 | 28 | 101 |
| 指数 | 100 | 28 | 44 | 20 | 88 |
| 总体 | 100 | 25 | 39 | 18 | 77 |
注意
Linux 时间使用 Ubuntu 20.04 和 GCC 9.3.0。Windows 时间在 Windows 10 上使用 Microsoft C/C++优化编译器版本 19(Visual Studio 2019)进行。所有时间均在 AMD Ryzen 9 3900X 处理器上产生。
64 位 Linux
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位无符号整数 | 100 | 168 | 166 | 100 | 182 |
| 64 位无符号整数 | 100 | 176 | 193 | 116 | 224 |
| 均匀性 | 100 | 188 | 202 | 118 | 228 |
| 标准 | 100 | 128 | 132 | 115 | 167 |
| 指数 | 100 | 152 | 157 | 111 | 168 |
| 总体 | 100 | 161 | 168 | 112 | 192 |
64 位 Windows
相对于 64 位 Linux 和 64 位 Windows 的性能大致相似,但 Philox 生成器是个显著的例外。
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位无符号整数 | 100 | 155 | 131 | 29 | 150 |
| 64 位无符号整数 | 100 | 157 | 143 | 25 | 154 |
| 统一 | 100 | 151 | 144 | 24 | 155 |
| 正态分布 | 100 | 129 | 128 | 37 | 150 |
| 指数 | 100 | 150 | 145 | 28 | 159 |
| 总体 | 100 | 148 | 138 | 28 | 154 |
32 位 Windows
64 位生成器在 32 位 Windows 上的性能远低于 64 位操作系统,这是由于寄存器宽度造成的。自 2005 年以来在 NumPy 中存在的生成器 MT19937 是基于 32 位整数运行的。
| 分布 | MT19937 | PCG64 | PCG64DXSM | Philox | SFC64 |
|---|---|---|---|---|---|
| 32 位无符号整数 | 100 | 24 | 34 | 14 | 57 |
| 64 位无符号整数 | 100 | 21 | 32 | 14 | 74 |
| 统一 | 100 | 21 | 34 | 16 | 73 |
| 正态分布 | 100 | 36 | 57 | 28 | 101 |
| 指数 | 100 | 28 | 44 | 20 | 88 |
| 总体 | 100 | 25 | 39 | 18 | 77 |
注意
Linux 的计时使用了 Ubuntu 20.04 和 GCC 9.3.0。Windows 的计时是在 Windows 10 上使用 Microsoft C/C++优化编译器版本 19(Visual Studio 2019)进行的。所有计时都是在 AMD Ryzen 9 3900X 处理器上完成的。
随机数的 C API
在版本 1.19.0 中新增。
通过 Cython 或 C 封装库(如 CFFI)可以访问下面的各种分布。所有函数接受一个 bitgen_t 作为其第一个参数。要从 Cython 或 C 访问这些函数,必须链接 npyrandom 静态库,这是 NumPy 发行版的一部分,位于 numpy/random/lib 中。请注意,您还必须链接 npymath,请参见 在扩展中链接核心数学库。
type bitgen_t
bitgen_t 包含 BitGenerator 的当前状态和返回标准 C 类型的函数指针,同时推进状态。
struct bitgen:
void *state
npy_uint64 (*next_uint64)(void *st) nogil
uint32_t (*next_uint32)(void *st) nogil
double (*next_double)(void *st) nogil
npy_uint64 (*next_raw)(void *st) nogil
ctypedef bitgen bitgen_t
请参见 扩展 以了解如何使用这些函数的示例。
函数命名遵循以下约定:
-
“standard” 是指任何参数的参考值。例如,“standard_uniform” 表示区间
0.0到1.0上的均匀分布。 -
“fill” 函数将用
cnt个值填充提供的out。 -
没有在名称中带有“standard”的函数需要附加参数来描述分布。
-
名称中带有
inv的函数基于较慢的反转方法,而不是 ziggurat 查找算法,后者速度明显更快。非 ziggurat 变体用于边缘情况和向后兼容性。
double random_standard_uniform( *bitgen_state)
void random_standard_uniform_fill( *bitgen_state, cnt, double *out)
double random_standard_exponential( *bitgen_state)
void random_standard_exponential_fill( *bitgen_state, cnt, double *out)
void random_standard_exponential_inv_fill( *bitgen_state, cnt, double *out)
double random_standard_normal( *bitgen_state)
void random_standard_normal_fill( *bitgen_state, count, double *out)
void random_standard_normal_fill_f( *bitgen_state, count, float *out)
double random_standard_gamma( *bitgen_state, double shape)
float random_standard_uniform_f( *bitgen_state)
void random_standard_uniform_fill_f( *bitgen_state, cnt, float *out)
float random_standard_exponential_f( *bitgen_state)
void random_standard_exponential_fill_f( *bitgen_state, cnt, float *out)
void random_standard_exponential_inv_fill_f( *bitgen_state, cnt, float *out)
float random_standard_normal_f( *bitgen_state)
float random_standard_gamma_f( *bitgen_state, float shape)
double random_normal( *bitgen_state, double loc, double scale)
double random_gamma( *bitgen_state, double shape, double scale)
float random_gamma_f( *bitgen_state, float shape, float scale)
double random_exponential( *bitgen_state, double scale)
double random_uniform( *bitgen_state, double lower, double range)
double random_beta( *bitgen_state, double a, double b)
double random_chisquare( *bitgen_state, double df)
double random_f( *bitgen_state, double dfnum, double dfden)
double random_standard_cauchy( *bitgen_state)
double random_pareto( *bitgen_state, double a)
double random_weibull( *bitgen_state, double a)
double random_power( *bitgen_state, double a)
double random_laplace( *bitgen_state, double loc, double scale)
double random_gumbel( *bitgen_state, double loc, double scale)
double random_logistic( *bitgen_state, double loc, double scale)
double random_lognormal( *bitgen_state, double mean, double sigma)
double random_rayleigh( *bitgen_state, double mode)
double random_standard_t( *bitgen_state, double df)
double random_noncentral_chisquare( *bitgen_state, double df, double nonc)
double random_noncentral_f( *bitgen_state, double dfnum, double dfden, double nonc)
double random_wald( *bitgen_state, double mean, double scale)
double random_vonmises( *bitgen_state, double mu, double kappa)
double random_triangular( *bitgen_state, double left, double mode, double right)
random_poisson( *bitgen_state, double lam)
random_negative_binomial( *bitgen_state, double n, double p)
type binomial_t
typedef struct s_binomial_t {
int has_binomial; /* !=0: following parameters initialized for binomial */
double psave;
RAND_INT_TYPE nsave;
double r;
double q;
double fm;
RAND_INT_TYPE m;
double p1;
double xm;
double xl;
double xr;
double c;
double laml;
double lamr;
double p2;
double p3;
double p4;
} binomial_t;
random_binomial( *bitgen_state, double p, n, *binomial)
random_logseries( *bitgen_state, double p)
random_geometric_search( *bitgen_state, double p)
random_geometric_inversion( *bitgen_state, double p)
random_geometric( *bitgen_state, double p)
random_zipf( *bitgen_state, double a)
random_hypergeometric( *bitgen_state, good, bad, sample)
random_interval( *bitgen_state, max)
void random_multinomial( *bitgen_state, n, *mnix, double *pix, d, *binomial)
int random_multivariate_hypergeometric_count( *bitgen_state, total, size_t num_colors, *colors, nsample, size_t num_variates, *variates)
void random_multivariate_hypergeometric_marginals( *bitgen_state, total, size_t num_colors, *colors, nsample, size_t num_variates, *variates)
生成单个整数
random_positive_int64( *bitgen_state)
random_positive_int32( *bitgen_state)
random_positive_int( *bitgen_state)
random_uint( *bitgen_state)
在闭区间 [off, off + rng] 生成随机的 uint64 数字。
random_bounded_uint64( *bitgen_state, off, rng, mask, bool use_masked)
扩展
这些 BitGenerators 已经被设计为可使用标准工具扩展高性能 Python——numba 和 Cython。Generator对象也可以与用户提供的 BitGenerators 一起使用,只要这些 BitGenerators 导出一小组所需的函数即可。
Numba
Numba 可以与 CTypes 或 CFFI 一起使用。当前的 BitGenerators 迭代都通过这两个接口导出一小组函数。
这个示例展示了 numba 如何通过纯 Python 实现来生成高斯样本,然后对其进行编译。随机数由ctypes.next_double提供。
import numpy as np
import numba as nb
from numpy.random import PCG64
from timeit import timeit
bit_gen = PCG64()
next_d = bit_gen.cffi.next_double
state_addr = bit_gen.cffi.state_address
def normals(n, state):
out = np.empty(n)
for i in range((n + 1) // 2):
x1 = 2.0 * next_d(state) - 1.0
x2 = 2.0 * next_d(state) - 1.0
r2 = x1 * x1 + x2 * x2
while r2 >= 1.0 or r2 == 0.0:
x1 = 2.0 * next_d(state) - 1.0
x2 = 2.0 * next_d(state) - 1.0
r2 = x1 * x1 + x2 * x2
f = np.sqrt(-2.0 * np.log(r2) / r2)
out[2 * i] = f * x1
if 2 * i + 1 < n:
out[2 * i + 1] = f * x2
return out
# Compile using Numba
normalsj = nb.jit(normals, nopython=True)
# Must use state address not state with numba
n = 10000
def numbacall():
return normalsj(n, state_addr)
rg = np.random.Generator(PCG64())
def numpycall():
return rg.normal(size=n)
# Check that the functions work
r1 = numbacall()
r2 = numpycall()
assert r1.shape == (n,)
assert r1.shape == r2.shape
t1 = timeit(numbacall, number=1000)
print(f'{t1:.2f} secs for {n} PCG64 (Numba/PCG64) gaussian randoms')
t2 = timeit(numpycall, number=1000)
print(f'{t2:.2f} secs for {n} PCG64 (NumPy/PCG64) gaussian randoms')
CTypes 和 CFFI 都允许在将文件 distributions.c 编译成DLL或so文件之后直接在 Numba 中使用更复杂的分布。下面的示例部分展示了使用更复杂分布的示例。
Cython
Cython 可用于解包由 BitGenerator 提供的PyCapsule。此示例使用PCG64以及上面的示例。使用 Cython 编写高性能代码的一般准则仍然适用于去除边界检查和环绕,提供数组对齐信息。
#!/usr/bin/env python3
#cython: language_level=3
"""
This file shows how the to use a BitGenerator to create a distribution.
"""
import numpy as np
cimport numpy as np
cimport cython
from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer
from libc.stdint cimport uint16_t, uint64_t
from numpy.random cimport bitgen_t
from numpy.random import PCG64
from numpy.random.c_distributions cimport (
random_standard_uniform_fill, random_standard_uniform_fill_f)
@cython.boundscheck(False)
@cython.wraparound(False)
def uniforms(Py_ssize_t n):
"""
Create an array of `n` uniformly distributed doubles.
A 'real' distribution would want to process the values into
some non-uniform distribution
"""
cdef Py_ssize_t i
cdef bitgen_t *rng
cdef const char *capsule_name = "BitGenerator"
cdef double[::1] random_values
x = PCG64()
capsule = x.capsule
# Optional check that the capsule if from a BitGenerator
if not PyCapsule_IsValid(capsule, capsule_name):
raise ValueError("Invalid pointer to anon_func_state")
# Cast the pointer
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name)
random_values = np.empty(n, dtype='float64')
with x.lock, nogil:
for i in range(n):
# Call the function
random_values[i] = rng.next_double(rng.state)
randoms = np.asarray(random_values)
return randoms
BitGenerator 也可以直接使用bitgen_t结构体的成员进行访问。
@cython.boundscheck(False)
@cython.wraparound(False)
def uint10_uniforms(Py_ssize_t n):
"""Uniform 10 bit integers stored as 16-bit unsigned integers"""
cdef Py_ssize_t i
cdef bitgen_t *rng
cdef const char *capsule_name = "BitGenerator"
cdef uint16_t[::1] random_values
cdef int bits_remaining
cdef int width = 10
cdef uint64_t buff, mask = 0x3FF
x = PCG64()
capsule = x.capsule
if not PyCapsule_IsValid(capsule, capsule_name):
raise ValueError("Invalid pointer to anon_func_state")
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name)
random_values = np.empty(n, dtype='uint16')
# Best practice is to release GIL and acquire the lock
bits_remaining = 0
with x.lock, nogil:
for i in range(n):
if bits_remaining < width:
buff = rng.next_uint64(rng.state)
random_values[i] = buff & mask
buff >>= width
randoms = np.asarray(random_values)
return randoms
Cython 可用于直接访问numpy/random/c_distributions.pxd中的函数。这需要与位于numpy/random/lib的npyrandom库进行链接。
def uniforms_ex(bit_generator, Py_ssize_t n, dtype=np.float64):
"""
Create an array of `n` uniformly distributed doubles via a "fill" function.
A 'real' distribution would want to process the values into
some non-uniform distribution
Parameters
----------
bit_generator: BitGenerator instance
n: int
Output vector length
dtype: {str, dtype}, optional
Desired dtype, either 'd' (or 'float64') or 'f' (or 'float32'). The
default dtype value is 'd'
"""
cdef Py_ssize_t i
cdef bitgen_t *rng
cdef const char *capsule_name = "BitGenerator"
cdef np.ndarray randoms
capsule = bit_generator.capsule
# Optional check that the capsule if from a BitGenerator
if not PyCapsule_IsValid(capsule, capsule_name):
raise ValueError("Invalid pointer to anon_func_state")
# Cast the pointer
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name)
_dtype = np.dtype(dtype)
randoms = np.empty(n, dtype=_dtype)
if _dtype == np.float32:
with bit_generator.lock:
random_standard_uniform_fill_f(rng, n, <float*>np.PyArray_DATA(randoms))
elif _dtype == np.float64:
with bit_generator.lock:
random_standard_uniform_fill(rng, n, <double*>np.PyArray_DATA(randoms))
else:
raise TypeError('Unsupported dtype %r for random' % _dtype)
return randoms
查看通过 Cython 扩展 numpy.random 来获取这些示例的完整清单和一个最小的setup.py以构建 c 扩展模块。
CFFI
CFFI 可用于直接访问include/numpy/random/distributions.h中的函数。需要对头文件进行一些“调整”:
"""
Use cffi to access any of the underlying C functions from distributions.h
"""
import os
import numpy as np
import cffi
from .parse import parse_distributions_h
ffi = cffi.FFI()
inc_dir = os.path.join(np.get_include(), 'numpy')
# Basic numpy types
ffi.cdef('''
typedef intptr_t npy_intp;
typedef unsigned char npy_bool;
''')
parse_distributions_h(ffi, inc_dir)
一旦头部被ffi.cdef解析,函数可以直接从_generator共享对象中访问,使用BitGenerator.cffi界面。
# Compare the distributions.h random_standard_normal_fill to
# Generator.standard_random
bit_gen = np.random.PCG64()
rng = np.random.Generator(bit_gen)
state = bit_gen.state
interface = rng.bit_generator.cffi
n = 100
vals_cffi = ffi.new('double[%d]' % n)
lib.random_standard_normal_fill(interface.bit_generator, n, vals_cffi)
# reset the state
bit_gen.state = state
vals = rng.standard_normal(n)
for i in range(n):
assert vals[i] == vals_cffi[i]
新的 Bit Generators
Generator可以与用户提供的BitGenerator一起使用。编写新的 BitGenerator 的最简单方法是检查现有 BitGenerators 中的 pyx 文件。必须提供的关键结构是包含指向类型为bitgen_t的结构指针的capsule。
typedef struct bitgen {
void *state;
uint64_t (*next_uint64)(void *st);
uint32_t (*next_uint32)(void *st);
double (*next_double)(void *st);
uint64_t (*next_raw)(void *st);
} bitgen_t;
提供了 5 个指针。第一个是一个不透明指针,用于 BitGenerators 使用的数据结构。接下来的三个是函数指针,它们分别返回下一个 64 位和 32 位无符号整数、下一个随机双精度浮点数以及下一个原始值。最后一个函数用于测试,如果不需要,可以设置为下一个 64 位无符号整数函数。Generator 内部的函数使用此结构,如下所示
bitgen_state->next_uint64(bitgen_state->state)
示例
-
Numba
-
CFFI + Numba
-
Cython
-
meson.build
-
extending.pyx
-
extending_distributions.pyx
-
-
CFFI
Numba
Numba 可以与 CTypes 或 CFFI 一起使用。当前迭代的 BitGenerators 都通过这两种接口导出了一小组函数。
此示例显示了如何使用 numba 生成高斯样本,其中纯 Python 实现然后进行编译。随机数由 ctypes.next_double 提供。
import numpy as np
import numba as nb
from numpy.random import PCG64
from timeit import timeit
bit_gen = PCG64()
next_d = bit_gen.cffi.next_double
state_addr = bit_gen.cffi.state_address
def normals(n, state):
out = np.empty(n)
for i in range((n + 1) // 2):
x1 = 2.0 * next_d(state) - 1.0
x2 = 2.0 * next_d(state) - 1.0
r2 = x1 * x1 + x2 * x2
while r2 >= 1.0 or r2 == 0.0:
x1 = 2.0 * next_d(state) - 1.0
x2 = 2.0 * next_d(state) - 1.0
r2 = x1 * x1 + x2 * x2
f = np.sqrt(-2.0 * np.log(r2) / r2)
out[2 * i] = f * x1
if 2 * i + 1 < n:
out[2 * i + 1] = f * x2
return out
# Compile using Numba
normalsj = nb.jit(normals, nopython=True)
# Must use state address not state with numba
n = 10000
def numbacall():
return normalsj(n, state_addr)
rg = np.random.Generator(PCG64())
def numpycall():
return rg.normal(size=n)
# Check that the functions work
r1 = numbacall()
r2 = numpycall()
assert r1.shape == (n,)
assert r1.shape == r2.shape
t1 = timeit(numbacall, number=1000)
print(f'{t1:.2f} secs for {n} PCG64 (Numba/PCG64) gaussian randoms')
t2 = timeit(numpycall, number=1000)
print(f'{t2:.2f} secs for {n} PCG64 (NumPy/PCG64) gaussian randoms')
使用 CTypes 和 CFFI 都允许在将文件 distributions.c 编译成 DLL 或 so 后,直接在 numba 中使用更复杂的分布。一个示例,展示了更复杂分布的使用,位于下面的 示例 部分。
Cython
Cython 可用于解开位生成器提供的 PyCapsule。此示例使用 PCG64 和上面的示例。使用 Cython 编写高性能代码的通常注意事项—去除边界检查和环绕,并提供数组对齐信息—仍然适用。
#!/usr/bin/env python3
#cython: language_level=3
"""
This file shows how the to use a BitGenerator to create a distribution.
"""
import numpy as np
cimport numpy as np
cimport cython
from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer
from libc.stdint cimport uint16_t, uint64_t
from numpy.random cimport bitgen_t
from numpy.random import PCG64
from numpy.random.c_distributions cimport (
random_standard_uniform_fill, random_standard_uniform_fill_f)
@cython.boundscheck(False)
@cython.wraparound(False)
def uniforms(Py_ssize_t n):
"""
Create an array of `n` uniformly distributed doubles.
A 'real' distribution would want to process the values into
some non-uniform distribution
"""
cdef Py_ssize_t i
cdef bitgen_t *rng
cdef const char *capsule_name = "BitGenerator"
cdef double[::1] random_values
x = PCG64()
capsule = x.capsule
# Optional check that the capsule if from a BitGenerator
if not PyCapsule_IsValid(capsule, capsule_name):
raise ValueError("Invalid pointer to anon_func_state")
# Cast the pointer
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name)
random_values = np.empty(n, dtype='float64')
with x.lock, nogil:
for i in range(n):
# Call the function
random_values[i] = rng.next_double(rng.state)
randoms = np.asarray(random_values)
return randoms
位生成器也可以直接通过 bitgen_t 结构的成员进行访问。
@cython.boundscheck(False)
@cython.wraparound(False)
def uint10_uniforms(Py_ssize_t n):
"""Uniform 10 bit integers stored as 16-bit unsigned integers"""
cdef Py_ssize_t i
cdef bitgen_t *rng
cdef const char *capsule_name = "BitGenerator"
cdef uint16_t[::1] random_values
cdef int bits_remaining
cdef int width = 10
cdef uint64_t buff, mask = 0x3FF
x = PCG64()
capsule = x.capsule
if not PyCapsule_IsValid(capsule, capsule_name):
raise ValueError("Invalid pointer to anon_func_state")
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name)
random_values = np.empty(n, dtype='uint16')
# Best practice is to release GIL and acquire the lock
bits_remaining = 0
with x.lock, nogil:
for i in range(n):
if bits_remaining < width:
buff = rng.next_uint64(rng.state)
random_values[i] = buff & mask
buff >>= width
randoms = np.asarray(random_values)
return randoms
Cython 可用于直接访问 numpy/random/c_distributions.pxd 中的函数。这需要与位于 numpy/random/lib 中的 npyrandom 库进行链接。
def uniforms_ex(bit_generator, Py_ssize_t n, dtype=np.float64):
"""
Create an array of `n` uniformly distributed doubles via a "fill" function.
A 'real' distribution would want to process the values into
some non-uniform distribution
Parameters
----------
bit_generator: BitGenerator instance
n: int
Output vector length
dtype: {str, dtype}, optional
Desired dtype, either 'd' (or 'float64') or 'f' (or 'float32'). The
default dtype value is 'd'
"""
cdef Py_ssize_t i
cdef bitgen_t *rng
cdef const char *capsule_name = "BitGenerator"
cdef np.ndarray randoms
capsule = bit_generator.capsule
# Optional check that the capsule if from a BitGenerator
if not PyCapsule_IsValid(capsule, capsule_name):
raise ValueError("Invalid pointer to anon_func_state")
# Cast the pointer
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name)
_dtype = np.dtype(dtype)
randoms = np.empty(n, dtype=_dtype)
if _dtype == np.float32:
with bit_generator.lock:
random_standard_uniform_fill_f(rng, n, <float*>np.PyArray_DATA(randoms))
elif _dtype == np.float64:
with bit_generator.lock:
random_standard_uniform_fill(rng, n, <double*>np.PyArray_DATA(randoms))
else:
raise TypeError('Unsupported dtype %r for random' % _dtype)
return randoms
参见 通过 Cython 扩展 numpy.random 获取这些示例的完整列表和一个用于构建 C 扩展模块的 minimal setup.py。
CFFI
CFFI 可用于直接访问 include/numpy/random/distributions.h 中的函数。需要对头文件进行一些“修饰”:
"""
Use cffi to access any of the underlying C functions from distributions.h
"""
import os
import numpy as np
import cffi
from .parse import parse_distributions_h
ffi = cffi.FFI()
inc_dir = os.path.join(np.get_include(), 'numpy')
# Basic numpy types
ffi.cdef('''
typedef intptr_t npy_intp;
typedef unsigned char npy_bool;
''')
parse_distributions_h(ffi, inc_dir)
一旦头文件被 ffi.cdef 解析,可以直接从 _generator 共享对象中使用 BitGenerator.cffi 接口访问其中的函数。
# Compare the distributions.h random_standard_normal_fill to
# Generator.standard_random
bit_gen = np.random.PCG64()
rng = np.random.Generator(bit_gen)
state = bit_gen.state
interface = rng.bit_generator.cffi
n = 100
vals_cffi = ffi.new('double[%d]' % n)
lib.random_standard_normal_fill(interface.bit_generator, n, vals_cffi)
# reset the state
bit_gen.state = state
vals = rng.standard_normal(n)
for i in range(n):
assert vals[i] == vals_cffi[i]
新位生成器
生成器可以与用户提供的BitGenerator一起使用。编写新的 BitGenerator 的最简单方法是检查现有 BitGenerators 之一的 pyx 文件。必须提供的关键结构是包含类型为bitgen_t的结构指针的capsule,
typedef struct bitgen {
void *state;
uint64_t (*next_uint64)(void *st);
uint32_t (*next_uint32)(void *st);
double (*next_double)(void *st);
uint64_t (*next_raw)(void *st);
} bitgen_t;
这提供了 5 个指针。第一个是对 BitGenerators 使用的数据结构的不透明指针。接下来三个是返回下一个 64 位和 32 位无符号整数,下一个随机双精度和下一个原始值的函数指针。如果不需要,这个最后一个函数用于测试,因此可以设置为下一个 64 位无符号整数函数。Generator内部的函数使用这个结构如
bitgen_state->next_uint64(bitgen_state->state)
例子
-
Numba
-
CFFI + Numba
-
Cython
-
meson.build
-
extending.pyx
-
extending_distributions.pyx
-
-
CFFI
集合操作
lib.arraysetops |
基于排序的数组的集合操作。 |
|---|
构建适当的集合
unique(ar[, return_index, return_inverse, ...]) |
查找数组的唯一元素。 |
|---|
布尔运算
in1d(ar1, ar2[, assume_unique, invert, kind]) |
测试 1-D 数组的每个元素是否也存在于第二个数组中。 |
|---|---|
intersect1d(ar1, ar2[, assume_unique, ...]) |
找到两个数组的交集。 |
isin(element, test_elements[, ...]) |
计算element in test_elements,只对element进行广播。 |
setdiff1d(ar1, ar2[, assume_unique]) |
找到两个数组的集合差异。 |
setxor1d(ar1, ar2[, assume_unique]) |
找到两个数组的集合异或。 |
union1d(ar1, ar2) |
找到两个数组的并集。 |
构建适当的集合
unique(ar[, return_index, return_inverse, ...]) |
查找数组的唯一元素。 |
|---|
布尔运算
in1d(ar1, ar2[, assume_unique, invert, kind]) |
测试 1-D 数组的每个元素是否也存在于第二个数组中。 |
|---|---|
intersect1d(ar1, ar2[, assume_unique, ...]) |
找到两个数组的交集。 |
isin(element, test_elements[, ...]) |
计算element in test_elements,只对element进行广播。 |
setdiff1d(ar1, ar2[, assume_unique]) |
找到两个数组的集合差异。 |
setxor1d(ar1, ar2[, assume_unique]) |
找到两个数组的集合异或。 |
union1d(ar1, ar2) |
找到两个数组的并集。 |
numpy.lib.arraysetops
原文:
numpy.org/doc/1.26/reference/generated/numpy.lib.arraysetops.html
基于排序的数组的集合操作。
备注
对于浮点数组,由于常见的舍入和浮点比较问题,可能出现不准确的结果。
通过对numpy.sort的实现,可以在某些操作中提高速度,该实现可以直接提供排列向量,从而避免调用numpy.argsort。
原作者:Robert Cimrman
备注
对于浮点数组,由于常见的舍入和浮点比较问题,可能出现不准确的结果。
通过对numpy.sort的实现,可以在某些操作中提高速度,该实现可以直接提供排列向量,从而避免调用numpy.argsort。
原作者:Robert Cimrman
numpy.unique
numpy.unique(ar, return_index=False, return_inverse=False, return_counts=False, axis=None, *, equal_nan=True)
找到数组的唯一元素。
返回数组的排序唯一元素。除了唯一元素之外,还有三个可选输出:
-
给出唯一值的输入数组的索引
-
重构输入数组的唯一数组的索引
-
输入数组中每个唯一值出现的次数
参数:
ararray_like
输入数组。除非指定了 axis,否则如果它不是已经是 1-D,则会被展平。
return_indexbool, optional
如果为 True,还将返回 ar 的索引(沿指定轴(如果提供)或在展平的数组中)导致唯一数组的值。
return_inversebool, optional
如果为 True,还返回唯一数组的索引(如果提供了指定轴)可以用于重构 ar。
return_countsbool, optional
如果为 True,则还返回每个唯一项在 ar 中出现的次数。
axisint or None, optional
操作的轴。如果为 None,ar 将被展平。如果为整数,给定轴索引的子数组将被展平,并视为具有给定轴维度的 1-D 数组的元素,更多细节请参见注释。如果使用 axis kwarg,则不支持对象数组或包含对象的结构化数组。默认为 None。
1.13.0 版中的新功能。
equal_nanbool, optional
如果为 True,则将返回数组中的多个 NaN 值合并为一个。
1.24 版中的新功能。
返回:
uniquendarray
排序的唯一值。
unique_indicesndarray, optional
原始数组中唯一值的第一次出现的索引。仅在 return_index 为 True 时提供。
unique_inversendarray, optional
从唯一数组重构原始数组的索引。仅在 return_inverse 为 True 时提供。
unique_countsndarray, optional
原始数组中每个唯一值出现的次数。仅在 return_counts 为 True 时提供。
1.9.0 版中的新功能。
另请参见
numpy.lib.arraysetops
具有执行数组上的集合操作的其他函数的模块。
repeat
重复数组的元素。
注释
当指定轴时,由轴索引的子数组被排序。这是通过将指定轴作为数组的第一个维度(将轴移到第一个维度以保持其他轴的顺序)并以 C 顺序展平子数组来完成的。然后,展平的子数组被视为具有每个元素都有标签的结构化类型,结果就是,我们最终得到了一个可以像任何其他 1-D 数组一样处理的结构化类型的 1-D 数组。结果是,展平的子数组按字典顺序排序,从第一个元素开始。
示例
>>> np.unique([1, 1, 2, 2, 3, 3])
array([1, 2, 3])
>>> a = np.array([[1, 1], [2, 3]])
>>> np.unique(a)
array([1, 2, 3])
返回 2D 数组的唯一行
>>> a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
>>> np.unique(a, axis=0)
array([[1, 0, 0], [2, 3, 4]])
返回原始数组中给出唯一值的索引:
>>> a = np.array(['a', 'b', 'b', 'c', 'a'])
>>> u, indices = np.unique(a, return_index=True)
>>> u
array(['a', 'b', 'c'], dtype='<U1')
>>> indices
array([0, 1, 3])
>>> a[indices]
array(['a', 'b', 'c'], dtype='<U1')
从唯一值和反向值重新构建输入数组:
>>> a = np.array([1, 2, 6, 4, 2, 3, 2])
>>> u, indices = np.unique(a, return_inverse=True)
>>> u
array([1, 2, 3, 4, 6])
>>> indices
array([0, 1, 4, 3, 1, 2, 1])
>>> u[indices]
array([1, 2, 6, 4, 2, 3, 2])
从唯一值和计数值重构输入值:
>>> a = np.array([1, 2, 6, 4, 2, 3, 2])
>>> values, counts = np.unique(a, return_counts=True)
>>> values
array([1, 2, 3, 4, 6])
>>> counts
array([1, 3, 1, 1, 1])
>>> np.repeat(values, counts)
array([1, 2, 2, 2, 3, 4, 6]) # original order not preserved
numpy.in1d
numpy.in1d(ar1, ar2, assume_unique=False, invert=False, *, kind=None)
测试 1-D 数组的每个元素是否也存在于第二个数组中。
返回一个与ar1长度相同的布尔数组,其中ar1的元素在ar2中为 True,否则为 False。
我们建议新代码使用isin而不是in1d。
参数:
ar1(M,) array_like
输入数组。
ar2array_like
用于测试每个ar1值的值。
assume_uniquebool, optional
如果为 True,则假定输入数组都是唯一的,这可以加快计算速度。默认值为 False。
invertbool, optional
如果为 True,则返回数组中的值被反转(即,ar1的元素在ar2中为 False,否则为 True)。默认值为 False。np.in1d(a, b, invert=True)等同于(但比np.invert(in1d(a, b))更快)。
kind, optional
要使用的算法。这不会影响最终结果,但会影响速度和内存使用。默认值 None,将根据内存考虑自动选择。
-
如果选择‘sort’,将使用基于归并排序的方法。这将使用大约ar1和ar2大小之和的 6 倍的内存,不考虑 dtype 的大小。
-
如果选择‘table’,将使用类似于计数排序的查找表方法。这仅适用于布尔和整数数组。这将使用ar1的大小加上ar2的最大-最小值的内存。当使用‘table’选项时,assume_unique不起作用。
-
如果为 None,则如果所需内存分配小于或等于ar1和ar2大小之和的 6 倍,将自动选择‘table’,否则将使用‘sort’。这样做是为了不默认使用大量内存,即使在大多数情况下‘table’可能更快。如果选择‘table’,assume_unique将不起作用。
在 1.8.0 版本中新增。
返回:
in1d(M,) ndarray, bool
ar1[in1d]的值在ar2中。
另请参见
isin
保留 ar1 形状的此函数版本。
numpy.lib.arraysetops
具有执行数组上集合操作的其他函数的模块。
注意
in1d可以被视为对 1-D 序列执行元素级函数版本的 python 关键字in。in1d(a, b)大致等同于np.array([item in b for item in a])。但是,如果ar2是一个集合或类似的(非序列)容器,则这个想法会失败:因为ar2被转换为数组,在这些情况下asarray(ar2)是一个对象数组,而不是预期的包含值的数组。
当以下关系成立时,使用 kind='table' 通常比 kind='sort' 更快:log10(len(ar2)) > (log10(max(ar2)-min(ar2)) - 2.27) / 0.927,但可能使用更多内存。 kind 的默认值将仅基于内存使用情况自动选择,因此如果内存约束可以放宽,可以手动设置 kind='table'。
新版本 1.4.0 中新增内容。
示例
>>> test = np.array([0, 1, 2, 5, 0])
>>> states = [0, 2]
>>> mask = np.in1d(test, states)
>>> mask
array([ True, False, True, False, True])
>>> test[mask]
array([0, 2, 0])
>>> mask = np.in1d(test, states, invert=True)
>>> mask
array([False, True, False, True, False])
>>> test[mask]
array([1, 5])
numpy.intersect1d
原文:
numpy.org/doc/1.26/reference/generated/numpy.intersect1d.html
numpy.intersect1d(ar1, ar2, assume_unique=False, return_indices=False)
找到两个数组的交集。
返回两个输入数组中都存在的排序唯一值。
参数:
ar1, ar2array_like
输入数组。如果不是 1D,则将其展平。
assume_uniquebool
如果为 True,则假定输入数组都是唯一的,这可以加快计算速度。如果为 True 但ar1或ar2不是唯一的,则可能会导致不正确的结果和超出范围的索引。默认为 False。
return_indicesbool
如果为 True,则返回与两个数组的交集对应的索引。如果有多个值,则使用第一个实例。默认为 False。
在版本 1.15.0 中新增。
返回:
intersect1dndarray
排序的包含共有和唯一元素的 1D 数组。
comm1ndarray
ar1中共有值的第一次出现的索引。仅当return_indices为 True 时提供。
comm2ndarray
ar2中共有值的第一次出现的索引。仅当return_indices为 True 时提供。
参见
numpy.lib.arraysetops
一个包含许多其他函数以在数组上执行集合操作的模块。
示例
>>> np.intersect1d([1, 3, 4, 3], [3, 1, 2, 1])
array([1, 3])
要对超过两个数组取交集,请使用 functools.reduce:
>>> from functools import reduce
>>> reduce(np.intersect1d, ([1, 3, 4, 3], [3, 1, 2, 1], [6, 3, 4, 2]))
array([3])
要返回沿着输入数组共有的值的索引以及相交的值:
>>> x = np.array([1, 1, 2, 3, 4])
>>> y = np.array([2, 1, 4, 6])
>>> xy, x_ind, y_ind = np.intersect1d(x, y, return_indices=True)
>>> x_ind, y_ind
(array([0, 2, 4]), array([1, 0, 2]))
>>> xy, x[x_ind], y[y_ind]
(array([1, 2, 4]), array([1, 2, 4]), array([1, 2, 4]))
numpy.isin
numpy.isin(element, test_elements, assume_unique=False, invert=False, *, kind=None)
计算 element 在 test_elements 中的存在,仅广播 element。返回一个与 element 相同形状的布尔数组,其中 element 的元素在 test_elements 中为 True,否则为 False。
参数:
elementarray_like
输入数组。
test_elementsarray_like
要测试每个 element 的值。如果它是一个数组或类似数组,则该参数将被展平。查看非类数组参数的行为的注意事项。
assume_uniquebool,可选
如果为 True,则假定输入数组都是唯一的,这可以加快计算速度。默认为 False。
invertbool,可选
如果为 True,则返回数组中的值将被反转,就像计算 element not in test_elements 一样。默认为 False。np.isin(a, b, invert=True) 等同于(但比)np.invert(np.isin(a, b)) 更快。
种类,可选
要使用的算法。这不会影响最终结果,但会影响速度和内存使用。默认情况下,None 将根据内存考虑自动选择。
-
如果为 ‘sort’,将使用基于归并排序的方法。这将大致使用 ar1 和 ar2 大小之和的 6 倍的内存,不考虑 dtype 的大小。
-
如果为 ‘table’,将使用类似于计数排序的查找表方法。这仅适用于布尔和整数数组。当使用 ‘table’ 选项时,assume_unique 不起作用。
-
如果为 None,则如果所需内存分配小于或等于 ar1 和 ar2 大小之和的 6 倍,则会自动选择 ‘table’,否则将使用 ‘sort’。这样做是为了不默认使用大量内存,即使在大多数情况下 ‘table’ 可能更快。如果选择了 ‘table’,assume_unique 将不起作用。
返回:
isinndarray,bool
具有与 element 相同的形状。element[isin] 中的值在 test_elements 中。
参见
in1d
此函数的展平版本。
numpy.lib.arraysetops
具有在数组中执行集合操作的其他函数的模块。
注意事项
isin 是 python 关键字 in 的逐元素函数版本。如果 a 和 b 是 1-D 序列,则 isin(a, b) 大致等同于 np.array([item in b for item in a])。
element 和 test_elements 如果尚未转换为数组,则会被转换为数组。如果 test_elements 是一个集合(或其他非序列集合),它将被转换为一个包含一个元素的对象数组,而不是包含 test_elements 中的值的数组。 这是由于 array 构造函数处理非序列集合的方式。将集合转换为列表通常会得到所期望的行为。
如果满足以下关系式:log10(len(ar2)) > (log10(max(ar2)-min(ar2)) - 2.27) / 0.927,那么使用 kind='table' 通常比 kind=’sort’ 更快,但可能会使用更多内存。 kind 的默认值将根据内存使用量自动选择,因此如果内存约束可以放宽,可以手动设置 kind='table'。
自 1.13.0 版开始新增。
示例
>>> element = 2*np.arange(4).reshape((2, 2))
>>> element
array([[0, 2],
[4, 6]])
>>> test_elements = [1, 2, 4, 8]
>>> mask = np.isin(element, test_elements)
>>> mask
array([[False, True],
[ True, False]])
>>> element[mask]
array([2, 4])
匹配值的索引可以通过 nonzero 获得:
>>> np.nonzero(mask)
(array([0, 1]), array([1, 0]))
测试也可以被反转:
>>> mask = np.isin(element, test_elements, invert=True)
>>> mask
array([[ True, False],
[False, True]])
>>> element[mask]
array([0, 6])
由于 array 处理集合的方式,以下操作无法按预期工作:
>>> test_set = {1, 2, 4, 8}
>>> np.isin(element, test_set)
array([[False, False],
[False, False]])
将集合转换为列表会得到预期的结果:
>>> np.isin(element, list(test_set))
array([[False, True],
[ True, False]])
numpy.setdiff1d
原文:
numpy.org/doc/1.26/reference/generated/numpy.setdiff1d.html
numpy.setdiff1d(ar1, ar2, assume_unique=False)
查找两个数组的差集。
返回ar1中存在而ar2中不存在的唯一值。
参数:
ar1 数组型
输入数组。
ar2 数组型
输入比较数组。
assume_unique 布尔型
如果为 True,则假定输入数组都是唯一的,这可以加速计算。默认值为 False。
返回:
setdiff1d 数组型
在ar1中的值的一维数组,这些值不在ar2中。当assume_unique=False时,结果是有序的,但如果输入是有序的,则仅在其他情况下进行排序。
另请参阅
numpy.lib.arraysetops
模块包含了许多其他用于数组上执行集合操作的函数。
示例
>>> a = np.array([1, 2, 3, 2, 4, 1])
>>> b = np.array([3, 4, 5, 6])
>>> np.setdiff1d(a, b)
array([1, 2])
numpy.setxor1d
原文:
numpy.org/doc/1.26/reference/generated/numpy.setxor1d.html
numpy.setxor1d(ar1, ar2, assume_unique=False)
查找两个数组的集合异或。
返回仅存在于两个输入数组中的排序、唯一值。
参数:
ar1, ar2:数组样式
输入数组。
assume_unique:布尔值
如果为真,则假定输入数组都是唯一的,这可以加快计算速度。默认为假。
返回值:
setxor1d:ndarray
排序的一维数组,其中包含仅存在于两个输入数组中的唯一值。
示例:
>>> a = np.array([1, 2, 3, 2, 4])
>>> b = np.array([2, 3, 5, 7, 5])
>>> np.setxor1d(a,b)
array([1, 4, 5, 7])
numpy.union1d
原文:
numpy.org/doc/1.26/reference/generated/numpy.union1d.html
numpy.union1d(ar1, ar2)
找到两个数组的并集。
返回两个输入数组中任一数组中的唯一、排序后的值数组。
参数:
ar1, ar2array_like
输入数组。如果它们不是 1D,则会被展平。
返回:
union1dndarray
输入数组的唯一、排序后的并集。
另请参阅
numpy.lib.arraysetops
该模块包含许多其他用于在数组上执行集合操作的函数。
示例
>>> np.union1d([-1, 0, 1], [-2, 0, 2])
array([-2, -1, 0, 1, 2])
要找到多于两个数组的并集,请使用 functools.reduce:
>>> from functools import reduce
>>> reduce(np.union1d, ([1, 3, 4, 3], [3, 1, 2, 1], [6, 3, 4, 2]))
array([1, 2, 3, 4, 6])
Sorting, searching, and counting
Sorting
sort(a[, axis, kind, order]) |
返回数组的排序副本。 |
|---|---|
lexsort(keys[, axis]) |
使用一系列键执行间接稳定排序。 |
argsort(a[, axis, kind, order]) |
返回对数组进行排序的索引。 |
ndarray.sort([axis, kind, order]) |
原地对数组进行排序。 |
sort_complex(a) |
使用实部优先,然后虚部对复数数组进行排序。 |
partition(a, kth[, axis, kind, order]) |
返回数组的分区副本。 |
argpartition(a, kth[, axis, kind, order]) |
使用 kind 关键字指定的算法沿给定轴执行间接分区。 |
Searching
argmax(a[, axis, out, keepdims]) |
返回沿轴的最大值的索引。 |
|---|---|
nanargmax(a[, axis, out, keepdims]) |
返回指定轴上最大值的索引,忽略 NaN 值。 |
argmin(a[, axis, out, keepdims]) |
返回沿轴的最小值的索引。 |
nanargmin(a[, axis, out, keepdims]) |
返回指定轴上最小值的索引,忽略 NaN 值。 |
argwhere(a) |
找到非零数组元素的索引,按元素分组。 |
nonzero(a) |
返回非零元素的索引。 |
flatnonzero(a) |
返回扁平化版本中非零的索引。 |
where(condition, [x, y], /) |
根据条件从 x 或 y 中返回元素。 |
searchsorted(a, v[, side, sorter]) |
查找应插入元素以保持顺序的索引。 |
extract(condition, arr) |
返回满足某些条件的数组元素。 |
Counting
count_nonzero(a[, axis, keepdims]) |
计算数组a中非零值的数量。 |
|---|
Sorting
sort(a[, axis, kind, order]) |
返回数组的排序副本。 |
|---|---|
lexsort(keys[, axis]) |
使用一系列键执行间接稳定排序。 |
argsort(a[, axis, kind, order]) |
返回对数组进行排序的索引。 |
ndarray.sort([axis, kind, order]) |
在原地对数组进行排序。 |
sort_complex(a) |
首先使用实部,然后使用虚部对复数数组进行排序。 |
partition(a, kth[, axis, kind, order]) |
返回数组的分区副本。 |
argpartition(a, kth[, axis, kind, order]) |
使用由kind关键字指定的算法沿着给定轴执行间接分区。 |
Searching
argmax(a[, axis, out, keepdims]) |
返回沿着轴的最大值的索引。 |
|---|---|
nanargmax(a[, axis, out, keepdims]) |
返回指定轴中最大值的索引,忽略 NaN。 |
argmin(a[, axis, out, keepdims]) |
返回沿轴的最小值的索引。 |
nanargmin(a[, axis, out, keepdims]) |
返回指定轴中最小值的索引,忽略 NaN。 |
argwhere(a) |
找到非零的数组元素的索引,按元素分组。 |
nonzero(a) |
返回非零元素的索引。 |
flatnonzero(a) |
返回在数组的扁平版本中为非零的索引。 |
where(condition, [x, y], /) |
根据condition返回从x或y中选择的元素。 |
searchsorted(a, v[, side, sorter]) |
查找元素应插入以保持顺序的索引。 |
extract(condition, arr) |
返回满足某些条件的数组元素。 |
Counting
count_nonzero(a[, axis, keepdims]) |
统计数组 a 中非零值的数量。 |
|---|
numpy.sort
numpy.sort(a, axis=-1, kind=None, order=None)
返回数组的排序副本。
参数:
a类似数组
要排序的数组。
axisint 或 None,可选
用于排序的轴。如果为 None,则在排序之前将数组扁平化。默认值为-1,表示沿着最后一个轴排序。
kind,可选
排序算法。默认为‘quicksort’。请注意,‘stable’和‘mergesort’都在底层使用 timsort 或基数排序,一般情况下,实际实现会根据数据类型而有所不同。‘mergesort’选项保留供向后兼容使用。
1.15.0 版更改:增加了‘stable’选项。
orderstr 或 str 列表,可选
当 a 是已定义字段的数组时,此参数指定首选比较的字段顺序,第一、第二等。可以将单个字段指定为字符串,不需要指定所有字段,但未指定字段仍将按照它们在 dtype 中出现的顺序用于打破关系。
返回:
sorted_arrayndarray
与 a 类型和形状相同的数组。
另请参见
ndarray.sort
在原位对数组进行排序的方法。
argsort
间接排序。
lexsort
多个键的间接稳定排序。
searchsorted
在排序数组中查找元素。
partition
部分排序。
注意事项
各种排序算法的特点在于它们的平均速度、最坏情况性能、工作空间大小以及它们是否稳定。稳定排序会保持具有相同键的项在相对顺序中保持一致。NumPy 中实现的四种算法具有以下属性:
| 类型 | 速度 | 最坏情况 | 工作空间 | 稳定 |
|---|---|---|---|---|
| ‘quicksort’ | 1 | O(n²) | 0 | no |
| ‘heapsort’ | 3 | O(n*log(n)) | 0 | no |
| ‘mergesort’ | 2 | O(n*log(n)) | ~n/2 | yes |
| ‘timsort’ | 2 | O(n*log(n)) | ~n/2 | yes |
注意
数据类型确定实际使用的是‘mergesort’还是‘timsort’,即使指定了‘mergesort’。目前不提供更细粒度的用户选择。
所有排序算法在除了最后一个轴之外的任何轴上排序时都会对数据进行临时复制。因此,沿着最后一个轴排序比沿着其他任何轴排序更快,并且使用的空间更少。
对于复数,排序顺序是词典序。如果实部和虚部都不是 NaN,则顺序由实部确定,除非它们相等,在这种情况下,顺序由虚部确定。
在 numpy 1.4.0 之前,对包含 nan 值的实数和复数数组进行排序会导致未定义的行为。在 numpy 版本>= 1.4.0 中,nan 值被排序到末尾。扩展的排序顺序是:
- 实数:[R, nan]
- 复数:[R + Rj, R + nanj, nan + Rj, nan + nanj]
其中 R 是一个非 nan 实数值。具有相同 nan 位置的复数值根据非 nan 部分(如果存在)进行排序。非 nan 值按照以前的方式进行排序。
新版本 1.12.0 中新增。
quicksort 已更改为introsort。当排序没有足够的进展时,它会切换到heapsort。这种实现使得 quicksort 在最坏情况下为 O(n*log(n))。
‘stable’会自动选择最适合数据类型的稳定排序算法。它,以及‘mergesort’目前映射到timsort或基数排序,具体取决于数据类型。API 向前兼容性目前限制了选择实现的能力,并且对于不同的数据类型是硬编码的。
新版本 1.17.0 中新增。
Timsort 用于在已经或几乎排序的数据上获得更好的性能。在随机数据上,timsort 几乎与 mergesort 相同。现在它用于稳定排序,而 quicksort 仍然是默认排序(如果没有选择)。有关 timsort 的详细信息,请参考CPython listsort.txt。‘mergesort’和‘stable’映射到基数排序以用于整数数据类型。基数排序是 O(n)排序,而不是 O(n log n)。
在版本 1.18.0 中更改。
NaT 现在为了与 NaN 一致性而排序到数组末尾。
例子
>>> a = np.array([[1,4],[3,1]])
>>> np.sort(a) # sort along the last axis
array([[1, 4],
[1, 3]])
>>> np.sort(a, axis=None) # sort the flattened array
array([1, 1, 3, 4])
>>> np.sort(a, axis=0) # sort along the first axis
array([[1, 1],
[3, 4]])
使用order关键字指定在对结构化数组进行排序时要使用的字段:
>>> dtype = [('name', 'S10'), ('height', float), ('age', int)]
>>> values = [('Arthur', 1.8, 41), ('Lancelot', 1.9, 38),
... ('Galahad', 1.7, 38)]
>>> a = np.array(values, dtype=dtype) # create a structured array
>>> np.sort(a, order='height')
array([('Galahad', 1.7, 38), ('Arthur', 1.8, 41),
('Lancelot', 1.8999999999999999, 38)],
dtype=[('name', '|S10'), ('height', '<f8'), ('age', '<i4')])
按年龄排序,如果年龄相等,则按身高排序:
>>> np.sort(a, order=['age', 'height'])
array([('Galahad', 1.7, 38), ('Lancelot', 1.8999999999999999, 38),
('Arthur', 1.8, 41)],
dtype=[('name', '|S10'), ('height', '<f8'), ('age', '<i4')])
numpy.lexsort
原文:
numpy.org/doc/1.26/reference/generated/numpy.lexsort.html
numpy.lexsort(keys, axis=-1)
使用一系列键执行间接稳定排序。
给定多个排序键,可以将其解释为电子表格中的列,lexsort 返回一个整数索引数组,描述了按多个列排序的顺序。序列中的最后一个键用于主要排序顺序,倒数第二个键用于次要排序顺序,依此类推。键参数必须是可以转换为相同形状数组的对象序列。如果为键参数提供了一个 2D 数组,则其行被解释为排序键,并且排序是根据最后一行、倒数第二行等进行的。
参数:
keys(k, N) 包含 k 个 (N,)-shaped 序列的数组或元组
k 不同的“列”要进行排序。最后一列(如果keys是一个 2D 数组,则为最后一行)是主要排序键。
axisint,可选
要进行间接排序的轴。默认情况下,对最后一个轴进行排序。
返回:
indices(N,) 整数的 ndarray
沿指定轴对键进行排序的索引数组。
另请参阅
argsort
间接排序。
ndarray.sort
原地排序。
sort
返回数组的排序副本。
示例
按姓氏排序:先按姓氏,再按名字。
>>> surnames = ('Hertz', 'Galilei', 'Hertz')
>>> first_names = ('Heinrich', 'Galileo', 'Gustav')
>>> ind = np.lexsort((first_names, surnames))
>>> ind
array([1, 2, 0])
>>> [surnames[i] + ", " + first_names[i] for i in ind]
['Galilei, Galileo', 'Hertz, Gustav', 'Hertz, Heinrich']
对两列数字进行排序:
>>> a = [1,5,1,4,3,4,4] # First column
>>> b = [9,4,0,4,0,2,1] # Second column
>>> ind = np.lexsort((b,a)) # Sort by a, then by b
>>> ind
array([2, 0, 4, 6, 5, 3, 1])
>>> [(a[i],b[i]) for i in ind]
[(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)]
请注意,首先根据a的元素进行排序。次要排序是根据b的元素进行的。
正常的argsort将产生:
>>> [(a[i],b[i]) for i in np.argsort(a)]
[(1, 9), (1, 0), (3, 0), (4, 4), (4, 2), (4, 1), (5, 4)]
结构化数组通过argsort按字典顺序排序:
>>> x = np.array([(1,9), (5,4), (1,0), (4,4), (3,0), (4,2), (4,1)],
... dtype=np.dtype([('x', int), ('y', int)]))
>>> np.argsort(x) # or np.argsort(x, order=('x', 'y'))
array([2, 0, 4, 6, 5, 3, 1])
numpy.argsort
原文:
numpy.org/doc/1.26/reference/generated/numpy.argsort.html
numpy.argsort(a, axis=-1, kind=None, order=None)
返回将数组排序的索引。
使用由kind关键字指定的算法沿给定轴进行间接排序。它返回一个与a形状相同的索引数组,按照排序顺序索引沿给定轴的数据。
参数:
aarray_like
要排序的数组。
axisint 或 None,可选
用于排序的轴。默认为-1(最后一个轴)。如果为 None,则使用扁平化的数组。
kind, 可选
排序算法。默认为“quicksort”。请注意,“stable”和“mergesort”都在内部使用 timsort,一般情况下,实际实现会随数据类型而变化。保留“mergesort”选项是为了向后兼容性。
1.15.0 版本更改:添加了“stable”选项。
orderstr 或 str 列表,可选
当a是一个定义了字段的数组时,此参数指定首先比较哪些字段,第二个字段等等。可以将单个字段指定为字符串,并且不需要指定所有字段,但未指定的字段仍将按照它们在 dtype 中出现的顺序来使用,以打破平局。
返回:
index_arrayndarray, int
沿指定axis对a进行排序的索引数组。如果a是一维的,则a[index_array]会产生一个排序后的a。更一般地,np.take_along_axis(a, index_array, axis=axis)始终产生排序后的a,无论维度如何。
参见
sort
描述所使用的排序算法。
lexsort
使用多个键进行间接稳定排序。
ndarray.sort
原地排序。
argpartition
间接部分排序。
take_along_axis
将 argsort 中的index_array应用于数组,就像调用 sort 一样。
注意
有关不同排序算法的说明,请参见sort。
截至 NumPy 1.4.0,argsort可用于包含 nan 值的实数/复数数组。增强的排序顺序在sort中有文档记录。
示例
一维数组:
>>> x = np.array([3, 1, 2])
>>> np.argsort(x)
array([1, 2, 0])
二维数组:
>>> x = np.array([[0, 3], [2, 2]])
>>> x
array([[0, 3],
[2, 2]])
>>> ind = np.argsort(x, axis=0) # sorts along first axis (down)
>>> ind
array([[0, 1],
[1, 0]])
>>> np.take_along_axis(x, ind, axis=0) # same as np.sort(x, axis=0)
array([[0, 2],
[2, 3]])
>>> ind = np.argsort(x, axis=1) # sorts along last axis (across)
>>> ind
array([[0, 1],
[0, 1]])
>>> np.take_along_axis(x, ind, axis=1) # same as np.sort(x, axis=1)
array([[0, 3],
[2, 2]])
N 维数组的排序元素的索引:
>>> ind = np.unravel_index(np.argsort(x, axis=None), x.shape)
>>> ind
(array([0, 1, 1, 0]), array([0, 0, 1, 1]))
>>> x[ind] # same as np.sort(x, axis=None)
array([0, 2, 2, 3])
使用键进行排序:
>>> x = np.array([(1, 0), (0, 1)], dtype=[('x', '<i4'), ('y', '<i4')])
>>> x
array([(1, 0), (0, 1)],
dtype=[('x', '<i4'), ('y', '<i4')])
>>> np.argsort(x, order=('x','y'))
array([1, 0])
>>> np.argsort(x, order=('y','x'))
array([0, 1])
numpy.ndarray.sort
原文:
numpy.org/doc/1.26/reference/generated/numpy.ndarray.sort.html
方法
ndarray.sort(axis=-1, kind=None, order=None)
就地对数组进行排序。有关完整文档,请参阅numpy.sort。
参数:
axisint, 可选
排序的轴。默认为-1,表示沿着最后一个轴排序。
kind, 可选
排序算法。默认为‘quicksort’。请注意,‘stable’和‘mergesort’都在底层使用 timsort,一般情况下,实际实现会随数据类型而变化。‘mergesort’选项保留用于向后兼容性。
1.15.0 版本更改:添加了‘stable’选项。
orderstr 或 str 列表,可选
当a是一个定义了字段的数组时,此参数指定首先比较哪些字段,第二个字段等。可以将单个字段指定为字符串,并且不需要指定所有字段,但未指定的字段仍将按照它们在 dtype 中出现的顺序来使用,以解决平局。
参见
numpy.sort
返回数组的排序副本。
numpy.argsort
间接排序。
numpy.lexsort
多个键的间接稳定排序。
numpy.searchsorted
在排序数组中查找元素。
numpy.partition
部分排序。
注意事项
有关不同排序算法的说明,请参见numpy.sort。
示例
>>> a = np.array([[1,4], [3,1]])
>>> a.sort(axis=1)
>>> a
array([[1, 4],
[1, 3]])
>>> a.sort(axis=0)
>>> a
array([[1, 3],
[1, 4]])
使用order关键字指定在对结构化数组进行排序时要使用的字段:
>>> a = np.array([('a', 2), ('c', 1)], dtype=[('x', 'S1'), ('y', int)])
>>> a.sort(order='y')
>>> a
array([(b'c', 1), (b'a', 2)],
dtype=[('x', 'S1'), ('y', '<i8')])
numpy.sort_complex
numpy.org/doc/1.26/reference/generated/numpy.sort_complex.html
numpy.sort_complex(a)
使用实部首先,然后是虚部对复数数组进行排序。
参数:
a 类似数组
输入数组
返回:
out 复数 ndarray
始终返回排序后的复数数组。
示例
>>> np.sort_complex([5, 3, 6, 2, 1])
array([1.+0.j, 2.+0.j, 3.+0.j, 5.+0.j, 6.+0.j])
>>> np.sort_complex([1 + 2j, 2 - 1j, 3 - 2j, 3 - 3j, 3 + 5j])
array([1.+2.j, 2.-1.j, 3.-3.j, 3.-2.j, 3.+5.j])
numpy.partition
译文:
numpy.org/doc/1.26/reference/generated/numpy.partition.html
numpy.partition(a, kth, axis=-1, kind='introselect', order=None)
返回数组的分区副本。
创建数组的副本,其元素重新排列,使得第 k 个位置的元素的值在排序数组中的位置。在分区数组中,所有在第 k 个元素之前的元素都小于或等于该元素,而在第 k 个元素之后的所有元素都大于或等于该元素。两个分区中元素的顺序是未定义的。
自版本 1.8.0 起新增。
参数:
a类似数组
要排序的数组。
kthint 或 int 序列
要按元素索引进行分区的元素。元素的第 k 个值将在其最终排序位置上,所有较小的元素将在其前面移动,所有相等或更大的元素将在其后面。分区中所有元素的顺序是未定义的。如果提供了一个 k-th 序列,它将一次将所有由它们的 k-th 索引的元素分区到它们的排序位置。
自版本 1.22.0 起弃用:将布尔值作为索引传递已弃用。
轴整数或 None,可选
要排序的轴。如果为 None,则在排序之前将数组展平。默认值为 -1,沿着最后一个轴排序。
种类,可选
选择算法。默认为 ‘introselect’。
顺序字符串或字符串列表,可选
当 a 是一个定义了字段的数组时,此参数指定首先比较哪些字段,其次是哪些字段等。可以将单个字段指定为字符串。不需要指定所有字段,但未指定的字段仍将按照它们在 dtype 中出现的顺序来解决冲突。
返回:
分区数组ndarray
与 a 相同类型和形状的数组。
另请参阅
ndarray.partition
在原地对数组进行排序的方法。
argpartition
间接分区。
sort
完全排序
笔记
各种选择算法的特点在于它们的平均速度、最坏情况性能、工作空间大小以及它们是否稳定。稳定排序会保持具有相同键的项目相对顺序不变。可用的算法具有以下特性:
| 种类 | 速度 | 最坏情况 | 工作空间 | 稳定性 |
|---|---|---|---|---|
| ‘introselect’ | 1 | O(n) | 0 | 否 |
所有分区算法在沿着除最后一个轴以外的任何轴进行分区时都会对数据进行临时复制。因此,沿着最后一个轴进行分区比沿着其他任何轴进行分区更快,使用的空间也更少。
复数的排序顺序是按字典顺序排列的。如果实部和虚部都不是 nan,则顺序由实部确定,除非它们相等,在这种情况下,顺序由虚部确定。
示例
>>> a = np.array([7, 1, 7, 7, 1, 5, 7, 2, 3, 2, 6, 2, 3, 0])
>>> p = np.partition(a, 4)
>>> p
array([0, 1, 2, 1, 2, 5, 2, 3, 3, 6, 7, 7, 7, 7])
p[4]为 2;p[:4]中的所有元素都小于或等于p[4],p[5:]中的所有元素都大于或等于p[4]。划分如下:
[0, 1, 2, 1], [2], [5, 2, 3, 3, 6, 7, 7, 7, 7]
下一个示例展示了传递给kth的多个值的使用。
>>> p2 = np.partition(a, (4, 8))
>>> p2
array([0, 1, 2, 1, 2, 3, 3, 2, 5, 6, 7, 7, 7, 7])
p2[4]为 2,p2[8]为 5。p2[:4]中的所有元素都小于或等于p2[4],p2[5:8]中的所有元素都大于或等于p2[4]且小于或等于p2[8],p2[9:]中的所有元素都大于或等于p2[8]。划分如下:
[0, 1, 2, 1], [2], [3, 3, 2], [5], [6, 7, 7, 7, 7]
numpy.argpartition
原文:
numpy.org/doc/1.26/reference/generated/numpy.argpartition.html
numpy.argpartition(a, kth, axis=-1, kind='introselect', order=None)
使用 kind 关键字指定的算法沿给定轴执行间接分区。它返回一个与 a 相同形状的索引数组,按照分区顺序索引给定轴上的数据。
自版本 1.8.0 起新增。
参数:
a类似数组
要排序的数组。
kth整数或整数序列
要按其进行分区的元素索引。第 k 个元素将处于其最终排序位置,所有较小的元素将在其前面移动,所有较大的元素将在其后面。分区中所有元素的顺序是未定义的。如果提供了 k-th 的序列,则会一次将它们全部分区到其排序位置。
自版本 1.22.0 起弃用:将布尔值作为索引已弃用。
axis整数或 None,可选
用于排序的轴。默认为 -1(最后一个轴)。如果为 None,则使用扁平化的数组。
kind,可选
选择算法。默认为 ‘introselect’
orderstr 或 str 列表,可选
当 a 是一个定义了字段的数组时,此参数指定首先比较哪些字段,第二个字段等。可以将单个字段指定为字符串,不需要指定所有字段,但未指定的字段仍将被使用,按照它们在 dtype 中出现的顺序来打破平局。
返回:
index_arrayndarray,整数
沿指定轴对 a 进行分区的索引数组。如果 a 是一维的,a[index_array] 会产生一个分区的 a。更一般地,np.take_along_axis(a, index_array, axis=axis) 总是产生分区的 a,无论维度如何。
另请参阅
partition
描述所使用的分区算法。
ndarray.partition
原地分区。
argsort
完全间接排序。
take_along_axis
将 argpartition 中的 index_array 应用于数组,就像调用分区一样。
注意
有关不同选择算法的说明,请参阅 partition。
示例
一维数组:
>>> x = np.array([3, 4, 2, 1])
>>> x[np.argpartition(x, 3)]
array([2, 1, 3, 4])
>>> x[np.argpartition(x, (1, 3))]
array([1, 2, 3, 4])
>>> x = [3, 4, 2, 1]
>>> np.array(x)[np.argpartition(x, 3)]
array([2, 1, 3, 4])
多维数组:
>>> x = np.array([[3, 4, 2], [1, 3, 1]])
>>> index_array = np.argpartition(x, kth=1, axis=-1)
>>> np.take_along_axis(x, index_array, axis=-1) # same as np.partition(x, kth=1)
array([[2, 3, 4],
[1, 1, 3]])
numpy.argmax
numpy.argmax(a, axis=None, out=None, *, keepdims=<no value>)
返回沿轴的最大值的索引。
参数:
aarray_like
输入数组。
axisint,可选
默认情况下,索引是到扁平数组,否则沿指定轴。
outarray,可选
如果提供,结果将插入到此数组中。它应具有适当的形状和 dtype。
keepdimsbool,可选
如果设置为 True,则被减少的轴将作为大小为一的维度保留在结果中。使用此选项,结果将正确地广播到数组。
1.22.0 版中的新内容。
返回:
index_arrayint 的 ndarray
索引数组。它与 a.shape 具有相同的形状,其中沿 axis 的维度已移除。如果 keepdims 设置为 True,则 axis 的大小将为 1,生成的数组将具有与 a.shape 相同的形状。
另请参见
ndarray.argmax, argmin
amax
沿给定轴的最大值。
unravel_index
将平坦索引转换为索引元组。
take_along_axis
将 np.expand_dims(index_array, axis) 从 argmax 应用到数组上,就像调用 max 一样。
注意
如果最大值出现多次,则返回对应于第一次出现的索引。
示例
>>> a = np.arange(6).reshape(2,3) + 10
>>> a
array([[10, 11, 12],
[13, 14, 15]])
>>> np.argmax(a)
5
>>> np.argmax(a, axis=0)
array([1, 1, 1])
>>> np.argmax(a, axis=1)
array([2, 2])
N 维数组的最大元素的索引:
>>> ind = np.unravel_index(np.argmax(a, axis=None), a.shape)
>>> ind
(1, 2)
>>> a[ind]
15
>>> b = np.arange(6)
>>> b[1] = 5
>>> b
array([0, 5, 2, 3, 4, 5])
>>> np.argmax(b) # Only the first occurrence is returned.
1
>>> x = np.array([[4,2,3], [1,0,3]])
>>> index_array = np.argmax(x, axis=-1)
>>> # Same as np.amax(x, axis=-1, keepdims=True)
>>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1)
array([[4],
[3]])
>>> # Same as np.amax(x, axis=-1)
>>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1)
array([4, 3])
将 keepdims 设置为 True,
>>> x = np.arange(24).reshape((2, 3, 4))
>>> res = np.argmax(x, axis=1, keepdims=True)
>>> res.shape
(2, 1, 4)
numpy.nanargmax
原文:
numpy.org/doc/1.26/reference/generated/numpy.nanargmax.html
numpy.nanargmax(a, axis=None, out=None, *, keepdims=<no value>)
返回指定轴上最大值的索引,忽略 NaN。对于全为 NaN 的切片,会引发ValueError。警告:如果一个切片只包含 NaN 和-Infs,则无法信任结果。
参数:
aarray_like
输入数据。
axisint,可选
沿其操作的轴。默认情况下使用扁平化的输入。
outarray,可选
如果提供了,结果将被插入到这个数组中。它应该具有适当的形状和 dtype。
新版本 1.22.0 中新增。
keepdimsbool,可选
如果设置为 True,则被减少的轴将作为大小为一的维度保留在结果中。使用此选项,结果将正确地广播到数组。
新版本 1.22.0 中新增。
返回:
index_arrayndarray
一个索引数组或单个索引值。
另请参阅
argmax,nanargmin
示例
>>> a = np.array([[np.nan, 4], [2, 3]])
>>> np.argmax(a)
0
>>> np.nanargmax(a)
1
>>> np.nanargmax(a, axis=0)
array([1, 0])
>>> np.nanargmax(a, axis=1)
array([1, 1])
numpy.argmin
numpy.argmin(a, axis=None, out=None, *, keepdims=<no value>)
返回沿轴的最小值的索引。
参数:
a类似数组
输入数组。
axis整数,可选
默认情况下,索引是进入扁平数组,否则沿指定轴。
out数组,可选
如果提供,结果将插入到此数组中。它应具有适当的形状和 dtype。
keepdims布尔值,可选
如果设置为 True,则被减少的轴将作为大小为一的维度保留在结果中。使用此选项,结果将正确地广播到数组。
在 1.22.0 版本中新增。
返回:
index_array整数的 ndarray
数组中的索引数组。它与a.shape具有相同的形状,沿axis的维度被移除。如果keepdims设置为 True,则axis的大小将为 1,生成的数组将具有与a.shape相同的形状。
另请参见
ndarray.argmin, argmax
amin
沿给定轴的最小值。
unravel_index
将一个扁平索引转换为索引元组。
take_along_axis
从 argmin 应用np.expand_dims(index_array, axis)到一个数组,就像调用 min 一样。
注意
在最小值出现多次的情况下,返回对应于第一次出现的索引。
示例
>>> a = np.arange(6).reshape(2,3) + 10
>>> a
array([[10, 11, 12],
[13, 14, 15]])
>>> np.argmin(a)
0
>>> np.argmin(a, axis=0)
array([0, 0, 0])
>>> np.argmin(a, axis=1)
array([0, 0])
N 维数组的最小元素的索引:
>>> ind = np.unravel_index(np.argmin(a, axis=None), a.shape)
>>> ind
(0, 0)
>>> a[ind]
10
>>> b = np.arange(6) + 10
>>> b[4] = 10
>>> b
array([10, 11, 12, 13, 10, 15])
>>> np.argmin(b) # Only the first occurrence is returned.
0
>>> x = np.array([[4,2,3], [1,0,3]])
>>> index_array = np.argmin(x, axis=-1)
>>> # Same as np.amin(x, axis=-1, keepdims=True)
>>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1)
array([[2],
[0]])
>>> # Same as np.amax(x, axis=-1)
>>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1)
array([2, 0])
将keepdims设置为True,
>>> x = np.arange(24).reshape((2, 3, 4))
>>> res = np.argmin(x, axis=1, keepdims=True)
>>> res.shape
(2, 1, 4)
numpy.nanargmin
译文:
numpy.org/doc/1.26/reference/generated/numpy.nanargmin.html
numpy.nanargmin(a, axis=None, out=None, *, keepdims=<no value>)
返回指定轴上最小值的索引,忽略 NaN。对于全 NaN 切片,会引发ValueError。警告:如果切片仅包含 NaN 和 Infs,则无法信任结果。
参数:
aarray_like
输入数据。
axisint,可选
操作的轴。默认使用扁平化的输入。
outarray,可选
如果提供,结果将被插入到此数组中。它应该具有适当的形状和数据类型。
新版本 1.22.0 中提供。
keepdimsbool,可选
如果设置为 True,则被减少的轴将作为大小为一的维度保留在结果中。使用此选项,结果将正确广播到数组。
新版本 1.22.0 中提供。
返回:
index_arrayndarray
一个索引数组或单个索引值。
另请参见
argmin, nanargmax
示例
>>> a = np.array([[np.nan, 4], [2, 3]])
>>> np.argmin(a)
0
>>> np.nanargmin(a)
2
>>> np.nanargmin(a, axis=0)
array([1, 1])
>>> np.nanargmin(a, axis=1)
array([1, 0])
numpy.argwhere
原文:
numpy.org/doc/1.26/reference/generated/numpy.argwhere.html
numpy.argwhere(a)
找到非零元素的数组索引,按元素分组。
参数:
aarray_like
输入数据。
返回:
index_array(N, a.ndim) ndarray
非零元素的索引。索引按元素分组。该数组的形状为(N, a.ndim),其中N是非零项的数量。
参见
where,nonzero
注意
np.argwhere(a) 几乎与 np.transpose(np.nonzero(a)) 相同,但对于 0D 数组产生了正确形状的结果。
argwhere 的输出不适合用于索引数组。为此,请使用 nonzero(a)。
示例
>>> x = np.arange(6).reshape(2,3)
>>> x
array([[0, 1, 2],
[3, 4, 5]])
>>> np.argwhere(x>1)
array([[0, 2],
[1, 0],
[1, 1],
[1, 2]])
numpy.nonzero
原文:
numpy.org/doc/1.26/reference/generated/numpy.nonzero.html
numpy.nonzero(a)
返回非零元素的下标。
返回一个数组的元组,每个维度都包含该维度中非零元素的下标。 a 中的值始终以行为主测试和返回,C 样式排序。
要按元素而不是维度对下标进行分组,请使用argwhere,其将为每个非零元素返回一行。
注意
当在零维数组或标量上调用nonzero(a)时,会将其视为nonzero(atleast_1d(a))。
自版本 1.17.0 开始弃用:如果此行为是刻意的,请显式使用atleast_1d。
参数:
a类似数组
输入数组。
返回:
tuple_of_arrays数组
非零元素的下标。
参见
返回输入数组的扁平版本中非零的下标。
等效的 ndarray 方法。
计算输入数组中的非零元素的数量。
注解
尽管可以使用a[nonzero(a)]获得非零值,但建议改用x[x.astype(bool)]或x[x != 0],这样可以正确处理 0 维数组。
示例
>>> x = np.array([[3, 0, 0], [0, 4, 0], [5, 6, 0]])
>>> x
array([[3, 0, 0],
[0, 4, 0],
[5, 6, 0]])
>>> np.nonzero(x)
(array([0, 1, 2, 2]), array([0, 1, 0, 1]))
>>> x[np.nonzero(x)]
array([3, 4, 5, 6])
>>> np.transpose(np.nonzero(x))
array([[0, 0],
[1, 1],
[2, 0],
[2, 1]])
nonzero的一个常见用法是找到数组的索引,其中条件为 True。给定数组 a,条件 a > 3 是一个布尔数组,因为 False 被解释为 0,np.nonzero(a > 3)产生条件为真的 a 的索引。
>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> a > 3
array([[False, False, False],
[ True, True, True],
[ True, True, True]])
>>> np.nonzero(a > 3)
(array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2]))
使用此结果索引 a 等同于直接使用掩码:
>>> a[np.nonzero(a > 3)]
array([4, 5, 6, 7, 8, 9])
>>> a[a > 3] # prefer this spelling
array([4, 5, 6, 7, 8, 9])
nonzero也可以作为数组的方法调用。
>>> (a > 3).nonzero()
(array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2]))
numpy.flatnonzero
原文:
numpy.org/doc/1.26/reference/generated/numpy.flatnonzero.html
numpy.flatnonzero(a)
返回扁平化版本 a 中非零的索引。
这相当于np.nonzero(np.ravel(a))[0]。
参数:
a数组样式
输入数据。
返回:
res数组
输出数组,包含a.ravel()中非零的元素的索引。
另请参见
nonzero
返回输入数组中非零元素的索引。
ravel
返回一个包含输入数组元素的 1-D 数组。
示例
>>> x = np.arange(-2, 3)
>>> x
array([-2, -1, 0, 1, 2])
>>> np.flatnonzero(x)
array([0, 1, 3, 4])
使用非零元素的索引作为索引数组来提取这些元素:
>>> x.ravel()[np.flatnonzero(x)]
array([-2, -1, 1, 2])
numpy.where
numpy.where(condition, [x, y, ]/)
根据 condition 从 x 或 y 中选择元素。
注
仅提供 condition 时,这函数就是 np.asarray(condition).nonzero() 的简写。应优先使用 nonzero,因为它对子类的行为正确。本文档余下部分仅适用于提供了三个参数的情况。
参数:
conditionarray_like, bool
在为 True 时产生 x,否则产生 y。
x, yarray_like
需要选择的值。 x、y 和 condition 需要能广播到某种形状。
返回:
outndarray
在 condition 为 True 时具有 x 的元素,其他情况下具有 y 的元素。
另请参阅
choose
nonzero
当省略 x 和 y 时调用的函数
注意事项
如果所有数组都是 1-D,则 where 相当于:
[xv if c else yv
for c, xv, yv in zip(condition, x, y)]
示例
>>> a = np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.where(a < 5, a, 10*a)
array([ 0, 1, 2, 3, 4, 50, 60, 70, 80, 90])
这也适用于多维数组:
>>> np.where([[True, False], [True, True]],
... [[1, 2], [3, 4]],
... [[9, 8], [7, 6]])
array([[1, 8],
[3, 4]])
x、y 和 condition 的形状一起进行广播:
>>> x, y = np.ogrid[:3, :4]
>>> np.where(x < y, x, 10 + y) # both x and 10+y are broadcast
array([[10, 0, 0, 0],
[10, 11, 1, 1],
[10, 11, 12, 2]])
>>> a = np.array([[0, 1, 2],
... [0, 2, 4],
... [0, 3, 6]])
>>> np.where(a < 4, a, -1) # -1 is broadcast
array([[ 0, 1, 2],
[ 0, 2, -1],
[ 0, 3, -1]])
numpy.searchsorted
原文:
numpy.org/doc/1.26/reference/generated/numpy.searchsorted.html
numpy.searchsorted(a, v, side='left', sorter=None)
查找应插入以保持顺序的元素的索引。
找到排序数组 a 中的索引,使得如果 v 的对应元素被插入到这些索引之前,a 的顺序将会被保留。
假设 a 已排序:
| side | 返回的索引 i 满足条件 |
|---|---|
| left | a[i-1] < v <= a[i] |
| right | a[i-1] <= v < a[i] |
参数:
a1-D 数组样式
输入数组。如果 sorter 为 None,则必须按升序排列,否则 sorter 必须是一个对其进行排序的索引数组。
v数组样式
要插入到 a 中的值。
side,可选
若为‘left’,则给出找到的第一个适当位置的索引。若为‘right’,则返回最后一个这样的索引。如果没有合适的索引,返回 0 或 N(其中 N 为a的长度)。
sorter1-D 数组样式,可选
整数索引数组,将数组a排序为升序。它们通常是 argsort 的结果。
版本 1.7.0 中的新内容。
返回:
indices整数或整数数组
具有与 v 相同形状的插入点数组,或者如果 v 是标量,则为整数。
另请参阅
sort
返回数组的排序副本。
histogram
从 1-D 数据生成直方图。
注意事项
二分搜索用于查找所需的插入点。
截至 NumPy 1.4.0,searchsorted 可与包含nan 值的实/复数数组一起使用。增强的排序顺序在sort 中有文档记录。
此函数使用与内置 python bisect.bisect_left (side='left') 和 bisect.bisect_right 函数相同的算法,也在 v 参数中向量化。
示例
>>> np.searchsorted([1,2,3,4,5], 3)
2
>>> np.searchsorted([1,2,3,4,5], 3, side='right')
3
>>> np.searchsorted([1,2,3,4,5], [-10, 10, 2, 3])
array([0, 5, 1, 2])
numpy.extract
原文:
numpy.org/doc/1.26/reference/generated/numpy.extract.html
numpy.extract(condition, arr)
返回满足某些条件的数组元素。
这相当于np.compress(ravel(condition), ravel(arr))。如果condition是布尔类型,np.extract相当于arr[condition]。
请注意,place与extract完全相反。
参数:
conditionarray_like
一个非零或 True 的数组,指示要提取的arr中的元素。
arrarray_like
与condition大小相同的输入数组。
返回:
提取ndarray
condition为 True 时,从arr中提取的值为condition为 True 的值的一维数组。
另请参阅
take, put, copyto, compress, place
例子
>>> arr = np.arange(12).reshape((3, 4))
>>> arr
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> condition = np.mod(arr, 3)==0
>>> condition
array([[ True, False, False, True],
[False, False, True, False],
[False, True, False, False]])
>>> np.extract(condition, arr)
array([0, 3, 6, 9])
如果condition是布尔类型:
>>> arr[condition]
array([0, 3, 6, 9])
numpy.count_nonzero
原文:
numpy.org/doc/1.26/reference/generated/numpy.count_nonzero.html
numpy.count_nonzero(a, axis=None, *, keepdims=False)
计算数组 a 中非零值的数量。
“非零”一词是指 Python 2.x 内置方法 __nonzero__()(在 Python 3.x 中更名为 __bool__())对 Python 对象进行“真实性”测试。例如,如果一个数字非零,则被视为真实,而如果一个字符串不为空,则被视为真实。因此,该函数(递归地)计算 a 中有多少元素(及其中的子数组)的 __nonzero__() 或 __bool__() 方法评估为 True。
参数:
a类似数组
要计算非零值的数组。
axisint 或元组, 可选
沿其计算非零值的轴或轴的元组。默认为 None,意味着非零值将沿着 a 的扁平版本计算。
新版本 1.12.0 中加入。
keepdimsbool, 可选
如果设置为 True,则将计算的轴保留在结果中作为大小为一的维度。使用此选项,结果将正确广播到输入数组。
新版本 1.19.0 中加入。
返回:
countint 或 int 数组
沿着给定轴的数组中非零值的数量。否则,返回数组中的总非零值数量。
参见
返回所有非零值的坐标。
示例
>>> np.count_nonzero(np.eye(4))
4
>>> a = np.array([[0, 1, 7, 0],
... [3, 0, 2, 19]])
>>> np.count_nonzero(a)
5
>>> np.count_nonzero(a, axis=0)
array([1, 1, 2, 1])
>>> np.count_nonzero(a, axis=1)
array([2, 3])
>>> np.count_nonzero(a, axis=1, keepdims=True)
array([[2],
[3]])


浙公网安备 33010602011771号