【问题标题】:Pythonic way to find maximum value and its index in a list?在列表中查找最大值及其索引的 Pythonic 方法?
【发布时间】:2011-09-05 19:34:08
【问题描述】:

如果我想要一个列表中的最大值,我可以写max(List),但是如果我还需要最大值的索引呢?

我可以这样写:

maximum=0
for i,value in enumerate(List):
    if value>maximum:
        maximum=value
        index=i

但这对我来说看起来很乏味。

如果我写:

List.index(max(List))

然后它会迭代列表两次。

有没有更好的办法?

【问题讨论】:

  • “它将通过列表两次”是什么意思? List.index(max(List)) 对我有用。
  • @mwc:它将迭代列表一次以确定最大值,然后再次迭代它以找到该值的索引。
  • 如果有重复的最大值,list.index() 会不会有问题?
  • @LoganYang 是的,可能有两个项目具有相同的值。
  • 如果顺序不重要,你可以做类似 List.sort()[-1]

标签: python


【解决方案1】:

我认为接受的答案很好,但你为什么不明确地做呢?我觉得更多人会理解你的代码,这与 PEP 8 一致:

max_value = max(my_list)
max_index = my_list.index(max_value)

这种方法也比公认的答案快三倍左右:

import random
from datetime import datetime
import operator

def explicit(l):
    max_val = max(l)
    max_idx = l.index(max_val)
    return max_idx, max_val

def implicit(l):
    max_idx, max_val = max(enumerate(l), key=operator.itemgetter(1))
    return max_idx, max_val

if __name__ == "__main__":
    from timeit import Timer
    t = Timer("explicit(l)", "from __main__ import explicit, implicit; "
          "import random; import operator;"
          "l = [random.random() for _ in xrange(100)]")
    print "Explicit: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)

    t = Timer("implicit(l)", "from __main__ import explicit, implicit; "
          "import random; import operator;"
          "l = [random.random() for _ in xrange(100)]")
    print "Implicit: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)

在我的电脑上运行的结果:

Explicit: 8.07 usec/pass
Implicit: 22.86 usec/pass

其他设置:

Explicit: 6.80 usec/pass
Implicit: 19.01 usec/pass

【讨论】:

  • 没想到会更快。即使我将 l 替换为“l = [random.random() for _ in xrange(10000000)]+[2]”,它也更快,这保证了最后一个元素是最大的。
  • @Sunny88:对于简单的数字列表,简单的方法更快。如果您在这种情况下追求性能,我建议使用numpy.argmax(),这在我的机器上又快了 30 倍。如果列表包含比数字更复杂的对象,我的答案中的方法会变得更快。该方法的另一个优点是它可以用于任意迭代器,而不仅仅是列表。
  • @Sven-Marnach 如果我必须先将列表转换为 numpy 数组,numpy 会更快吗?简单示例 [0,1,0] 会更快吗?
  • @Sven-Marnach 我刚刚检查过。 numpy.argmax 是迄今为止最慢的方法,如果数组包含字符串而不是浮点数或整数,它会给出错误的答案。
  • 如果有重复的最大值,list.index() 会不会有问题?
【解决方案2】:

有很多选项,例如:

import operator
index, value = max(enumerate(my_list), key=operator.itemgetter(1))

【讨论】:

  • 啊,我在其他地方看到过这个,但我认为它只会返回一个值,而不是一个元组。
  • @Sunny88:key 函数仅用于决定哪个元素最大。元素没有改变。
  • @SvenMarnach 为什么不用key=lambda e: e[1] 来避免导入?
  • @lifebalance 使用itemgetter() 更快,避免导入不是值得追求的目标。在某些情况下避免外部依赖可能是值得的,但从标准库导入是没有问题的。
【解决方案3】:

假设列表非常大,并且假设它已经是一个 np.array(),这个答案比 @Escualo 快 33 倍。我不得不减少测试运行的数量,因为测试正在查看 10000000 个元素而不仅仅是 100 个。

import random
from datetime import datetime
import operator
import numpy as np

def explicit(l):
    max_val = max(l)
    max_idx = l.index(max_val)
    return max_idx, max_val

def implicit(l):
    max_idx, max_val = max(enumerate(l), key=operator.itemgetter(1))
    return max_idx, max_val

def npmax(l):
    max_idx = np.argmax(l)
    max_val = l[max_idx]
    return (max_idx, max_val)

if __name__ == "__main__":
    from timeit import Timer

t = Timer("npmax(l)", "from __main__ import explicit, implicit, npmax; "
      "import random; import operator; import numpy as np;"
      "l = np.array([random.random() for _ in xrange(10000000)])")
print "Npmax: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

t = Timer("explicit(l)", "from __main__ import explicit, implicit; "
      "import random; import operator;"
      "l = [random.random() for _ in xrange(10000000)]")
print "Explicit: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

t = Timer("implicit(l)", "from __main__ import explicit, implicit; "
      "import random; import operator;"
      "l = [random.random() for _ in xrange(10000000)]")
print "Implicit: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

我的电脑上的结果:

Npmax: 8.78 msec/pass
Explicit: 290.01 msec/pass
Implicit: 790.27 msec/pass

