在 Python 中查找最小值或最大值的值的索引
Posted
技术标签:
【中文标题】在 Python 中查找最小值或最大值的值的索引【英文标题】:Finding the index of the value which is the min or max in Python 【发布时间】:2013-06-01 11:35:50 【问题描述】:我有一个表格的结构:
>>> items
[([[0, 1], [2, 20]], 'zz', ''), ([[1, 3], [5, 29], [50, 500]], 'a', 'b')]
每个元组中的第一项是范围列表,我想创建一个生成器,根据起始索引按升序向我提供范围。
由于范围列表已经按照它们的起始索引排序,这个操作很简单:它只是一个排序合并。我希望以良好的计算效率来做到这一点,所以我认为隐式跟踪合并状态的一种好方法是简单地从具有最小起始索引的元组列表中弹出前面范围列表。
我可以使用min()
获得[0, 1]
,这是我想要的第一个,但是我如何获得它的索引?
我有这个:
[ min (items[i][0]) for i in range(len(items)) ]
这给了我每个列表中的第一项,然后我可以min()
以某种方式结束,但是一旦任何列表变为空,它就会失败,而且还不清楚如何让索引使用pop()
无需在列表中查找它。
总结一下:想要构建为我返回的生成器:
([0,1], 'zz', '')
([1,3], 'a', 'b')
([2,20], 'zz', '')
([5,29], 'a', 'b')
([50,500], 'a', 'b')
或者更高效,我只需要这些数据:
[0, 1, 0, 1, 1]
(我想取前面的元组的索引)
【问题讨论】:
我为之前的答案写了mergeiter
function;我用enumerate()
添加索引。
@MartijnPieters 对 Python 非常熟悉,起初我很难理解你的 mergeiter
函数。但是在查看了这些其他答案之后,您的方法显然是正确的。然而,它是唯一一个没有作为答案发布的......
标题中的问题是python - Getting the index of the returned max or min item using max()/min() on a list - Stack Overflow
【参考方案1】:
from operator import itemgetter
index, element = max(enumerate(items), key=itemgetter(1))
返回items
中最大元素的索引和元素本身。
【讨论】:
太好了,假设我想深入了解一些(比如,假设我想按第一个索引的某些部分进行排序),我需要为此使用 lambda 吗?请考虑我的“无用的东西”作为我为什么要让它变得如此复杂的理由:) 我将在最后一条评论中回答我的问题,我认为深入挖掘的方法是使用 lambda。max(enumerate(items), key=lambda x: x[1])
,为您节省导入。
这总是比其他解决方案更糟糕,请参阅benchmark。 lambda 可能更慢。【参考方案2】:
如果您不尝试使用内部范围列表已排序的事实,这很容易
sorted(sum([ [(rng,) + i[1:] for rng in i[0]] for i in items ], []), lambda i: i[0][0])
听起来你想要一个返回最小值索引的函数
def min_idx(l, key=lambda x: x):
min_i, min_key = None, float('inf')
for i, v in enumerate(l):
key_v = key(v)
if key_v < min_key:
mini_i = i
min_key = key_v
return min_i
def merge_items(items):
res = []
while True:
i = min_idx(items, key=lambda i: i[0][0][0])
item = items[i]
res.append((item[0][0],) + item[1:])
return res
【讨论】:
【参考方案3】:这行得通:
by_index = ([sub_index, list_index] for list_index, list_item in
enumerate(items) for sub_index in list_item[0])
[item[1] for item in sorted(by_index)]
给予:
[0, 1, 0, 1, 1]
详细。生成器:
by_index = ([sub_index, list_index] for list_index, list_item in
enumerate(items) for sub_index in list_item[0])
list(by_index)
[[[0, 1], 0], [[2, 20], 0], [[1, 3], 1], [[5, 29], 1], [[50, 500], 1]]
所以唯一需要做的就是排序并只获取所需的索引:
[item[1] for item in sorted(by_index)]
【讨论】:
我认为排序在这里完全没有必要,即使它可能不会使运行时变得可怕或任何事情。只需要一个 min 操作 Python 的排序非常高效 (en.wikipedia.org/wiki/Timsort)。也使用了合并排序。如果我理解正确,您想将min
应用于所有索引。取出最小的并重复直到列表被消耗掉?我进行了一些测试,表明对于较大的列表以及项目已经按某种排序顺序进行排序会更快。
嗯。你可能是对的。毕竟一分钟以上的操作。
在看到您的解决方案后更新了我的解决方案,并更好地了解了您的需求。现在只有两条线。尝试根据您的大型数据集解决方案计时。
哦,我喜欢这个!根据我的直觉,这段代码看起来更适合 VM 进行优化。我不怀疑它会更高效。谢谢【参考方案4】:
所以这是获得您正在寻找的高效版本的真正快速简便的方法:
a = []
count = 0
for i in items:
for x in i[0]:
#place a list with the index next to it in list a for sorting
a.append((x,count))
#continually grabs the smallest list and returns the index it was in
sort = [a.pop(a.index(min(a)))[1] for i in range(len(a))]
这是你的物品,以表明它有效:
>>> items = [([[0, 1], [2, 20]], 'zz', ''), ([[1, 3], [5, 29], [50, 500]], 'a', 'b')]
>>> a = []
>>> count = 0
>>> for i in items:
... for x in i[0]:
... a.append((x,count))
... count += 1
...
>>> sort = [a.pop(a.index(min(a)))[1] for i in range(len(a))]
>>> sort
[0, 1, 0, 1, 1]
【讨论】:
我试图避免重新查找,这是a.index(min(a))
所做的。 index 是一个搜索...
哦,好吧。该列表理解是几乎任何数组的快速排序器!一个列表,列表列表......它基本上适用于任何东西,我只需要添加[1]
。虽然如果你不想查看它们,我很难理解你打算如何排序......
嗯,是的,我也很难理解它,这就是我发布问题的原因,但必须有办法做到这一点。为了提高计算效率,我们只需要跟踪我们向下移动每个子数组的距离。我认为这可以通过弹出它们来完成,但我只是不知道如何将它们放在一起。
根据您的解释,我认为我的代码很适合。因为如果您阅读评论,我的代码会做什么,它会跟踪哪个列表在哪个元组中!之所以有a.index
,是因为你需要这样做才能使用a.pop
。我误会你了吗?
是的,但它只需要知道项目中的哪个元组作为其第一项中的第一个索引具有最小的价值(那些是两个列表)。所以它可以弹出它。然后下一次它会重复。它不需要使用两个列表调用index
。【参考方案5】:
我不确定发生了什么,但我认为每个人都有些不合时宜。我会把它归咎于在解释我要解决的问题时做得不好。无论如何,这是我得到了多少:
items[min(range(len(items)), key = lambda x: items[x][0][0])][0].pop(0)
这需要我大部分时间,但剩下要处理的是处理一个列表已用尽的情况。一旦解决了这个问题,让它成为一个生成器应该是微不足道的,因为我可以把它放在一个循环中并在其中产生,并且希望不需要太多的工作,这可以适应对生成器执行有效的排序合并。
>>> items[min(range(len(items)), key = lambda x: items[x][0][0])][0].pop(0)
[0, 1]
>>> items[min(range(len(items)), key = lambda x: items[x][0][0])][0].pop(0)
[1, 3]
>>> items[min(range(len(items)), key = lambda x: items[x][0][0])][0].pop(0)
[2, 20]
>>> items[min(range(len(items)), key = lambda x: items[x][0][0])][0].pop(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
IndexError: list index out of range
更新:
将仍然有效的项目的正确子集组装到min
是票。
def next_value_in_sections(sections):
while 1:
idxs = []
for i, x in enumerate(sections):
if x[0]:
idxs.append(i)
print idxs
if not idxs:
break
j = min(idxs, key=lambda x: sections[x][0][0])
yield (sections[j][0].pop(0), j)
items = [([[0, 1], [2, 20]], 'zz', ''),
([[1, 3], [5, 29], [50, 500]], 'a', 'b')]
x = next_value_in_sections(items)
for i in x:
print i
执行:
$ python test.py
[0, 1]
([0, 1], 0)
[0, 1]
([1, 3], 1)
[0, 1]
([2, 20], 0)
[1]
([5, 29], 1)
[1]
([50, 500], 1)
[]
我会注意到这仍然可以改进,每次迭代都会重建 idxs 列表。不需要,但这样做并不能改善渐近界......当然,人们不得不怀疑我们是否真的关心性能,使用 lambda 是否也是一个好主意,虽然我真的不在不拆开min
的情况下找到解决方法,这简直是陷入疯狂。
【讨论】:
这可能有点令人筋疲力尽,但为什么不把它放在try
和except
中,然后你只需找出except
中的哪个列表是空的,然后你剩下的!
是的,考虑到我什至避免排序和组装更多列表,因为我认为这些是不必要的,因此为此引入例外感觉太重了。如果我打算将它与生成器兼容作为输入列表,它看起来确实可以发挥很好的作用。所以这很好。我想我有一个解决方案。测试后将编辑答案【参考方案6】:
此方法查找任何可迭代的最大元素的索引,并且不需要任何外部导入:
def argmax(iterable):
return max(enumerate(iterable), key=lambda x: x[1])[0]
【讨论】:
唯一的非循环解决方案可以在一次扫描中为您提供 max 和 argmax! 这里有一个相关的方法,基于反转元组字典顺序 -max(enumerate(l), key=lambda t: list(reversed(t)))
。与原始不同,当最大值出现多次时,这将为您提供最后一个索引(在某些情况下更有用)【参考方案7】:
列表最大值的索引:
def argmax(lst):
return lst.index(max(lst))
如果 lst 中有重复的最大值,这将返回找到的第一个最大值的索引。
【讨论】:
它还会迭代列表两次,所以不要这样做。 @mrexodia 两次迭代列表有什么问题?只是效率低下?这种实现有可能比基于enumerate
的实现快得多,因为它们在堆上为每个元素分配一对。此解决方案也比其他解决方案更短且更易于理解,因此可以说更 Pythonic。但如果项目比较昂贵,它可能会更慢,并且它不适用于一般的可迭代对象。【参考方案8】:
获取 argmax 的另一种方法是:
def argmax(lst):
return max(range(len(lst)), key=lst.__getitem__)
【讨论】:
【参考方案9】:最简单最有效的方式(O(n))
arg_max, maximum = max(list(enumerate(nums)), key=lambda x: x[1]) # Returns both the maximum element and it's index
【讨论】:
以上是关于在 Python 中查找最小值或最大值的值的索引的主要内容,如果未能解决你的问题,请参考以下文章
R语言使用Which.max和Which.min函数定位数据对象中的第一个最大值或最小值实战:使用which.max函数查找第一个最大值的索引使用which.min函数查找第一个最小值的索引