使用列表上的max()/ min()获取返回的最大或最小项目的索引


464

我正在使用列表中的Python maxmin函数来执行minimax算法,并且需要由max()或返回的值的索引min()。换句话说,我需要知道哪个移动产生了最大(第一玩家回合)或最小(第二玩家)值。

for i in range(9):
    newBoard = currentBoard.newBoardWithMove([i / 3, i % 3], player)

    if newBoard:
        temp = minMax(newBoard, depth + 1, not isMinLevel)  
        values.append(temp)

if isMinLevel:
    return min(values)
else:
    return max(values)

我需要能够返回最小值或最大值的实际索引,而不仅仅是返回值。


32
divmod存在内置函数可以避免[i / 3, i % 3]多说。
Mike Graham 2010年

Answers:


416
如果isMinLevel:
    返回values.index(min(values))
其他:
    返回values.index(max(values))

38
@KevinGriffin,请注意,这只会使您看到几次出现的最小/最大次数之一。这可能不是您想要的,例如,如果可以通过两种方式增加收益,但是其中一种会给另一位球员带来更大的伤害。我不知道这是否是您需要考虑的情况。
Mike Graham 2010年

89
@Kashyap实际上是O(N),而不是O(N ^ 2)。在min情况下,首先评估min(values),它是O(N),然后调用values.index(),它也是O(N)。O(N)+ O(N)= O(N)。index的参数仅计算一次。这等效于:tmp = min(values); return values.index(tmp)
Tom Karzes 2015年

@太多的php,当元素重复时该怎么办?
Shashi Tunga,

@ShashiTunga [list] .index()仅返回某事物的首次出现,不能保证它是唯一的,最小值在列表中可能并不唯一
Scott Anderson

470

假设您有一个list values = [3,6,1,5],并且需要最小元素的索引,即index_min = 2在这种情况下。

避免itemgetter()使用其他答案中提出的解决方案,而改用

index_min = min(range(len(values)), key=values.__getitem__)

因为它不需要import operator使用enumerate,也总是比使用解决方案更快(下面的基准)itemgetter()

如果您正在处理numpy数组或可以numpy作为依赖提供,请考虑同时使用

import numpy as np
index_min = np.argmin(values)

即使在以下情况下将其应用于纯Python列表,也将比第一个解决方案更快。

  • 它大于几个元素(我的机器上大约2 ** 4个元素)
  • 您可以提供从纯列表到numpy数组的内存副本

正如该基准所指出的: 在此处输入图片说明

我已经在我的机器上使用python 2.7运行了基准测试,用于上述两个解决方案(蓝色:纯python,第一个解决方案)(红色,numpy解决方案)以及基于的标准解决方案itemgetter()(黑色,参考解决方案)。与python 3.5相同的基准测试表明,这些方法与上述python 2.7情况完全相同


非常强的+1。我喜欢建议的解决方案的基准测试以及您总结的经验法则。正如我在下面的另一个答案中所建议的那样,您可以提供(或链接到)您的测试代码,以便其他人复制您的结果吗?机器和库会随着时间而变化,因此可以与其他解决方案进行比较。
Rakurai

3
我认为可能有一个错字:xrange。不应该是范围吗?
林赛·福勒

6
@LindsayFowler xrange()现在已经废弃,你可以使用range()
达维德

np.argmin不适用于浮点数。只有第一个建议适用于整数和浮点数。
吉姆

我认为您弄错了,尝试import numpy as np; x = [2.3, -1.4]; np.argmin(x)。您还将看到它也argmin可以在浮点数上使用
gg349

332

如果您枚举列表中的项目,则可以同时找到min / max索引和值,但是要对列表的原始值执行min / max。像这样:

import operator
min_index, min_value = min(enumerate(values), key=operator.itemgetter(1))
max_index, max_value = max(enumerate(values), key=operator.itemgetter(1))

这样,列表将只遍历一次最小值(或最大值)。


110
或使用Lambda:key=lambda p: p[1]
搜寻

116

如果要在数字列表中查找max的索引(这似乎是您的情况),那么建议您使用numpy:

import numpy as np
ind = np.argmax(mylist)

在多次出现最大值的情况下,返回对应于第一次出现的索引。
Cohensius

41

