Python:根据索引集从列表中选择子集


98

我有几个具有相同数量条目的列表(每个列表都指定一个对象属性):

property_a = [545., 656., 5.4, 33.]
property_b = [ 1.2,  1.3, 2.3, 0.3]
...

并列出具有相同长度的标志

good_objects = [True, False, False, True]

(可以很容易地用等效的索引列表代替:

good_indices = [0, 3]

生成仅包含由条目或索引指示的值的新列表property_aselproperty_bsel... 的最简单方法是什么True

property_asel = [545., 33.]
property_bsel = [ 1.2, 0.3]

Answers:


126

您可以只使用列表推导

property_asel = [val for is_good, val in zip(good_objects, property_a) if is_good]

要么

property_asel = [property_a[i] for i in good_indices]

后者要快一些,因为它good_indices的长度小于的长度property_a,假设good_indices它们是预先计算的,而不是即时生成的。


编辑:第一个选项等效于itertools.compressPython 2.7 / 3.1之后的版本。请参阅@Gary Kerr的答案。

property_asel = list(itertools.compress(property_a, good_objects))

1
@fuen:是的。在Python 2上引起很多(使用itertools.izip代替),而在Python 3上引起的却不那么多。这是因为zip在Python 2中将创建一个新列表,但是在Python 3上它将仅返回一个(惰性)生成器。
kennytm 2010年

好的,那么我应该坚持您的第二个建议,因为这构成了我代码的中心部分。
fuenfundachtzig,2010年

4
@ 85:为什么要担心性能?写下您必须执行的操作(如果速度很慢),然后进行测试以查找瓶颈。
加里·克尔

1
@PreludeAndFugue:如果有两个等效的选项,最好知道哪个更快,并立即使用该选项。
fuenfundachtzig,2010年

1
您可以from itertools import izip使用它,而不必zip在第一个示例中使用它。这就产生了一个迭代器,一样的Python 3
克里斯B.

28

我看到2个选项。

  1. 使用numpy:

    property_a = numpy.array([545., 656., 5.4, 33.])
    property_b = numpy.array([ 1.2,  1.3, 2.3, 0.3])
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = property_a[good_objects]
    property_bsel = property_b[good_indices]
  2. 使用列表理解并将其压缩:

    property_a = [545., 656., 5.4, 33.]
    property_b = [ 1.2,  1.3, 2.3, 0.3]
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = [x for x, y in zip(property_a, good_objects) if y]
    property_bsel = [property_b[i] for i in good_indices]

2
建议使用Numpy,因为OP似乎希望将数字存储在列表中。二维数组会更好。
菲利普

这也是一个很好的建议,因为这对于R的用户来说是非常熟悉的语法,R的这种选择非常强大,尤其是在嵌套和/或多维时。
托马斯·布朗

[property_b[i] for i in good_indices]是不使用时的好选择numpy
Ilya Rusin

16

使用内置的功能zip

property_asel = [a for (a, truth) in zip(property_a, good_objects) if truth]

编辑

只看2.7的新功能。itertools模块中现在有一个函数,与上面的代码类似。

http://docs.python.org/library/itertools.html#itertools.compress

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F

1
我对itertools.compress这里的使用感到不知所措。该列表理解是远远更具可读性,而不必挖到底什么敷在做什么。
PaulMcG 2010年

5
嗯,我发现使用compress的代码更具可读性:)也许我有偏见,因为它确实满足我的要求。
fuenfundachtzig,2010年

8

假设您只有项目列表和真实/必需索引列表,那么这应该是最快的:

property_asel = [ property_a[index] for index in good_indices ]

这意味着属性选择将只进行与真实/必需索引一样多的回合。如果您有很多遵循单个标签(真/假)列表规则的属性列表,则可以使用相同的列表理解原则创建索引列表:

good_indices = [ index for index, item in enumerate(good_objects) if item ]

这会遍历good_objects中的每个项目(同时使用枚举记住其索引),并且仅返回该项目为true的索引。


对于没有理解列表的人,这是英文散文版本,其代码以粗体突出显示:

列出每个索引组的索引,索引存在好对象枚举中,如果(其中)该项为True


0

Matlab和Scilab语言为您提出的问题提供了比Python更简单,更优雅的语法,因此,我认为最好的方法是使用Python中的Numpy包来模仿Matlab / Scilab。通过这样做,您的问题的解决方案非常简洁,优雅:

from numpy import *
property_a = array([545., 656., 5.4, 33.])
property_b = array([ 1.2,  1.3, 2.3, 0.3])
good_objects = [True, False, False, True]
good_indices = [0, 3]
property_asel = property_a[good_objects]
property_bsel = property_b[good_indices]

Numpy试图模仿Matlab / Scilab,但这要付出一定的代价:您需要用关键字“ array”声明每个列表,这会使脚本过载(Matlab / Scilab不存在此问题)。请注意,此解决方案仅限于数字数组,在您的示例中就是这种情况。


3
他在问题中无处提及NumPy,无需表达您对NumPy对Matlab的看法。Python列表是一样的东西NumPy的数组,即使它们都大致相当于载体。(Python列表就像Matlab单元数组一样-每个元素可以具有不同的数据类型。为了实现某些优化,对NumPy数组的限制更多)。您可以通过Python的内置filter库或外部库获得与示例相似的语法pandas。如果您要交换语言,也可以尝试使用R,但这不是问题所在
Livius 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.