我试图在 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
是样本数,但我只是想知道是否有更好的方法,特别是我做错了什么来引发值错误当集合大小相同时。
Set1
和 Set2
的打印输出以供参考:
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
答案 0 :(得分:2)
您的问题是由于对 numpy 数组的工作方式的误解造成的。第一个例子不能“真正”变成数组,因为 numpy 不支持不规则数组。您最终会得到一个指向两个 Python 列表的对象引用数组。第二个例子是一个合适的 2xN 数值数组。我可以在这里想到两种解决方案。
显而易见的方法(顺便说一下,这在两种情况下都适用)是选择索引而不是子列表。既然是有放回抽样,直接生成索引就可以了:
Set[np.random.randint(N, size=k)]
这个是一样的
Set[np.random.choice(N, k)]
如果您想选择不替换,最好的办法是使用 np.random.choice
,带 replace=False
。这与 shuffle 类似,但效率较低。无论哪种情况,您都可以为索引编写一行代码:
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 的唯一原因是 choice
函数。事实上,我根本不推荐它,因为此时您可以并且可能应该使用 python 标准 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]