如何获取numpy.random.choice的索引? - Python

28

是否可以修改numpy.random.choice函数,使其返回所选元素的索引? 基本上,我想创建一个列表并随机选择元素,不重复选择

import numpy as np
>>> a = [1,4,1,3,3,2,1,4]
>>> np.random.choice(a)
>>> 4
>>> a
>>> [1,4,1,3,3,2,1,4]

a.remove(np.random.choice(a))将删除与其遇到的第一个具有该值的列表元素(例如上面示例中的a [1]),这可能不是所选元素(例如a [7])。


2
它可能不是所选元素,但似乎两种情况无法区分。 - Robᵩ
“枚举”可能有效。 - Brian Cain
@Rob:不完全是这样。在我创建列表之后,无论我删除哪个元素,它保持原有的顺序非常重要。 - HappyPy
1
应该有一个函数 np.random.argchoice(...)。 - Jesper - jtk.eth
9个回答

18

关于你的第一个问题,你可以反过来操作,随机从数组a的索引中选择一个,然后获取其值。

>>> a = [1,4,1,3,3,2,1,4]
>>> a = np.array(a)
>>> random.choice(arange(a.size))
6
>>> a[6]

但如果你只需要不重复的随机样本,replace=False 就可以了。我不记得它是什么时候第一次被添加到 random.choice 中的,可能是1.7.0版本。所以如果你正在运行非常旧的 numpy 版本,它可能无法正常工作。请记住,默认情况下为replace=True


除非需要许多相互排斥的选择,否则不需要在此情况下制作列表并从中选择,只需执行 np.random.randint(0,a.size) 即可。 - askewchan
2
@askwchan,没错!我在想什么呢。np.random.randint(0,a.size, size=size_you_want)就足够了。 - CT Zhu
@CT Zhu:我遇到了一个 AttributeError: 'list' object has no attribute 'size' 的错误。 - HappyPy
哦,a 是一个列表,不是一个 array。先将其转换为 array。我忘记复制一行了。 - CT Zhu
1
@askwchan,哦,不。你的方法总是会变成有放回抽样。HappyPy真的需要replace=False,这样一旦一个元素被抽取,它就不会再次被抽取。 - CT Zhu
非常出色的答案 - 它很好,因为可以将索引用于不同的数组 - 例如机器学习 X[ia,:]y[ia] - 你应该将这种用例添加到你的答案中。谢谢! - jtlz2

15

这里有一种方法可以找出随机选定元素的索引:

import random # plain random module, not numpy's
random.choice(list(enumerate(a)))[0]
=> 4      # just an example, index is 4

或者你可以一步检索元素索引:

random.choice(list(enumerate(a)))
=> (1, 4) # just an example, index is 1 and element is 4

2
这对我不起作用。它给了我一个“ValueError:a必须是一维的”。 - HappyPy
@HappyPy,你说得对,我用的是random.choice而不是np.random.choice进行测试。如果你一定要使用np.random.choice,那么我的答案就不适用了,我会将其删除。但是如果你使用普通的random.choice(来自random模块),它会起作用。 - Óscar López
那是因为你正在使用np,@user2357112,示例中使用了random.choice - askewchan
@Óscar López:是的,现在它可以工作了。但是我该如何从“a”中删除所选数字呢?我需要将random.choice(list(enumerate(a)))[0]赋值给一个变量,然后将其用作“a”的索引吗? - HappyPy
10
强烈警告,这将会有可怕的性能表现,这也是人们首先使用numpy的主要原因之一。您正在遍历整个数组。只需生成0到列表长度之间的随机整数比这更便宜。 - Russell Myers
显示剩余4条评论

10
numpy.random.choice(a, size=however_many, replace=False)
如果你想要一个无放回的样本,请让numpy给你制作一个。不要重复循环和抽取物品,这将产生冗余代码和可怕的性能。
例如:
>>> a = numpy.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> numpy.random.choice(a, size=5, replace=False)
array([7, 5, 8, 6, 2])

如果您使用的是足够新的NumPy(至少1.17版本),则应该使用新的随机API,它可以解决旧API中存在已久的性能问题。在旧API中,replace = False代码路径在内部不必要地生成了输入的完整排列。

rng = numpy.random.default_rng()
result = rng.choice(a, size=however_many, replace=False)

