Python多维列表..如何获取一个维度?

13

我的问题是,如果我有一个如下所示的列表:

someList = [[0,1,2],[3,4,5],[6,7,8]]
如何获取每个子列表的第一个条目?
我知道可以这样做:
newList = []
for entry in someList:
    newList.append(entry[0])

新列表将是:

[0, 3, 6]

但是有没有一种方法可以做到像这样:

newList = someList[:][0] 

编辑:

效率非常关键。我实际上正在处理一个拥有超过300000个条目的列表。

3个回答

16

编辑:这里有一些实际的数字!使用izip、列表推导式和numpy来做这件事的速度都差不多。

# zip
>>> timeit.timeit( "newlist = zip(*someList)[0]", setup = "someList = [range(1000000), range(1000000), range(1000000)]", number = 10 )
1.4984046398561759

# izip
>>> timeit.timeit( "newlist = izip(*someList).next()", setup = "someList = range(1000000), range(1000000), range(1000000)]; from itertools import izip", number = 10 )
2.2186223645803693e-05

# list comprehension
>>> timeit.timeit( "newlist = [li[0] for li in someList]", setup = "someList = [range(1000000), range(1000000), range(1000000)]", number = 10 )
1.4677040212518477e-05

# numpy
>>> timeit.timeit( "newlist = someList[0,:]", setup = "import numpy as np; someList = np.array([range(1000000), range(1000000), range(1000000)])", number = 10 )
6.6217344397045963e-05
>>>

对于这样的大型数据结构,您应该使用numpy,它在C中实现了数组类型,因此效率显著提高。它还提供了您可能需要的所有矩阵操作。

>>> import numpy as np
>>> foo = np.array([[0,1,2],[3,4,5],[6,7,8]])
>>> foo[:,0]
array([0, 3, 6])

你也可以使用

transpose()

函数,将二维数组的行和列互换。

>>> foo.transpose()
array([[0, 3, 6],
       [1, 4, 7],
       [2, 5, 8]])

...处理 n 维数组的方法...

>>> foo = np.zeros((3,3,3))
>>> foo
array([[[ 0.,  0.,  0.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.]],

       [[ 0.,  0.,  0.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.]],

       [[ 0.,  0.,  0.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.]]])
>>> foo[0,...]
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

..进行高效的线性代数计算...

>>> foo = no.ones((3,3))
>>> np.linalg.qr(foo)
(array([[-0.57735027,  0.81649658,  0.        ],
       [-0.57735027, -0.40824829, -0.70710678],
       [-0.57735027, -0.40824829,  0.70710678]]), array([[ -1.73205081e+00,  -1.
73205081e+00,  -1.73205081e+00],
       [  0.00000000e+00,  -1.57009246e-16,  -1.57009246e-16],
       [  0.00000000e+00,   0.00000000e+00,   0.00000000e+00]]))

...并且基本上可以做Matlab能做的任何事情


1
@Richard:这取决于……列表越大,izip和列表推导式的解决方案对numpy的性能表现越好,因为它们不考虑矩阵条目的绝大部分。 - Johannes Charra
2
@jellybean:我怀疑……NumPy真的很优化。我认为可以放心地说,简单的按列切片不会读取整个矩阵。(虽然我可能错了,我不知道C实现细节。)事实上,我认为numpy在较大的列表上执行会更好,因为Python本机list类型的开销开始累加。但是当然,只有一种方法可以找出答案! - Katriel
1
@katrielalex:基本上,我认为你是对的。我刚刚尝试了一个例子(请参见我的答案),这对于izip解决方案来说非常幸运。 - Johannes Charra
1
@jellybean:你把所有的设置(导入模块和定义列表)都放在计时器里面,这会导致糟糕的结果。特别是,导入numpy需要一两秒钟的时间(它很__大__!),而你已经将其包含在计时中了。请参见上文。 - Katriel
1
@katrielalex:当然,但这不公平吗?如果真的只是关于那个给出第一个索引的操作,你将不得不在时间分析中包括导入。但我不想在这里质疑numpy的优越性... OP可能想对列表进行更多操作,因此长期来看导入是值得的。 - Johannes Charra
显示剩余4条评论

10

使用列表推导式是最佳选择:

[sublist[0] for sublist in someList]

由于效率是一个重要问题,这种方法要比使用 zip 的方式更快。根据你对结果的处理方式,你可能还可以通过使用生成器表达式方法获得更高的效率:

(sublist[0] for sublist in someList)
请注意,尽管它返回的是生成器而不是列表,因此无法进行索引。

8
zip(*someList)[0]

编辑:

针对recursive的评论,也可以使用以下方法:

from itertools import izip
izip(*someList).next()

为了获得更好的性能。
一些时间分析:
python -m timeit "someList = [range(1000000), range(1000000), range(1000000)]; newlist = zip(*someList)[0]"
10 loops, best of 3: 498 msec per loop
python -m timeit "someList = [range(1000000), range(1000000), range(1000000)]; from itertools import izip; newlist = izip(*someList).next()"
10 loops, best of 3: 111 msec per loop
python -m timeit "someList = [range(1000000), range(1000000), range(1000000)]; newlist = [li[0] for li in someList]"
10 loops, best of 3: 110 msec per loop

所以izip和列表推导在同一级别。

当你需要一个非0的索引时,列表推导更加灵活,而且更加明确。

编辑2:

即使numpy解决方案也不如此快(但我可能选择了一个不代表性的示例):

python -m timeit "import numpy as np; someList = np.array([range(1000000), range(1000000), range(1000000)]); newList = someList[:,0]"
10 loops, best of 3: 551 msec per loop

如果someList很大,这将会做很多不必要的工作,因为它还会合并所有其他列。 - recursive

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