【问题标题】:Finding the index of elements based on a condition using python list comprehension使用python列表理解根据条件查找元素的索引
【发布时间】:2011-11-08 09:04:05
【问题描述】:

以下 Python 代码在来自 Matlab 背景时显得很冗长

>>> a = [1, 2, 3, 1, 2, 3]
>>> [index for index,value in enumerate(a) if value > 2]
[2, 5]

在 Matlab 中我可以写:

>> a = [1, 2, 3, 1, 2, 3];
>> find(a>2)
ans =
     3     6

有没有用 Python 编写的简写方法,还是我只使用长版本?


感谢您对 Python 语法原理的所有建议和解释。

在numpy网站上找到以下内容后,我想我找到了一个我喜欢的解决方案:

http://docs.scipy.org/doc/numpy/user/basics.indexing.html#boolean-or-mask-index-arrays

将来自该网站的信息应用于我上面的问题,将给出以下结果:

>>> from numpy import array
>>> a = array([1, 2, 3, 1, 2, 3])
>>> b = a>2 
array([False, False, True, False, False, True], dtype=bool)
>>> r = array(range(len(b)))
>>> r(b)
[2, 5]

以下应该可以工作(但我手头没有 Python 解释器来测试它):

class my_array(numpy.array):
    def find(self, b):
        r = array(range(len(b)))
        return r(b)


>>> a = my_array([1, 2, 3, 1, 2, 3])
>>> a.find(a>2)
[2, 5]

【问题讨论】:

  • [idx for idx in range(len(a)) if a[idx] > 2] 怎么样?在 Python 中这样做有点尴尬的原因是它不像其他语言那样使用索引。

标签: python


【解决方案1】:

对我来说效果很好:

>>> import numpy as np
>>> a = np.array([1, 2, 3, 1, 2, 3])
>>> np.where(a > 2)[0]
[2 5]

【讨论】:

    【解决方案2】:

    即使是迟到的答案:我认为这仍然是一个非常好的问题,恕我直言 Python(没有像 numpy 这样的额外库或工具包)仍然缺乏根据手动定义的过滤器访问列表元素索引的便捷方法.

    您可以手动定义一个提供该功能的函数:

    def indices(list, filtr=lambda x: bool(x)):
        return [i for i,x in enumerate(list) if filtr(x)]
    
    print(indices([1,0,3,5,1], lambda x: x==1))
    

    产量:[0, 4]

    在我的想象中,完美的方法是创建一个列表子类并将索引函数添加为类方法。这样就只需要 filter 方法了:

    class MyList(list):
        def __init__(self, *args):
            list.__init__(self, *args)
        def indices(self, filtr=lambda x: bool(x)):
            return [i for i,x in enumerate(self) if filtr(x)]
    
    my_list = MyList([1,0,3,5,1])
    my_list.indices(lambda x: x==1)
    

    我在这里详细阐述了该主题: http://tinyurl.com/jajrr87

    【讨论】:

      【解决方案3】:

      也许另一个问题是,“一旦获得这些索引,您将如何处理它们?”如果您打算使用它们来创建另一个列表,那么在 Python 中,它们是不必要的中间步骤。如果您想要所有符合给定条件的值,只需使用内置过滤器:

      matchingVals = filter(lambda x : x>2, a)
      

      或者编写你自己的列表理解:

      matchingVals = [x for x in a if x > 2]
      

      如果你想从列表中删除它们,那么 Pythonic 方法不一定是从列表中删除,而是编写一个列表推导,就像你正在创建一个新列表一样,并使用 @987654324 原地分配@在左侧:

      a[:] = [x for x in a if x <= 2]
      

      Matlab 提供 find 是因为其以数组为中心的模型通过使用数组索引选择项目来工作。当然,您可以在 Python 中执行此操作,但更 Python 的方式是使用迭代器和生成器,正如 @EliBendersky 已经提到的那样。

      【讨论】:

      • Paul,我还没有在脚本/函数/类中遇到这个需求。它更多地用于对我正在编写的课程进行交互式测试。
      • @Mike - 感谢您的编辑,但我的意思是 a[:] = ... - 请参阅 Alex Martelli 对此问题的回答 stackoverflow.com/questions/1352885/…
      • @Paul,我假设(并希望!)您的描述并不是真的要“创建一个新列表”;我发现当程序非常谨慎地改变现有数据时,它们往往更容易理解和维护。无论如何,我很抱歉越界了——您当然应该能够将您的帖子编辑回您想要的任何内容。
      【解决方案4】:

      另一种方式:

      >>> [i for i in range(len(a)) if a[i] > 2]
      [2, 5]
      

      一般来说,请记住虽然find 是一个现成的函数,但列表推导是一个通用的,因此非常强大的解决方案。没有什么能阻止您在 Python 中编写 find 函数并在以后根据需要使用它。即:

      >>> def find_indices(lst, condition):
      ...   return [i for i, elem in enumerate(lst) if condition(elem)]
      ... 
      >>> find_indices(a, lambda e: e > 2)
      [2, 5]
      

      请注意,我在这里使用列表来模仿 Matlab。使用生成器和迭代器会更加 Pythonic。

      【讨论】:

      • OP 本可以将其写成[i for i,v in enumerate(a) if v &gt; 2]
      • 不是更短,而是更长。将index替换为i,将value替换为v并计算字符数。
      • @NullUser, agf: 你是对的,但重点是第二部分:)
      • range(len(...)) 上使用enumerate 更加健壮和高效。
      • @Mike Graham:我同意 - 将 find_indices 函数更改为使用 enumerate
      【解决方案5】:
      • 在 Python 中,您根本不会为此使用索引,而只需处理值 —[value for value in a if value &gt; 2]。通常处理索引意味着你没有做最好的事情。

      • 如果您确实需要类似于 Matlab 的 API,您可以使用 numpy,这是一个 Python 中的多维数组和数值数学包,它深受 Matlab 的启发。您将使用 numpy 数组而不是列表。

        >>> import numpy
        >>> a = numpy.array([1, 2, 3, 1, 2, 3])
        >>> a
        array([1, 2, 3, 1, 2, 3])
        >>> numpy.where(a > 2)
        (array([2, 5]),)
        >>> a > 2
        array([False, False,  True, False, False,  True], dtype=bool)
        >>> a[numpy.where(a > 2)]
        array([3, 3])
        >>> a[a > 2]
        array([3, 3])
        

      【讨论】:

      • 您有列表,一个用于范围,一个用于角度,您想过滤掉高于某个阈值的范围值。您如何以“最佳方式”的方式过滤与这些范围相对应的角度?
      • filtered_ranges_and_angles = [(range, angle) for range, angle in zip(ranges, angles) if should_be_kept(range)]
      • “在 Python 中,你根本不会使用索引,而只是处理值”这句话表明你没有做足够的数据分析和机器学习建模。一个张量基于特定条件的索引用于过滤另一个张量。
      猜你喜欢
      • 2021-06-25
      • 1970-01-01
      • 2023-03-05
      • 2018-04-20
      • 2017-12-12
      • 1970-01-01
      • 2020-12-05
      • 2015-03-29
      相关资源
      最近更新 更多