Break python list into multiple lists, shuffle each lists separately
假设我有按日期排序的帖子。
1
| [<Post: 6>, <Post: 5>, <Post: 4>, <Post: 3>, <Post: 2>, <Post: 1>] |
我想把它们分成3组,并相应地对列表中的项目进行无序排列。
1
| chunks = [posts[x:x+2] for x in xrange(0, len(posts), 2)] |
现在块将返回:
1
| [[<Post: 6>, <Post: 5>], [<Post: 4>, <Post: 3>], [<Post: 2>, <Post: 1>]] |
在每个列表中随机排列这些项目的有效方法是什么?我可以考虑遍历它们,创建每个列表,但这似乎是重复的…
我希望最终输出看起来像:
1
| [[<Post: 5>, <Post: 6>], [<Post: 4>, <Post: 3>], [<Post: 1>, <Post: 2>]] |
或更好:
1
| [<Post: 5>, <Post: 6>, <Post: 4>, <Post: 3>, <Post: 1>, <Post: 2>] |
当然。random.shuffle工作到位,因此循环遍历列表元素并将其应用到它们上面就完成了第一项工作。对于"扁平化",我使用了我最喜欢的技巧:在子列表上应用sum,并将start元素作为空列表。
1 2 3 4 5 6 7 8 9 10 11 12 13
| import random,itertools
chunks = [["Post: 6","Post: 5"], ["Post: 4","Post: 3"], ["Post: 2","Post: 1"]]
# shuffle
for c in chunks: random.shuffle(c)
# there you already have your list of lists with shuffled sub-lists
# now the flattening
print(sum(chunks,[])) # or (more complex but faster below)
print(list(itertools.chain(*chunks))) # faster than sum on big lists |
一些结果:
1 2
| ['Post: 5', 'Post: 6', 'Post: 4', 'Post: 3', 'Post: 2', 'Post: 1']
['Post: 6', 'Post: 5', 'Post: 3', 'Post: 4', 'Post: 1', 'Post: 2'] |
(你说你想要一个像[[, , , , , ]]这样的列表,但我认为这是一个错误:我提供了一个简单、扁平的列表。
- 你是对的,这是一个打字错误的解决方案。谢谢!
- sum并不是真正用于连接序列;它将在每个阶段创建全新的序列,这意味着大量的复制和临时性。如果要有效地连接序列,请使用itertools.chain生成一个平坦一级的生成器,并将其包装在list中(从该生成器构建新的list,而不使用临时的list)。先是from itertools import chain,然后是print(list(chain(*chunks)))。这是重复字符串连接和使用''.join之间的区别。
- @护林员:谢谢。我真的要潜入江户记1〔8〕。你说得对,尽管列表增长性能比字符串增长要好得多。列表设计为可调整大小。例如,sum被保护以避免组成字符串:如果你尝试sum("abcdef","")你会得到TypeError: sum() can't sum strings [use ''.join(seq) instead]。
- @shadowranger:当你使用ecx1(12)时,因为你必须迭代元素,这不是同一个问题吗?我的意思是:"".join("some string")知道字符串的长度,但由于chain是一个生成器函数,list不可能知道大小,最后一个接一个地添加元素,这可能比执行sum2×2更糟,就像我的答案(有趣的聊天)中那样。!)
- @Jean-Fran和Oisfabre:确实,chain不允许list像''.join那样预先分配。但是,list使用过度分配策略来最小化reallocs(对于每个添加的元素,它的摊销O(1),并且新的构造/扩展假定至少可以添加8个元素);如果我正确阅读代码,对于10个元素chain,它执行一个realloc,对于100个元素,9个realloc,对于1000个元素,25个reallocs。分配器可能每次都不需要副本。
- @在小名单上执行sum时,是否不能应用重新分配策略?
- 这些都不需要重新分配(并为GC设置)list对象本身,只需要它的存储。相比之下,重复sum的重复连接等价于+,而不是+=,这是paint算法的一种形式。每个串联分配一个新的list,调整其大小以匹配合并的list,复制(递增引用计数)原始list中的所有元素,然后(在本例中)放弃以前的临时list(执行所需的所有递减操作)。比例太大了。
- 为了进行比较,如果您手头有ipython,请尝试这个简单的测试用例:x = [[1, 2]] * 100。然后试试%timeit sum(x, [])、%timeit functools.reduce(operator.iconcat, x, [])和%timeit list(itertools.chain.from_iterable(x))(from_iterable比chain(*x)快一点,但在这种情况下它是小的,因为x是一个实现的list而不是发电机。reduce/iconcat获胜(虽然灵活性较低,但你不能选择不加list化的迭代),list/chain.from_iterable或chain(*)刚好落后,落后(5-6x更长)的是sum。现在执行x *= 10以查看缩放。
- 如果在使x长10倍之后重复测试,sum比已经失去的速度长70倍,而其他的只需要8-10倍(list过度分配策略随着输出大小的增加,realloc的数量越来越少,因此实际上它只是线性缩放的)。
- 我相信你。仅仅是list1 += list2和list1 = list1+list2让我信服。我已经编辑了我的文章来提供这两种方法。谢谢你的教训。
- @Jean-Fran&231;Oisfabre:是的,我有时会有点过分详细。很高兴这次对某人有用。:-)顺便说一句,我注意到他们实际上评论了你在sum源中对sum的具体使用,解释了为什么他们必须使用+而不是+=行为(以避免会改变其他地方引用的初始值的副作用),以防你感兴趣。
- 我很感兴趣。谢谢你的链接。我将编辑我的其他帖子,在这些帖子中我将在列表中推广SUM:)
- @我认为你应该对这个讨论感兴趣。希望江户一〔二〕能治百病。