Создание списка отдельных элементов списка, умноженных 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.