如何在不使用循环的情况下生成循环数字序列?

时间:2017-04-10 07:58:58

标签: python numpy vectorization

我想要生成一个循环的数字序列,例如[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)将比我想象的要小。我知道这是一个非常新的问题,但我真的卡住了

5 个答案:

答案 0 :(得分: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会在重复之前展平数组。

答案 1 :(得分:2)

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

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

方法#2:为了提高性能,这是另一种方法,它会将最大间隔数的多个切片,然后使用slicing来选择第一个{ {1}}元素 -

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 似乎运作良好。

答案 2 :(得分: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中所看到的,与其他答案相比,这种方法在速度方面通常是中间的。当您需要迭代器return而不是listnumpy数组时,我建议何时使用此解决方案。

答案 3 :(得分:1)

首先超长它(使用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会自动在1D

中平铺响应

答案 4 :(得分:0)

你可以使用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)
   ...: