Создание списка отдельных элементов списка, умноженных n раз
Я новичок в Python и думаю, что это должна быть довольно распространенная проблема, но не может найти решение. Я уже просмотрел эту страницу и нашел, что это полезно для одного элемента, но я изо всех сил пытаюсь расширить пример до нескольких элементов, не используя цикл 'for'. Я запускаю этот бит кода для 250 ходоков через Emcee, поэтому я ищу самый быстрый способ.
У меня есть список чисел, a = [x,y,z]
, который я хочу повторить b = [1,2,3]
раз (например), поэтому я заканчиваю списком списков:
[
[x],
[y,y],
[z,z,z]
]
Для цикла "for" у меня есть:
c = [ ]
for i in range (0,len(a)):
c.append([a[i]]*b[i])
Что делает именно то, что я хочу, но означает, что мой код мучительно медленный. Я также попытался наивно превратить a и b в массивы и сделать [a]*b
в надежде, что он будет умножать элемент на элемент, но не радость.
Ответы
Ответ 1
Вы можете использовать zip
и понимание списка здесь:
>>> a = ['x','y','z']
>>> b = [1,2,3]
>>> [[x]*y for x,y in zip(a,b)]
[['x'], ['y', 'y'], ['z', 'z', 'z']]
или
>>> [[x for _ in xrange(y)] for x,y in zip(a,b)]
[['x'], ['y', 'y'], ['z', 'z', 'z']]
zip
сначала создаст весь список в памяти, чтобы получить использование итератора itertools.izip
В случае, если a
содержит изменяемые объекты, такие как списки или списки списков, тогда вам, возможно, придется использовать copy.deepcopy
здесь, потому что изменение одной копии также изменит другие копии.:
>>> from copy import deepcopy as dc
>>> a = [[1 ,4],[2, 5],[3, 6, 9]]
>>> f = [[dc(x) for _ in xrange(y)] for x,y in zip(a,b)]
#now all objects are unique
>>> [[id(z) for z in x] for x in f]
[[172880236], [172880268, 172880364], [172880332, 172880492, 172880428]]
timeit
сравнения (игнорирование импорта):
>>> a = ['x','y','z']*10**4
>>> b = [100,200,300]*10**4
>>> %timeit [[x]*y for x,y in zip(a,b)]
1 loops, best of 3: 104 ms per loop
>>> %timeit [[x]*y for x,y in izip(a,b)]
1 loops, best of 3: 98.8 ms per loop
>>> %timeit map(lambda v: [v[0]]*v[1], zip(a,b))
1 loops, best of 3: 114 ms per loop
>>> %timeit map(list, map(repeat, a, b))
1 loops, best of 3: 192 ms per loop
>>> %timeit map(list, imap(repeat, a, b))
1 loops, best of 3: 211 ms per loop
>>> %timeit map(mul, [[x] for x in a], b)
1 loops, best of 3: 107 ms per loop
>>> %timeit [[x for _ in xrange(y)] for x,y in zip(a,b)]
1 loops, best of 3: 645 ms per loop
>>> %timeit [[x for _ in xrange(y)] for x,y in izip(a,b)]
1 loops, best of 3: 680 ms per loop
Ответ 2
Самый быстрый способ сделать это с помощью map() и operator.mul():
>>> from operator import mul
>>> map(mul, [['x'], ['y'], ['z']], [1, 2, 3])
[['x'], ['y', 'y'], ['z', 'z', 'z']]
Ответ 3
>>> from itertools import repeat
>>> from itertools import starmap
>>> a = ['x','y','z']
>>> b = [1,2,3]
>>> starmap(repeat,zip(a,b))
starmap
возвращает итерабельность, которая содержит значения, равные результату вызова repeat
, с аргументами, равными значениям, содержащимся в кортеже, в данном случае, например, ('x',1)
.
>>> for p in starmap(repeat,zip(a,b)):
print(list(p))
['x']
['y', 'y']
['z', 'z', 'z']
Ответ 4
@kirelagin предложила версию без циклов for
, здесь, которая также не имеет lambda
(помните, что решение от @AshwiniChaudhary наиболее читаемо)
>>> from itertools import repeat
>>> a = ['x','y','z']
>>> b = [1,2,3]
>>> map(list, map(repeat, a, b))
[['x'], ['y', 'y'], ['z', 'z', 'z']]
>>> map(repeat, a, b)
[repeat('x', 1), repeat('y', 2), repeat('z', 3)]
создает список объектов repeat
(используйте imap
на Python 2.x, если вам нужен ленивый итератор вместо списка), которые не занимают лишнего места в памяти, это здорово, если вы просто хотите перебирать элементы вместо их хранения)
Ответ 5
Вот версия без for
циклов, если вам почему-то не нравятся:
map(lambda v: [v[0]]*v[1], zip(a,b))
Я также должен предупредить вас, что эта версия немного медленнее, чем понимание списка:
$ a = ['hi']*100
$ b = [20]*100
$ %timeit map(lambda v: [v[0]]*v[1], zip(a,b))
10000 loops, best of 3: 101 us per loop
%timeit [[x]*y for x,y in zip(a,b)]
10000 loops, best of 3: 74.1 us per loop
Я бы также рекомендовал использовать itertools.izip
вместо zip
, если вы находитесь на Python 2.