python列表中的不连续切片

| 我正在寻找一种有效的方法来实现这一目标,我认为这是一种类似于切片的操作:
>>> mylist = range(100)
>>>magicslicer(mylist, 10, 20)
[0,1,2,3,4,5,6,7,8,9,30,31,32,33,34,35,36,37,38,39,60,61,62,63......,97,98,99]
这个想法是:切片可获取10个元素,然后跳过20个元素,然后获取下一个10,然后跳过下一个20,依此类推。 我认为,如果可能的话,我不应该使用循环,因为使用切片的原因(我想)是在单个操作中有效地执行“提取”。 谢谢阅读。     
已邀请:
itertools.compress
(2.7 / 3.1中的新功能)很好地支持像这样的用例,尤其是当与
itertools.cycle
结合使用时:
from itertools import cycle, compress
seq = range(100)
criteria = cycle([True]*10 + [False]*20) # Use whatever pattern you like
>>> list(compress(seq, criteria))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
Python 2.7计时(相对于Sven的显式列表理解):
$ ./python -m timeit -s \"a = range(100)\" \"[x for start in range(0, len(a), 30) for x in a[start:start+10]]\"
100000 loops, best of 3: 4.96 usec per loop

$ ./python -m timeit -s \"from itertools import cycle, compress\" -s \"a = range(100)\" -s \"criteria = cycle([True]*10 + [False]*20)\" \"list(compress(a, criteria))\"
100000 loops, best of 3: 4.76 usec per loop
Python 3.2计时(也相对于Sven的显式列表理解):
$ ./python -m timeit -s \"a = range(100)\" \"[x for start in range(0, len(a), 30) for x in a[start:start+10]]\"
100000 loops, best of 3: 7.41 usec per loop

$ ./python -m timeit -s \"from itertools import cycle, compress\" -s \"a = range(100)\" -s \"criteria = cycle([True]*10 + [False]*20)\" \"list(compress(a, criteria))\"
100000 loops, best of 3: 4.78 usec per loop
可以看出,相对于2.7中的内联列表理解而言,它没有太大的区别,但是通过避免隐式嵌套范围的开销,在3.2中有很大帮助。 如果目标是遍历结果序列而不是将其转化为完全实现的列表,则在2.7中也可以看到类似的区别:
$ ./python -m timeit -s \"a = range(100)\" \"for x in (x for start in range(0, len(a), 30) for x in a[start:start+10]): pass\"
100000 loops, best of 3: 6.82 usec per loop
$ ./python -m timeit -s \"from itertools import cycle, compress\" -s \"a = range(100)\" -s \"criteria = cycle([True]*10 + [False]*20)\" \"for x in compress(a, criteria): pass\"
100000 loops, best of 3: 3.61 usec per loop
对于特别长的模式,可以将模式表达式中的列表替换为
chain(repeat(True, 10), repeat(False, 20))
这样的表达式,这样就不必在内存中完全创建它。     

bab

也许最好的方法是简单的方法:
def magicslicer(seq, take, skip):
    return [x for start in range(0, len(seq), take + skip)
              for x in seq[start:start + take]]
我认为您无法避免循环。 编辑:因为这被标记为“性能”,所以这里与
a = range(100)
的模数解决方案比较:
In [2]: %timeit [x for start in range(0, len(a), 30)
                   for x in a[start:start + 10]]
100000 loops, best of 3: 4.89 us per loop

In [3]: %timeit [e for i, e in enumerate(a) if i % 30 < 10]
100000 loops, best of 3: 14.8 us per loop
    
不幸的是,我认为切片无法做到这一点。我会使用列表推导来解决问题
>>> a = range(100)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    ...
 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> [e for i, e in enumerate(a) if i % 30 < 10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
    
我不知道您是否仅使用数字,但是如果您坚持使用numpy,则可以采用一种更快的方法。但是,只有当您的列表由平整的等长子列表组成时,以下内容才有效。 为了比较:
import numpy as np
from itertools import cycle, compress

startList = list(range(0, 3000))
startNpArray = np.linspace(0,2999,3000,dtype=np.int)

def WithNumpy(seq, keep, skip):
    return seq.reshape((-1, keep+skip))[:,:keep+1].flatten()

def WithItertools(seq, keep, skip):
    criteria = cycle([True]*keep + [False]* skip)
    return list(compress(seq, criteria))

%timeit WithNumpy(startListNp, 10, 20)
>>> 2.59 µs ± 48.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit WithItertools(startList, 10, 20)
>>> 33.5 µs ± 911 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
我会使用一个循环:
#!/usr/bin/env python


def magicslicer(l, stepsize, stepgap):
    output = []
    i = 0
    while i<len(l):
        output += l[i:i+stepsize]
        i += stepsize + stepgap
    return output


mylist = range(100)
print magicslicer(mylist,10,20)
    
>>>[mylist[start:start+10] for start in mylist[::30]]
>>>[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]]
但我得到列表的列表:(     
mylist = range(100)

otherlist = [\'21\',\'31\',\'689\',\'777\',\'479\',\'51\',\'71\',\'yut\',\'poi\',\'ger\',
             \'11\',\'61\',\'789\',\'zozozozo\',\'8888\',\'1\']



def magic_slicer(iterable,keep,throw):
        it = iter(iterable).next
        for n in xrange((len(iterable)//keep+throw)+1):
                for i in xrange(keep):  yield it()
                for i in xrange(throw):  it()

print list(magic_slicer(mylist,10,20))
print
print list(magic_slicer(otherlist,2,3))


print \'__________________\'


def magic_slicer2(iterable,keep,throw):
        return ( x for i,x in enumerate(iterable) if -1< i%(keep+throw)<keep) 

print list(magic_slicer2(mylist,10,20))
print
print list(magic_slicer2(otherlist,2,3))
结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

[\'21\', \'31\', \'51\', \'71\', \'11\', \'61\', \'1\']
__________________
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

[\'21\', \'31\', \'51\', \'71\', \'11\', \'61\', \'1\']
    
[x for x in range(100) if x%30 < 10]
是另一种方式。但是,随着列表大小的增加,这可能会很慢。 在同一行上的功能
def magic_slice(n, no_elems, step):
    s = no_elems + step
    return [x for x in range(n) if x%s < no_elems]
    

要回复问题请先登录注册