可能更简单的解决方案是将值的数组转换为值,索引对的数组,并取其最大值/最小值。这将给出具有最大值/最小值的最大/最小索引(即,通过首先比较第一个元素,然后比较第二个元素(如果第一个元素相同,则比较第二对元素))。注意,实际上不需要创建数组,因为最小/最大允许生成器作为输入。

values = [3,4,5]
(m,i) = max((v,i) for i,v in enumerate(values))
print (m,i) #(5, 2)

30
list=[1.1412, 4.3453, 5.8709, 0.1314]
list.index(min(list))

将给您第一个最小值的索引。



14

我对此也很感兴趣,并使用perfplot比较了一些建议的解决方案(我的一个宠物项目)。

原来那是numpy的argmin

numpy.argmin(x)

即使足够大的列表(从输入list到a 的隐式转换),它也是最快的方法numpy.array

在此处输入图片说明


生成绘图的代码:

import numpy
import operator
import perfplot


def min_enumerate(a):
    return min(enumerate(a), key=lambda x: x[1])[0]


def min_enumerate_itemgetter(a):
    min_index, min_value = min(enumerate(a), key=operator.itemgetter(1))
    return min_index


def getitem(a):
    return min(range(len(a)), key=a.__getitem__)


def np_argmin(a):
    return numpy.argmin(a)


perfplot.show(
    setup=lambda n: numpy.random.rand(n).tolist(),
    kernels=[
        min_enumerate,
        min_enumerate_itemgetter,
        getitem,
        np_argmin,
        ],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    )

请注意,两年多以前,我的答案中已经发布了相同的结论,并提供了有关何时以及为什么可以使用argmin的信息,以及为什么可以使用argmin的信息。考虑删除答案,这也没有使同一页上已经提出的建议有价值。考虑还应考虑针对SO的类似行为以查看其他答案:您似乎没有引用实际答案,从而在性能分析中提供了最佳解决方案。这是相当糟糕的,特别是对于具有> 10K代表并且已经足够了解更多信息的人而言。
gg349

@ gg349,非常好,但是他确实提供了用于生成结果的源代码,这使得该代码易于再现并且适用于比较其他解决方案。我同意他可以考虑删除此答案作为重复项,但是也许您可以通过包含或链接到所使用的代码来增加答案的价值?
Rakurai


8

获得最大值后,请尝试以下操作:

max_val = max(list)
index_max = list.index(max_val)

比很多选择要简单得多。


6

我认为以上答案解决了您的问题,但我想我会分享一种方法,该方法可以为您提供最小值以及所有出现在其中的索引。

minval = min(mylist)
ind = [i for i, v in enumerate(mylist) if v == minval]

这两次通过了列表,但仍然非常快。但是,这比找到最小值的第一次遇到的索引要慢一些。因此,如果您仅需要一个最小值,请使用Matt Anderson的解决方案,如果您需要全部解决方案,请使用此解决方案。


1
我之所以喜欢它,是因为它使用基本的Python,而且我发现列表理解比itemgetter,lambda等更容易理解(并且足够灵活,可以解决各种任务,例如....)
James

生的。我喜欢这个。
Dev_Man

6

使用numpy模块的函数numpy.where

import numpy as n
x = n.array((3,3,4,7,4,56,65,1))

对于最小值索引:

idx = n.where(x==x.min())[0]

对于最大值索引:

idx = n.where(x==x.max())[0]

实际上,此功能更强大。您可以构成各种布尔运算对于3到60之间的值的索引:

idx = n.where((x>3)&(x<60))[0]
idx
array([2, 3, 4, 5])
x[idx]
array([ 4,  7,  4, 56])

python中的索引从0开始。返回的索引应为6(对于65),而您的代码将返回7(OP的问题是“获取索引...”)
tagoma

在命令中,我查询了索引为IS 7的最小值(此处为1)的索引。65是数组中元素的最大值。如果键入:n.where(x == x.max())[0],则将获得最大索引。此处的值为65。它的索引将是6
Ishan Tomar

使用numpy:在此应用程序中可能被禁止。但是,如果您要使用numpy,那么最好使用argmin()而不是此处执行的操作。
RBF06 '18年

谢谢@ RBF06我会检查一下。
伊桑·托马尔

5

