【问题标题】:randomly choose different sets in numpy?在numpy中随机选择不同的集合?
【发布时间】:2026-01-12 01:10:01
【问题描述】:

我正在尝试在 numpy 中随机选择一组整数,但遇到了一个奇怪的错误。如果我定义具有两组不同大小的 numpy 数组,np.random.choice 可以毫无问题地在它们之间进行选择:

Set1 = np.array([[1, 2, 3], [2, 4]])
In:  np.random.choice(Set1)
Out: [4, 5]

但是,一旦 numpy 数组是相同大小的集合,我就会得到一个值错误:

Set2 = np.array([[1, 3, 5], [2, 4, 6]])
In:   np.random.choice(Set2)
ValueError: a must be 1-dimensional    

可能是用户错误,但我检查了几次,唯一的区别是集合的大小。我意识到我可以这样做:

Chosen = np.random.choice(N, k)
Selection = Set[Chosen]

N 是集合的数量,k 是样本的数量,但我只是想知道是否有更好的方法,特别是当集合为大小一样。

Set1Set2 的打印输出供参考:

In: Set1
Out: array([list([1, 3, 5]), list([2, 4])], dtype=object)
In: type(Set1)
Out: numpy.ndarray

In: Set2
Out: 
array([[1, 3, 5],
       [2, 4, 6]])
In: type(Set2)
Out: numpy.ndarray

【问题讨论】:

  • 你真的打印了数组吗? dtype 不同。 Numpy 实际上并不支持参差不齐的数组,所以你的第一个数组不是你想的那样。
  • 哦,谢谢。是的,当我打印第一个 Set 时,它作为两个列表的数组出现,而非参差不齐的数组显示没有列表的数组。但是当我检查类型时,两者都是 numpy.ndarrays。我将编辑问题以显示打印输出。
  • Type 是 python 的概念,dtype 是 numpy 的概念。来自np.array 的所有内容都是np.ndarray。但是任何带有dtype=object 的东西都不是一个数组。它实际上更像是一个可以修改内容但不能修改大小的 python 列表,而且通常速度较慢。

标签: python numpy sampling


【解决方案1】:

您的问题是由对 numpy 数组的工作原理的误解引起的。第一个示例不能“真正”转换为数组,因为 numpy 不支持不规则数组。您最终会得到一个指向两个 python 列表的对象引用数组。第二个例子是一个适当的 2xN 数值数组。我可以在这里想到两种解决方案。

显而易见的方法(顺便说一下,这两种情况都适用)是选择索引而不是子列表。由于您是替换采样,因此您可以生成索引并直接使用它:

Set[np.random.randint(N, size=k)]

这是一样的

Set[np.random.choice(N, k)]

如果你想选择不替换,最好的办法是使用np.random.choice,加上replace=False。这类似于洗牌,但效率低于洗牌。无论哪种情况,您都可以为索引编写单行:

Set[np.random.choice(N, k, replace=False)]

或者:

index = np.arange(Set.shape[0])
np.random.shuffle(index)
Set[index[:k]]

不过,np.random.shuffle 的好处在于,您可以直接将其应用于Set,无论它是一维数组还是多维数组。洗牌总是沿着第一个轴发生,所以你可以在之后取顶部的 k 元素:

np.random.shuffle(Set)
Set[:k]

洗牌操作只能在原地工作,所以你必须把它写出来。对于大型数组,它的效率也较低,因为无论k 有多小,您都必须预先创建整个范围。

另一种解决方案是将第二个示例转换为与第一个示例类似的列表对象数组。我不推荐这个解决方案,除非您使用 numpy 的 only 原因是 choice 函数。事实上,我根本不推荐它,因为此时您可以并且可能应该使用 pythons 标准 random 模块。除了免责声明,您可以将第二个数组的数据类型强制为object。它将消除使用 numpy 的任何好处,并且不能直接完成。简单地设置dtype=object 仍将创建一个二维数组,但将存储对python int 对象的引用,而不是其中的原语。你必须这样做:

Set = np.zeros(N, dtype=object)
Set[:] = [[1, 2, 3], [2, 4]]

您现在将获得一个与第一个示例中的对象基本等效的对象,因此可以直接应用np.random.choice

注意

我在这里展示遗留的np.random 方法是因为个人惯性,如果没有别的。正如我链接到的文档中所建议的那样,正确的方法是使用新的Generator API。对于choice 方法尤其如此,它在新实现中效率更高。使用不再困难:

Set[np.random.default_rng().choice(N, k, replace=False)]

还有其他优势,例如您现在可以直接选择,甚至可以从多维数组中选择:

np.random.default_rng().choice(Set2, k, replace=False)

shuffle 也是如此,它与choice 一样,现在允许您选择要重新排列的轴:

np.random.default_rng().shuffle(Set)
Set[:k]

【讨论】:

  • 您会期望构造函数尽其所能。当np.array 决定由于子序列参差不齐而无法将某些内容转换为数组时,它只会将子序列视为对象引用。这通常是你想要的。事实上,您可以在 SO 上找到许多关于为什么设置 dtype=object 不会为非参差不齐的输入创建列表数组的问题。
  • @phntm。我已更新以显示无需替换的采样。
  • @phntm。再想一想,您可能只想打乱您的数组...
  • numpy.random.choicereplace=False 效率极低,因为会在后台生成输入的完整排列。由于向后兼容性限制,这是一个无法纠正的设计错误。新的Generator API 没有这个问题。如果可能,新代码应该使用 Generator API。
  • @user2357112supportsMonica。谢谢你的提示。我没有处理足够多的随机机制来使用生成器,所以我在这里更关注索引方面。