我花了很多时间阅读有关在python中获取随机样本的各种答案,random.sample
似乎是最自然和最常见的选择,但是我试图从python {{1对象,并希望有效地做到这一点。
由于python中非常好用且高效的设置功能(交叉点,差异等),我正在使用一个集合。对于我的目的,集合是一种非常有效的数据结构,而列表则没有。我有一个算法情况,我在集合中有set
个元素,并且可能需要为集合的每个采样采用任意大小的N
个子样本。集合的每个子采样都不是完全相同的集合,并且由我必须生成子样本的每个元素的属性定义。这是一些模糊的代码,它演示了算法的复杂性:
N
根据我在网上和某些测试中收集的信息,main_set = set(...) # Values sourced from elsewhere.
capacity = 20
for element in list:
potential_values = main_set - element.set # Exclude values already in element
sample_size = capacity - len(element.set) # Num needed to fill the set to capacity
new_vals = sample(potential_values, sample_size) # <- insert sampling idea here
element.set = element.set | new_vals # Union of sample and element set
似乎将random.sample
转换为set
个对象。 list
,main_set - element.set
的大小几乎总是远大于potential_values
的大小,因此如果在每次采样时必须将potential_values转换为列表,则算法将极大地受到性能影响。
那么有没有人对如何有效地使用套装有任何建议或想法?我很欣赏有关此问题的任何意见,在任何人跳到“过早优化”例程之前,我非常清楚它将要执行的规模以及O(n)和O之间的差异(n ^ 2)非常可观。
我特意不关心所提供的element.set
方法的输出。与sample()
的大小相比,我从potential_values
提取的实际样本是小。相反,所有建议的potential_values
方法都需要类似列表的输入才能工作,这意味着必须首先将sample()
转换为可索引类型,这是我想要避免的。
我现在也意识到我以非常模糊的方式提出了大O符号,可能不应该这样。当我的意思是我想避免O(n ^ 2)时,我的意思是我想避免在循环中添加另一个O(n)操作。正如我所指出的那样potential_values
与main_set - element.set
的时间复杂度相同,所以它已经是O(n ^ 2)。添加list(main_set)
转换会使整个算法更像O(2n ^ 2),但这些都不是很重要。
答案 0 :(得分:1)
你可以使用heapq.nlargest
,它可以接受任何迭代,并提供一个随机密钥来选择,例如:
import random, heapq
sample = heapq.nlargest(sample_size, your_set, key=lambda L: random.random())
注意 - 这将为您提供一个list
对象,因此您需要在必要时进行转换...
答案 1 :(得分:1)
在IPython中快速尝试计时表明,使用heapq.nlargest
并不一定比现有方法更好,请根据需要调整实际数据的特征:
import random
import heapq
set_size = 100000
sample_size = 1000
def sample_heapq(your_set, sample_size):
sample = heapq.nlargest(sample_size, your_set, key = lambda e: random.random())
return sample
def sample_original(your_set, sample_size):
sample = random.sample(your_set, sample_size)
return sample
eg_set = set(range(sample_size))
通过timeit
:
%timeit sample_heapq(eg_set, sample_size)
1000 loops, best of 3: 523 µs per loop
%timeit sample_original(eg_set, sample_size)
1000 loops, best of 3: 479 µs per loop
答案 2 :(得分:1)
正如@ user2357112建议的那样,这里是我原始问题中的代码的拒绝采样版本,它有效地对来自源集合的n个元素进行采样,因为我只是从main_set
中采样尚未在elements.set
中的值1}}。
main_set = set(...) # Values sourced from elsewhere.
capacity = 20
listed_set = list(main_set) # initially convert set to list so we can sample
for element in list:
while len(element.set) < capacity
item = random.choice(listed_set)
element.set.add(item) # Sets cannot contain duplicates, no conditional required
虽然这并没有回答如何直接从python中的set
进行采样的问题,但它确实有效地解决了我的算法尝试做的事情。如果过了一段时间,没有人想出直接从集合中采样的想法或比这更有效的东西,我可能会将此标记为答案。感谢@ user2357112的想法!
正如@LieRyan指出的那样,如果element.set
与main_set
重叠很多,则此算法无法从random.choice()
获取非重叠项。因此,如果我们期望高重叠(例如可能是50%),那么只需使用main_set - element.set
获取两个集合之间的唯一项目,并将其转换为列表将比此方法快得多。本质上,此算法适用于main_set
与element.set
的重叠率与main_set
的百分比非常小的情况。
答案 3 :(得分:0)
取决于您对随机的定义。
只是一些元素,我不关心哪个:
[s.copy().pop() for i in range(count)] # with replacement
copy = s.copy()
[copy.pop() for i in range(count)] # without replacement
具有体面[伪]随机分布的元素:
copy = list(s)
random.sample(copy, count)
可重复的伪随机分布:
copy = sorted(s)
# random.seed(...)
random.sample(copy, count)
可重复的伪随机,假设,运行时开销较小:
heapq.nlargest(...) # per Jon or Marius
讨论:
set.pop()
已经删除并返回任意元素,但如果对象散列值在set中相同,则可以预测,例如如果每次都是相同的数字组合,那么每次设置不同时都可以接受set.copy()
是O(N)
sorted();list.sort()
已O(NlogN)
摊销,可能因为套餐是按哈希随机化的heapq.nlargest
每Median of Medians可能O(N)
,Python实现是一个常量大小的二进制堆,使其成为O(N*log(n))
,因为N个元素通过堆筛过滤大小请注意,唱key=
会增加显着的线性开销,因此O(C*N*log(n))
,您的域将确定C*log(n) <?> logN
是否