【讨论】:

  • 只是为了澄清:加速只是由于 numpy C 实现与纯 python 相比?或者有一种方法可以使用纯 python 对@Escualo 的答案进行任何改进?
  • 如果想使用 python 3.6,可以执行以下操作:"l = np.array([random.random() for _ in range(10000000)])") print (f" Npmax: {(1000 * t.timeit(number=10)/10 ):5.2f} 毫秒/通过")
  • 这是在 2.7
  • 好吧,numpy.argmax 的速度看起来很惊人,除非你让它处理 标准 python 列表。然后速度介于显式和隐式版本之间。我猜np.array 不仅会创建一个列表,还会在其中保存一些额外的信息——例如最小值和最大值(只是一个假设)。
  • @MiroslawOpoka 我试图简要检查源代码,但没有找到任何这样的“预计算聚合”(尽管我不确定,因为代码不是那么简单,还有更多通用存储系统(例如数据库/存储格式)确实记录了此类最小/最大聚合)。不过,仅 numpy 的内存布局,加上 C 编译就可以带来显着的加速。
【解决方案4】:

使用 Python 的内置库,这很容易:

a = [2, 9, -10, 5, 18, 9] 
max(xrange(len(a)), key = lambda x: a[x])

这告诉max在列表[0, 1, 2, ..., len(a)]中找到最大的数字,使用自定义函数lambda x: a[x],它说0实际上是21实际上是9等.

【讨论】:

  • 在 Python 3 中,没有 xrange ,如果你想编写同时适用于 Python 2 和 Python 3 的代码,你应该使用 range()。
【解决方案5】:

我建议一个非常简单的方法:

import numpy as np
l = [10, 22, 8, 8, 11]
print(np.argmax(l))
print(np.argmin(l))

希望对你有帮助。

【讨论】:

    【解决方案6】:
    max([(v,i) for i,v in enumerate(my_list)])
    

    【讨论】:

    • 这更好,因为您可以调整它以与元组以外的东西一起使用。
    • 这究竟是如何工作的?你能分解一下这个过程吗?
    • 嗨@clabe45,它将my_list转换为元组列表(v,i),其中v是我列表中的每个项目,i是对应索引,然后它获取具有最大值的元组(以及相关的索引)
    • 谢谢,您可以在答案中发布吗? max 怎么知道在计算最大值时只考虑每个元组的第一项(v)?
    • @clabe45 可能这个回复来得太晚了,但对于现在遇到这个帖子的其他人(比如我)来说,这里:stackoverflow.com/questions/18296755/… 是一个解释。不是这一行:“默认情况下,max 将通过第一个索引比较项目,如果第一个索引相同,那么它将比较第二个索引。”所以我尝试了 list: l=[1,1,1] 然后 max([(v,i) for i,v in enumerate(l)]) 它给我的不是第一个,而是最后一个一:结果为(1,2)。我希望它能解释:)
    【解决方案7】:
    max([(value,index) for index,value in enumerate(your_list)]) #if maximum value is present more than once in your list then this will return index of the last occurrence
    

    如果最大值出现不止一次,并且您想获取所有索引,

    max_value = max(your_list)
    maxIndexList = [index for index,value in enumerate(your_list) if value==max(your_list)]
    

    【讨论】:

    • 是的。我几乎发布了一个答案,但后来我看到您已经在列表理解单行中使用相同的逻辑具有相同的解决方案。
    【解决方案8】:

    也许您仍然需要一个排序列表?

    试试这个:

    your_list = [13, 352, 2553, 0.5, 89, 0.4]
    sorted_list = sorted(your_list)
    index_of_higher_value = your_list.index(sorted_list[-1])
    

    【讨论】:

    • 1.排序具有更高的时间复杂度。 2.sorted_list没有索引而是值,所以它不起作用。
    【解决方案9】:

    我列出了一些大清单。一个是列表,一个是 numpy 数组。

    import numpy as np
    import random
    arrayv=np.random.randint(0,10,(100000000,1))
    listv=[]
    for i in range(0,100000000):
        listv.append(random.randint(0,9))
    

    使用 jupyter notebook 的 %%time 功能,我可以比较各种事物的速度。

    2 秒:

    %%time
    listv.index(max(listv))
    

    54.6 秒:

    %%time
    listv.index(max(arrayv))
    

    6.71 秒:

    %%time
    np.argmax(listv)
    

    103 毫秒:

    %%time
    np.argmax(arrayv)
    

    numpy 的数组非常快。

    【讨论】:

      【解决方案10】:

      列表理解方法:

      假设你有一些列表List = [5,2,3,8]

      那么[i for i in range(len(List)) if List[i] == max(List)] 将是一个pythonic 列表理解方法来查找值“i”,其中List[i] == max(List)

      对于作为列表列表的数组,它很容易扩展,只需执行一个 for 循环。

      例如,使用任意列表“数组”并将“索引”初始化为空列表。

      array = [[5, 0, 1, 1], 
      [1, 0, 1, 5], 
      [0, 1, 6, 0], 
      [0, 4, 3, 0], 
      [5, 2, 0, 0], 
      [5, 0, 1, 1], 
      [0, 6, 0, 1], 
      [0, 1, 0, 6]]
      index = []
      
      for List in array:
          index.append([i for i in range(len(List)) if List[i] == max(List)])
      index
      

      输出:[[0], [3], [2], [1], [0], [0], [1], [3]]

      【讨论】:

        【解决方案11】:

        以下是使用 Python 的内置函数对您的问题的完整解决方案:

        # Create the List
        numbers = input("Enter the elements of the list. Separate each value with a comma. Do not put a comma at the end.\n").split(",") 
        
        # Convert the elements in the list (treated as strings) to integers
        numberL = [int(element) for element in numbers] 
        
        # Loop through the list with a for-loop
        
        for elements in numberL:
            maxEle = max(numberL)
            indexMax = numberL.index(maxEle)
        
        print(maxEle)
        print(indexMax)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-10-24
          • 1970-01-01
          • 1970-01-01
          • 2012-01-10
          • 2012-07-16
          • 2021-03-22
          • 2020-02-20
          相关资源
          最近更新 更多