创建将各个列表项乘以n倍的个别列表项列表

11

我对Python还比较新,认为这应该是一个相当常见的问题,但找不到解决方案。我已经看过这个页面,并且在单个项目上发现了它很有帮助,但我无法扩展示例以涉及多个项目而不使用'for'循环。我正在通过Emcee运行250个行走者的这段代码,所以我正在寻找最快的方式。

我有一个数字列表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逐个元素相乘,但没有成功。

5个回答

10
你可以使用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

1
我建议提到 itertools.izip - kirelagin
1
谢谢!但是 'for' 循环会使代码变慢吗? - user2444731
3
由于用户是新手,指出[x] * y会创建y个对x的引用,这在某些情况下可能会带来问题,因为如果一个引用被更改,所有引用都可能被更改。 - Nuclearman
正如 Nuclearman 所说,我是新手 - 我被告知 for 循环本质上很慢,并且在 MCMC 中最好避免使用 - 仍然盲目地听从任何我听到的建议 :s - user2444731
1
你可能应该在提问时说明这是蒙特卡罗算法。上下文会有帮助。 - Nuclearman
显示剩余4条评论

4

最快的方法是使用 map()operator.mul():

>>> from operator import mul
>>> map(mul, [['x'], ['y'], ['z']], [1, 2, 3])
[['x'], ['y', 'y'], ['z', 'z', 'z']]

3
+1 当你发布这篇文章的时候,我才明白它的含义(输入并不是一个嵌套列表)。 - jamylak

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']

请注意,在starmap上调用map(list, ...)以获取列表的列表。 - jamylak

2

@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对象列表(如果你想要一个懒惰的迭代器而不是一个列表,请在Python 2.x上使用imap),这些对象不会占用额外的内存空间,如果你只是想遍历项目而不是存储它们,那么这些对象非常实用。


1

如果你不喜欢使用for循环,这是一个没有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

如果你使用的是Python 2,我建议使用itertools.izip而不是zip


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接