【问题标题】:How to generate a cyclic sequence of numbers without using looping?如何在不使用循环的情况下生成循环数字序列?
【发布时间】:2017-09-05 04:09:10
【问题描述】:

我想生成一个循环的数字序列,例如:[A B C A B C] 任意长度 N 我试过了:

import numpy as np
def cyclic(N):
    x = np.array([1.0,2.0,3.0]) # The main sequence
    y = np.tile(x,N//3) # Repeats the sequence N//3 times 
    return y

但我的代码的问题是,如果我输入任何不能被三整除的整数,那么结果的长度 (N) 将比我预期的要小。我知道这是一个非常新的问题,但我真的被卡住了

【问题讨论】:

  • 这总是一维数组吗?
  • @WillemVanOnsem 是的
  • 发布的解决方案是否适合您?
  • @Divakar 是的,第一个回答率更高,但为什么?
  • 因为那时你可以考虑接受答案。欲了解更多信息 - meta.stackexchange.com/questions/5234/…

标签: python numpy vectorization


【解决方案1】:

您可以为此使用itertools.cycle,一个无限迭代器:

>>> import itertools
>>> it = itertools.cycle([1,2,3])
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
1

你得到一个特定长度的序列(N),将它与itertools.islice结合起来:

>>> list(itertools.islice(itertools.cycle([1,2,3]),11))
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2]

编辑:正如您在Divakar's benchmark 中看到的那样,与其他答案相比,这种方法在速度方面通常处于中等水平。当您需要迭代器 returned 而不是 listnumpy 数组时,我建议使用此解决方案。

