将“元组列表”转换成一个平面列表或矩阵

112

使用Sqlite,select .. from命令返回结果output,然后打印出来:

>>print output
[(12.2817, 12.2817), (0, 0), (8.52, 8.52)]

看起来是一个元组列表。我想要将 output 转换成简单列表:

[12.2817, 12.2817, 0, 0, 8.52, 8.52]

或一个 2x3 矩阵:

12.2817 12.2817
0          0 
8.52     8.52

需要通过output[i][j]进行读取。

对于第一种选项,flatten命令无法完成任务,对于第二种选项我也不知道该怎么做...

希望能提供一个快速的解决方案,因为实际数据要大得多。


2
[(12.2817, 12.2817), (0, 0), (8.52, 8.52)] 已经是一个3x2的矩阵了吗?还是我漏掉了什么? - mouad
请参考这个问题 - Joel Cornett
1
对于展平函数,请检查itertools模块的示例,已经有一个展平函数示例:http://docs.python.org/library/itertools.html#recipes - mouad
4
[item for sublist in output for item in sublist] 可以完美地工作,并且它的优点是你的内部元组也可以是列表; 更一般地,任何内部和外部可迭代对象的组合都可以工作。 - Kyss Tao
11个回答

156

目前发布的最快(且最短)解决方案:

list(sum(output, ()))

比使用 itertools 更快约50%,比使用map更快约70%。


