【问题标题】:Is there a pythonic way to sample N consecutive elements from a list or numpy array有没有一种pythonic方法可以从列表或numpy数组中采样N个连续元素
【发布时间】:2021-01-27 02:18:32
【问题描述】:

有没有一种 Python 方法可以从列表或 numpy 数组中选择 N 个连续元素。

假设:

Choice = [1,2,3,4,5,6] 

我想通过在 Choice 中随机选择元素 X 以及后面的 N-1 个连续元素来创建一个长度为 N 的新列表。

如果:

X = 4 
N = 4

结果列表将是:

Selection = [5,6,1,2] 

我认为类似于以下内容会起作用。

S = [] 
for i in range(X,X+N):
    S.append(Selection[i%6])    

但我想知道是否有一个 python 或 numpy 函数可以一次选择更有效的元素。

【问题讨论】:

  • 为什么不从 [0, len(choices) - N) 中随机选择一个起始索引?
  • 查看slice notationmy_list[1:3]。如果您考虑循环列表,您将需要弄清楚逻辑。
  • 这能回答你的问题吗? Cycle through list starting at a certain element
  • N 可以比len(Choice) 大吗?
  • @phntm 是的,如果N <= len(Choice) 答案会简单得多。查看我的答案的编辑

标签: python numpy sample


【解决方案1】:

使用itertools,特别是islicecycle

start = random.randint(0, len(Choice) - 1)
list(islice(cycle(Choice), start, start + n))

cycle(Choice) 是一个重复原始列表的无限序列,因此切片 start:start + n 将在必要时换行。

【讨论】:

  • 这太棒了,谢谢!使用 itertools 通常比 numpy 快吗?
  • @phntm:对于大输入,它几乎可以保证比任何其他解决方案都慢,因为它在内存使用和处理时间上都是O(n)islice 不能跳过值,@987654329 @ 将它们全部存储,直到输入用尽)。对于少量输入,您使用什么解决方案并不重要。
【解决方案2】:

您可以使用列表推导,对索引使用模运算以使其保持在列表范围内:

Choice = [1,2,3,4,5,6] 
X = 4 
N = 4
L = len(Choice)
Selection = [Choice[i % L] for i in range(X, X+N)]
print(Selection)

输出

[5, 6, 1, 2]

注意如果N小于等于len(Choice),可以大大简化代码:

Choice = [1,2,3,4,5,6] 
X = 4 
N = 4
L = len(Choice)
Selection = Choice[X:X+N] if X+N <= L else Choice[X:] + Choice[:X+N-L]
print(Selection)

【讨论】:

    【解决方案3】:

    由于您要求最有效的方法,我创建了一个小基准来测试此线程中提出的解决方案。

    我将您当前的解决方案改写为:

    def op(choice, x):
        n = len(choice)
        selection = []
        for i in range(x, x + n):
            selection.append(choice[i % n])
        return selection
    

    其中choice 是输入列表,x 是随机索引。

    如果choice 包含 1_000_000 个随机数,这些是结果:

    chepner: 0.10840400000000017 s
    nick: 0.2066781999999998 s
    op: 0.25887470000000024 s
    fountainhead: 0.3679908000000003 s
    

    完整代码

    import random
    from itertools import cycle, islice
    from time import perf_counter as pc
    import numpy as np
    
    
    def op(choice, x):
        n = len(choice)
        selection = []
        for i in range(x, x + n):
            selection.append(choice[i % n])
        return selection
    
    
    def nick(choice, x):
        n = len(choice)
        return [choice[i % n] for i in range(x, x + n)]
    
    
    def fountainhead(choice, x):
        n = len(choice)
        return np.take(choice, range(x, x + n), mode='wrap')
    
    
    def chepner(choice, x):
        n = len(choice)
        return list(islice(cycle(choice), x, x + n))
    
    
    results = []
    n = 1_000_000
    choice = random.sample(range(n), n)
    x = random.randint(0, n - 1)
    
    # Correctness
    assert op(choice, x) == nick(choice,x) == chepner(choice,x) == list(fountainhead(choice,x))
    
    # Benchmark
    for f in op, nick, chepner, fountainhead:
        t0 = pc()
        f(choice, x)
        t1 = pc()
        results.append((t1 - t0, f))
    
    for t, f in sorted(results):
        print(f'{f.__name__}: {t} s')
    

    【讨论】:

    • 感谢您抽出宝贵时间 - 看到性能结果总是很有趣。
    • 有趣的是,在我的计算机上,对于 1,000,000 个条目,chepner 的优势仅为 15-20% 左右,并且随着列表长度的增加而进一步降低。在较短的长度(10k 或更短)下,chepner 的速度提高了 2 倍以上。
    • @Nick yes chepner 解决方案对于list 来说似乎是最快的。对于较大的列表大小 (>10^6),使用数组作为输入(如 array('i', choice))速度更快,占用的内存更少。如果choice 是一个 Numpy 数组,那么无论输入大小如何,this solution 都是最快的。
    【解决方案4】:

    如果使用numpy 数组作为源,我们当然可以使用numpy "fancy indexing"。

    所以,如果 ChoiceArray 是列表 Choicenumpy 数组等价物,并且如果 Llen(Choice)len(ChoiceArray)

    Selection = ChoiceArray [np.arange(X, N+X) % L]
    

    【讨论】:

      【解决方案5】:

      这是numpy 方法:

      import numpy as np
      
      Selection = np.take(Choice, range(X,N+X), mode='wrap')
      

      即使 Choice 是 Python 列表而不是 numpy 数组也有效。

      【讨论】:

      • 谢谢,这真的很有帮助!使用 np.random.default_rng 和 np.random.choice 有区别吗?
      • @phntm - 是的,存在差异。我认为this SO thread 讨论了这个
      猜你喜欢
      • 2021-11-16
      • 2015-02-06
      • 1970-01-01
      • 1970-01-01
      • 2020-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      相关资源
      最近更新 更多