【讨论】:

    【解决方案2】:

    方法#1:这是一种处理通用序列的方法,使用modulus 生成这些循环索引 -

    def cyclic_seq(x, N):
        return np.take(x, np.mod(np.arange(N),len(x)))
    

    方法#2:为了提高性能,这是另一种方法,它平铺到最大间隔数的倍数,然后利用slicing 选择第一个N 元素 -

    def cyclic_seq_v2(x, N):   
        return np.tile(x,(N+N-1)//len(x))[:N]
    

    示例运行 -

    In [81]: cyclic_seq([6,9,2,1,7],14)
    Out[81]: array([6, 9, 2, 1, 7, 6, 9, 2, 1, 7, 6, 9, 2, 1])
    
    In [82]: cyclic_seq_v2([6,9,2,1,7],14)
    Out[82]: array([6, 9, 2, 1, 7, 6, 9, 2, 1, 7, 6, 9, 2, 1])
    

    运行时测试

    In [327]: x = np.random.randint(0,9,(3))
    
    In [328]: %timeit np.resize(x, 10000) # @Daniel Forsman's solution
         ...: %timeit list(itertools.islice(itertools.cycle(x),10000)) # @Chris soln
         ...: %timeit cyclic_seq(x,10000) # Approach #1 from this post
         ...: %timeit cyclic_seq_v2(x,10000) # Approach #2 from this post
         ...: 
    1000 loops, best of 3: 296 µs per loop
    10000 loops, best of 3: 185 µs per loop
    10000 loops, best of 3: 120 µs per loop
    10000 loops, best of 3: 28.7 µs per loop
    
    In [329]: x = np.random.randint(0,9,(30))
    
    In [330]: %timeit np.resize(x, 10000) # @Daniel Forsman's solution
         ...: %timeit list(itertools.islice(itertools.cycle(x),10000)) # @Chris soln
         ...: %timeit cyclic_seq(x,10000) # Approach #1 from this post
         ...: %timeit cyclic_seq_v2(x,10000) # Approach #2 from this post
         ...: 
    10000 loops, best of 3: 38.8 µs per loop
    10000 loops, best of 3: 101 µs per loop
    10000 loops, best of 3: 115 µs per loop
    100000 loops, best of 3: 13.2 µs per loop
    
    In [331]: %timeit np.resize(x, 100000) # @Daniel Forsman's solution
         ...: %timeit list(itertools.islice(itertools.cycle(x),100000)) # @Chris soln
         ...: %timeit cyclic_seq(x,100000) # Approach #1 from this post
         ...: %timeit cyclic_seq_v2(x,100000) # Approach #2 from this post
         ...: 
    1000 loops, best of 3: 297 µs per loop
    1000 loops, best of 3: 942 µs per loop
    1000 loops, best of 3: 1.13 ms per loop
    10000 loops, best of 3: 88.3 µs per loop
    

    在性能方面,approach #2 似乎运行良好。

    【讨论】:

    • 我想你现在想要return np.tile(x,(N//len(x))+1)[:N],例如,cyclic_seq_v2([6,9,2,1,7],7) 返回array([6, 9, 2, 1, 7])
    • 同样令人失望的是,尽管这是任何人都可以使用 np.resize 的唯一可能用途,但有一个单行线至少快 5 倍。
    • 在这个任务中使用numpyitertools 有优势吗?
    • @Chris_Rands 这并没有我想象的那么糟糕!事实上,它在我在计时测试中提出的三个场景之一中击败了numpy.resize。不错!
    • 谢谢!我猜itertools 的优势是当你想要一个迭代器而不是列表或 numpy 数组时。无论如何,为所有这些基准 +1
    【解决方案3】:

    您可以为此使用 itertools 循环。

    In [3]: from itertools import cycle
    In [4]: for x in cycle(['A','B','C']):
    ...:     print(x)
    ...:
    C
    A
    B
    C
    A
    B
    C
    A
    B
    C
    A
    B
    C
    A
    B
    C
    A
    B
    

    编辑: 如果你想用 out 循环来实现它,你将需要递归函数。基于 itertools 循环等的解决方案只是将循环隐藏在导入的函数后面。

    In [5]: def repeater(arr, n):
       ...:     yield arr[0]
       ...:     yield arr[1]
       ...:     yield arr[2]
       ...:     if n == 0:
       ...:         yield StopIteration
       ...:     else:
       ...:          yield from repeater(arr, n-1)
       ...:  
    

    【讨论】:

    • cycleislice 结合起来,轻松创建长度为 n 的序列,正如我在回答中所建议的那样
    • @MrzFarjamirad 避免 if 语句的原因是什么,因为它们只是隐藏在导入后面。
    • @Chris_Rands itertools.cycle 是使用循环实现的,正如 OP 在他的评论中指出的那样,他不想使用任何循环。
    • @greole 感谢您的回答。但我认为在 python 中使用数组的全部意义在于,避免像 if 这样的普通控制结构,并用更有效的东西替换它们。仅此而已。
    【解决方案4】:

    首先超长它(使用math.ceil)然后resize它在tile之后

    import numpy as np
    import math
    def cyclic(N):
        x = np.array([1.0,2.0,3.0]) # The main sequence
        y = np.tile(x, math.ceil(N / 3.0))
        y = np.resize(y, N)
        return y
    

    采纳Daniel Forsman的建议后,可以简化为

    import numpy as np
    def cyclic(N):
        x = np.array([1.0,2.0,3.0]) # The main sequence
        y = np.resize(x, N)
        return y
    

    因为np.resize 会自动在一维中平铺响应

    【讨论】:

    • np.resize 自动将响应平铺在一维中,因此无需额外的步骤。
    【解决方案5】:

    你可以使用numpy.resize

    x = np.array([1.0, 2.0, 3.0])
    
    y = np.resize(x, 13)
    
    y
    Out[332]: array([ 1.,  2.,  3.,  1.,  2.,  3.,  1.,  2.,  3.,  1.,  2.,  3.,  1.])
    

    警告:此答案不会扩展到 2D,因为 resize 在重复之前将数组展平。

    【讨论】:

      猜你喜欢
      • 2019-11-01
      • 2011-05-04
      • 1970-01-01
      • 2017-11-22
      • 1970-01-01
      • 1970-01-01
      • 2019-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多