12
@Joel,不错,但我想知道它是如何工作的? list(output[0]+output[1]+output[2]) 给出了期望的结果,但 list(sum(output)) 却没有。为什么?这个 () 有什么“魔力”? - Kyss Tao
10
好的,我应该读一下说明书g。看起来sum(sequence[, start]):如果没有指定start,则默认从0开始相加,而不是从sequence[0]开始相加然后再加上其余的元素。抱歉打扰你了。 - Kyss Tao
4
这是一个众所周知的反模式:不要使用sum函数来连接序列,这将导致一个二次时间复杂度算法。实际上,如果你尝试在字符串上这样做,sum函数会抱怨! - juanpa.arrivillaga
@juanpa.arrivillaga:同意。这种情况下可取的用例非常少。 - Joel Cornett
18
是的,速度很快,但完全晦涩难懂。你需要留下一条评论来说明它实际上在做什么 :( - CpILL
在我看来,这种技术相对于其他更快、更符合Python风格的技术来说,效率较低。 - Gman

73

使用列表推导式的方法适用于可迭代类型,并且比这里展示的其他方法更快。

flattened = [item for sublist in l for item in sublist]

l 是要展开的列表(在原帖中称为output


timeit 测试:

l = list(zip(range(99), range(99)))  # list of tuples to flatten

列表推导式

[item for sublist in l for item in sublist]

timeit 结果为 7.67 微秒 ± 129 纳秒每次循环

列表 extend() 方法

flattened = []
list(flattened.extend(item) for item in l)

timeit 结果为 11 微秒 ± 433 纳秒每次循环

sum()

list(sum(l, ()))

timeit 结果 = 24.2 微秒 ± 269 纳秒每次循环


2
我必须在一个大数据集上使用它,列表推导式方法是迄今为止最快的! - nbeuchat
我对.extend解决方案进行了一点改动,现在性能稍微有所提升。你可以用timeit检查一下进行比较。 - Totoro
这很令人困惑,我根本不理解这里的语法。列表推导的一般语法是expression for item in list,例如x*2 for x in listONumbers。因此,对于展平,您期望会出现像num for num in sublist for sublist in list这样的表达式,而不是num for sublist in list for num in sublist。那么这个推导式是如何分解的? - Cheruvim

31
在Python 2.7以及所有版本的Python3中,您可以使用itertools.chain来展平可迭代对象的列表。可通过*语法或类方法实现。
>>> t = [ (1,2), (3,4), (5,6) ]
>>> t
[(1, 2), (3, 4), (5, 6)]
>>> import itertools
>>> list(itertools.chain(*t))
[1, 2, 3, 4, 5, 6]
>>> list(itertools.chain.from_iterable(t))
[1, 2, 3, 4, 5, 6]

16

更新:使用extend进行扁平化,但不使用列表解析式,也不使用列表作为迭代器(最快)。

在检查了下一个提供了使用dual for的列表解析式的更快解决方案后,我做了一点调整,现在它执行得更好了,首先list(...)的执行时间占用了很大的比例,然后将列表解析式改为简单的循环又削减了一些时间。

新的解决方案是:

l = []
for row in output: l.extend(row)

[]替换list的旧方法(速度稍慢但差别不大):

[l.extend(row) for row in output]

旧版(较慢):

使用列表推导式进行扁平化处理

l = []
list(l.extend(row) for row in output)

有时候,只需删除 list(...) 中的括号并使用 [...], 就能获得新的扩展和改进:

import timeit
t = timeit.timeit
o = "output=list(zip(range(1000000000), range(10000000))); l=[]"
steps_ext = "for row in output: l.extend(row)"
steps_ext_old = "list(l.extend(row) for row in output)"
steps_ext_remove_list = "[l.extend(row) for row in output]"
steps_com = "[item for sublist in output for item in sublist]"

print(f"{steps_ext}\n>>>{t(steps_ext, setup=o, number=10)}")
print(f"{steps_ext_remove_list}\n>>>{t(steps_ext_remove_list, setup=o, number=10)}")
print(f"{steps_com}\n>>>{t(steps_com, setup=o, number=10)}")
print(f"{steps_ext_old}\n>>>{t(steps_ext_old, setup=o, number=10)}")

时间测试结果:

for row in output: l.extend(row)                  
>>> 7.022608777000187

[l.extend(row) for row in output]
>>> 9.155910597999991

[item for sublist in output for item in sublist]
>>> 9.920002304000036

list(l.extend(row) for row in output)
>>> 10.703829122000116

9
>>> flat_list = []
>>> nested_list = [(1, 2, 4), (0, 9)]
>>> for a_tuple in nested_list:
...     flat_list.extend(list(a_tuple))
... 
>>> flat_list
[1, 2, 4, 0, 9]
>>> 

您可以按照上述示例,将元组列表转换为单个列表。

9

使用 itertools 的链式操作:

>>> import itertools
>>> list(itertools.chain.from_iterable([(12.2817, 12.2817), (0, 0), (8.52, 8.52)]))
[12.2817, 12.2817, 0, 0, 8.52, 8.52]

7
或者你可以将列表展开,像这样:
reduce(lambda x,y:x+y, map(list, output))

reduce(lambda x,y:x+y, output) 看起来可以直接将其转换为一个长元组(可以转换为列表)。为什么在 reduce() 调用内部使用 map(list, output)?也许这更符合元组是不可变的,而列表是可变的事实。 - Paul Rougieux

5
这正是numpy的用武之地,无论从数据结构还是速度角度来看。
import numpy as np

output = [(12.2817, 12.2817), (0, 0), (8.52, 8.52)]
output_ary = np.array(output)   # this is your matrix 
output_vec = output_ary.ravel() # this is your 1d-array

3
def flatten_tuple_list(tuples):
    return list(sum(tuples, ()))


tuples = [(5, 6), (6, 7, 8, 9), (3,)]
print(flatten_tuple_list(tuples))

5
感谢您提供答案。是否方便您编辑一下答案并解释一下代码?这样可以帮助未来的读者更好地理解代码内容,尤其是那些新手可能会感到困惑的概念。在这里,您的答案要与其他九个答案竞争,因此解释代码将显得尤为重要。请说明您的答案有何特别之处?它与上面已经存在的优秀答案相比,何时更适合使用? - Jeremy Caney
1
好的,我会这样做。 - SATYAM TRIPATHI

3

如果存在任意嵌套的列表(以防万一):

def flatten(lst):
    result = []
    for element in lst: 
        if hasattr(element, '__iter__'):
            result.extend(flatten(element))
        else:
            result.append(element)
    return result

>>> flatten(output)
[12.2817, 12.2817, 0, 0, 8.52, 8.52]

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