我不明白这怎么运作。在这种情况下,“a”是什么?能否举个例子? - HappyPy
@HappyPy:a 就是你代码中的那个数组对象;我们想要从中取样。size 是我们想要的样本元素数量,而 replace=False 表示我们要无重复地取样。结果将是一个形状为 (however_many,) 的一维数组,其中包含你想要的样本。 - user2357112
样本已经是"a"。我想直接使用"a"来控制剩余的元素数量并执行其他操作。 - HappyPy
@HappyPy:听起来你可能完全错误地使用了numpy。如果a已经是一个随机样本,但你想从中抽取元素而不重复,那么你实际上是在从a中抽取另一个随机样本。如果你真的、真的想要逐步从a中删除元素,那么numpy可能无法帮助你。 - user2357112

4

相较于其他答案,我提供的方法可能有些不同,但是我认为它可以在更大的范围内帮助你达到目标。你可以通过打乱源数组中元素的索引来生成一个无需替换的随机样本:

source = np.random.randint(0, 100, size=100) # generate a set to sample from
idx = np.arange(len(source))
np.random.shuffle(idx)
subsample = source[idx[:10]]

这将创建一个样本(这里的样本大小为10),从源集合中(这里的大小为100)无重复地选取元素。

您可以通过使用剩余的索引值与未选择的元素交互,例如:

notsampled = source[idx[10:]]

2

也许有些迟了,但提及这个解决方案是值得的,因为我认为最简单的方法是:

a = [1, 4, 1, 3, 3, 2, 1, 4]
n = len(a)
idx = np.random.choice(list(range(n)), p=np.ones(n)/n)

这意味着您从索引中均匀选择。在更一般的情况下,您可以通过以下方式进行加权抽样(并返回索引):

probs = [.3, .4, .2, 0, .1]
n = len(a)
idx = np.random.choice(list(range(n)), p=probs)

如果你尝试这样做很多次(例如1e5),所选索引的直方图将像在此情况下的[0.30126 0.39817 0.19986 0. 0.10071],这是正确的。无论如何,你应该从这些索引中选择并使用它们的值(如果需要)作为它们的概率。

1

除了使用 choice,你也可以简单地 random.shuffle 你的数组,即:

random.shuffle(a)  # will shuffle a in-place

0

根据您的评论:

样本已经是 a。我想直接使用 a,以便我可以控制还剩下多少元素,并执行其他操作与 a。- HappyPy

听起来你想在从 a 中删除 n 个随机元素后继续使用 a。为什么不直接从 a 中选择 N = len(a) - n 个随机元素呢?由于您希望它们仍然保持原始顺序,因此可以像 @CTZhu 的答案中那样从索引中选择,但然后对它们进行排序并从原始列表中获取:

import numpy as np
n = 3 #number to 'remove'
a = np.array([1,4,1,3,3,2,1,4])
i = np.random.choice(np.arange(a.size), a.size-n, replace=False)
i.sort()
a[i]
#array([1, 4, 1, 3, 1])

现在你可以再次将其保存为a

a = a[i]

并从中移除 n 个元素,然后操作 a


0
问题标题与其描述有些不同。我只想要标题问题的答案,即从numpy.random.choice()中仅获取(整数)索引。而不是上面提到的任何内容,我选择了index = numpy.random.choice(len(array_or_whatever))(在numpy 1.21.6中测试过)。
例如:
import numpy
a = [1, 2, 3, 4]
i = numpy.random.choice(len(a))

在其他解决方案中我遇到的问题是不必要的转换为list,这将在新对象中重新创建整个集合(速度慢!)。
参考:https://numpy.org/doc/stable/reference/random/generated/numpy.random.choice.html?highlight=choice#numpy.random.choice 文档中关于第一个参数a的关键点:

a:1-D 数组或 int 如果是 ndarray,则从其元素生成随机样本。如果是 int,则生成的随机样本就像它是 np.arange(a) 一样。

由于问题非常古老,因此可能我正在从新版本的便利性中接近自己和 OP 想要的东西。

0

这里有一个简单的解决方案,只需从范围函数中选择。

import numpy as np
a = [100,400,100,300,300,200,100,400]
I=np.random.choice(np.arange(len(a)))
print('index is '+str(I)+' number is '+str(a[I]))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接