使用内置enumerate()max()函数以及函数的可选key参数max()和简单的lambda表达式,就可以轻松实现:

theList = [1, 5, 10]
maxIndex, maxValue = max(enumerate(theList), key=lambda v: v[1])
# => (2, 10)

在文档中max()说,该key参数需要一个类似于函数中的list.sort()函数。另请参阅“ 排序方法”

的工作原理相同min()。顺便说一句,它返回第一个最大值/最小值。


迟到但最好的答案(如果您不需要速度)。
mmj

5

假设您有一个清单,例如:

a = [9,8,7]

以下两种方法是使用最小元素及其索引获取元组的非常紧凑的方法。两者都相似时间来处理。我更喜欢zip方法,但这就是我的口味。

拉链方式

element, index = min(list(zip(a, range(len(a)))))

min(list(zip(a, range(len(a)))))
(7, 2)

timeit min(list(zip(a, range(len(a)))))
1.36 µs ± 107 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

列举方法

index, element = min(list(enumerate(a)), key=lambda x:x[1])

min(list(enumerate(a)), key=lambda x:x[1])
(2, 7)

timeit min(list(enumerate(a)), key=lambda x:x[1])
1.45 µs ± 78.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

4

只要您知道如何使用lambda和“ key”参数,一个简单的解决方案就是:

max_index = max( range( len(my_list) ), key = lambda index : my_list[ index ] )

很干净!与接受的答案不同,这是真实的 O(n),对吗?我知道O(2n)被认为是O(n),但是对于很大的情况,n它可能会明显变慢。
kevlarr

4

就那么简单 :

stuff = [2, 4, 8, 15, 11]

index = stuff.index(max(stuff))

3

为什么要先添加索引然后再颠倒索引呢?Enumerate()函数只是zip()函数用法的一种特殊情况。让我们以适当的方式使用它:

my_indexed_list = zip(my_list, range(len(my_list)))

min_value, min_index = min(my_indexed_list)
max_value, max_index = max(my_indexed_list)

2

只是已经说过的一小部分。 values.index(min(values))似乎返回的最小值的最小值。以下是最大的索引:

    values.reverse()
    (values.index(min(values)) + len(values) - 1) % len(values)
    values.reverse()

如果原地反转的副作用无关紧要,则可以省略最后一行。

遍历所有事件

    indices = []
    i = -1
    for _ in range(values.count(min(values))):
      i = values[i + 1:].index(min(values)) + i + 1
      indices.append(i)

为了简洁起见。将其缓存min(values), values.count(min)在循环外可能是一个更好的主意。


2
reversed(…)而不是….reverse()可能更可取,因为它不会突变并且无论如何都会返回一个生成器。而且所有的情况也可能是minv = min(values); indices = [i for i, v in enumerate(values) if v == minv]
HoverHell 2012年

2

如果您不想导入其他模块,则可以使用一种简单的方法在列表中查找值最小的索引:

min_value = min(values)
indexes_with_min_value = [i for i in range(0,len(values)) if values[i] == min_value]

然后选择第一个:

choosen = indexes_with_min_value[0]


0

https://docs.python.org/3/library/functions.html#max

如果有多个最大项,则该函数返回遇到的第一个项。这与其他排序稳定性保持工具(例如sorted(iterable, key=keyfunc, reverse=True)[0]

要获得的不仅仅是第一个,请使用sort方法。

import operator

x = [2, 5, 7, 4, 8, 2, 6, 1, 7, 1, 8, 3, 4, 9, 3, 6, 5, 0, 9, 0]

min = False
max = True

min_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = min )

max_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = max )


min_val_index[0]
>(0, 17)

max_val_index[0]
>(9, 13)

import ittertools

max_val = max_val_index[0][0]

maxes = [n for n in itertools.takewhile(lambda x: x[0] == max_val, max_val_index)]

0

那这个呢:

a=[1,55,2,36,35,34,98,0]
max_index=dict(zip(a,range(len(a))))[max(a)]

它从in中的项a作为键创建其字典,并将其索引作为值创建索引,从而dict(zip(a,range(len(a))))[max(a)]返回与键对应的值,即max(a)a中最大值的索引。我是python的初学者,所以我不知道该解决方案的计算复杂性。

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.