Разрывный фрагмент в списке 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 и т.д.
Я думаю, что я не должен использовать петли, если это возможно, потому что сама причина использования среза - это (я думаю), чтобы эффективно выполнять "извлечение" за одну операцию.
Спасибо за чтение.
Ответы
Ответ 1
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))
, чтобы он никогда не должен полностью создаваться в памяти.
Ответ 2
Возможно, лучший способ - прямолинейный подход:
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
Ответ 3
Я думаю, что кусочки не могут этого сделать, к сожалению. Я бы решил проблему, используя список понятий
>>> 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]
Ответ 4
Я не знаю, работаете ли вы только с числами, но в случае, если вы находитесь, есть более быстрый способ, если вы придерживаетесь 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)
Ответ 5
Я бы использовал цикл:
#!/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)
Ответ 6
>>>[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]]
но я получаю список списка: (
Ответ 7
[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]
Ответ 8
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']