【问题标题】:How to get random.sample() from deque in Python 3?如何从 Python 3 中的双端队列获取 random.sample()?
【发布时间】:2017-03-04 00:55:56
【问题描述】:

我有一个元组的 collections.deque(),我想从中抽取随机样本。 在 Python 2.7 中,我可以使用 batch = random.sample(my_deque, batch_size)

但在 Python 3.4 中,这会引发 TypeError: Population must be a sequence or set. For dicts, use list(d).

在 Python 3 中从双端队列高效采样的最佳解决方法或推荐方法是什么?

【问题讨论】:

  • 如果双端队列足够短,我只需要sample(list(the_deque), k)
  • 奇怪。 random.sample(deq, size) 在 Python 3.5 上为我工作。确认不适用于 3.4

标签: python python-2.7 python-3.x random


【解决方案1】:

deque 上的sample() 在 Python ≥3.5 中运行良好,而且速度非常快。

在 Python 3.4 中,您可以改用它,它的运行速度差不多:

sample_indices = sample(range(len(deq)), 50)
[deq[index] for index in sample_indices]

在我使用 Python 3.6.8 的 MacBook 上,这个解决方案比 Eli Korvigo 的解决方案快 44 倍以上。 :)

我使用 deque 包含 100 万个项目,我抽样了 50 个项目:

from random import sample
from collections import deque

deq = deque(maxlen=1000000)
for i in range(1000000):
    deq.append(i)

sample_indices = set(sample(range(len(deq)), 50))

%timeit [deq[i] for i in sample_indices]
1.68 ms ± 23.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit sample(deq, 50)
1.94 ms ± 60.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit sample(range(len(deq)), 50)
44.9 µs ± 549 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit [val for index, val in enumerate(deq) if index in sample_indices]
75.1 ms ± 410 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

也就是说,正如其他人指出的那样,deque 不太适合随机访问。如果你想实现重放内存,你可以使用这样的循环列表:

class ReplayMemory:
    def __init__(self, max_size):
        self.buffer = [None] * max_size
        self.max_size = max_size
        self.index = 0
        self.size = 0

    def append(self, obj):
        self.buffer[self.index] = obj
        self.size = min(self.size + 1, self.max_size)
        self.index = (self.index + 1) % self.max_size

    def sample(self, batch_size):
        indices = sample(range(self.size), batch_size)
        return [self.buffer[index] for index in indices]

拥有一百万个项目,对 50 个项目进行采样非常快:

%timeit mem.sample(50)
#58 µs ± 691 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

【讨论】:

    【解决方案2】:

    显而易见的方法 - 转换为列表。

    batch = random.sample(list(my_deque), batch_size))
    

    但您可以避免创建整个列表。

    idx_batch = set(sample(range(len(my_deque)), batch_size))
    batch = [val for i, val in enumerate(my_deque) if i in idx_batch] 
    

    附: (已编辑)

    实际上,random.sample 在 Python >= 3.5 中应该可以正常使用双端队列。因为该类已更新以匹配 Sequence 接口。

    In [3]: deq = collections.deque(range(100))
    
    In [4]: random.sample(deq, 10)
    Out[4]: [12, 64, 84, 77, 99, 69, 1, 93, 82, 35]
    

    注意!正如 Geoffrey Irving 在下面的评论中正确指出的那样,您最好将队列转换为列表,因为队列是作为链表实现的,使得每个索引访问 O(n) 的队列大小,因此随机抽样 m值将花费 O(m*n) 时间。

    【讨论】:

    • 请注意,根据文档,random.sample 将是二次时间,因为每个双端队列下标在中间是 O(n)。
    猜你喜欢
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 2018-07-02
    • 2023-04-02
    • 2018-09-08
    • 2018-12-02
    • 2012-09-14
    • 2016-05-11
    相关资源
    最近更新 更多