简体   繁体   English

查找 Python 中最小值或最大值的索引

[英]Finding the index of the value which is the min or max in Python

I've got a structure of the form:我有一个形式的结构:

>>> items
[([[0, 1], [2, 20]], 'zz', ''), ([[1, 3], [5, 29], [50, 500]], 'a', 'b')]

The first item in each tuple is a list of ranges, and I want to make a generator that provides me the ranges in ascending order based on the starting index.每个元组中的第一项是范围列表,我想制作一个生成器,根据起始索引按升序为我提供范围。

Since the range-lists are already sorted by their starting index this operation is simple: it is just a sorted merge.由于范围列表已经按其起始索引排序,因此此操作很简单:它只是一个排序合并。 I'm hoping to do it with good computational efficiency, so I'm thinking that one good way to implicitly track the state of my merge is to simply pop the front off of the list of the tuple which has the smallest starting index in its range list.我希望以良好的计算效率来做到这一点,所以我认为隐式跟踪我的合并的 state 的一种好方法是简单地弹出元组列表的前面,该元组的起始索引在其范围列表。

I can use min() to obtain [0, 1] which is the first one I want, but how do I get the index of it?我可以使用min()来获得[0, 1]这是我想要的第一个,但我如何获得它的索引?

I have this:我有这个:

[ min (items[i][0]) for i in range(len(items)) ]

which gives me the first item in each list, which I can then min() over somehow, but it fails once any of the lists becomes empty, and also it's not clear how to get the index to use pop() with without looking it back up in the list.这给了我每个列表中的第一个项目,然后我可以min()以某种方式进行,但是一旦任何列表变空它就会失败,而且还不清楚如何让索引在不看的情况下使用pop()备份在列表中。

To summarize: Want to build generator that returns for me:总结一下:想要构建为我返回的生成器:

([0,1], 'zz', '')
([1,3], 'a', 'b')
([2,20], 'zz', '')
([5,29], 'a', 'b')
([50,500], 'a', 'b')

Or even more efficiently, I only need this data:或者更有效地,我只需要这些数据:

[0, 1, 0, 1, 1]

(the indices of the tuples i want to take the front item of) (我想取前面的元组的索引)

 from operator import itemgetter
 index, element = max(enumerate(items), key=itemgetter(1))

Return the index of the biggest element in items and the element itself. 返回items最大元素的索引和元素本身。

This method finds the index of the maximum element of any iterable and does not require any external imports: 此方法查找任何可迭代的最大元素的索引,并且不需要任何外部导入:

def argmax(iterable):
    return max(enumerate(iterable), key=lambda x: x[1])[0]

The index of the max of a list: 列表最大值的索引:

def argmax(lst):
  return lst.index(max(lst))

If there are duplicate max values in lst, this will return the index of the first maximum value found. 如果lst中存在重复的最大值,则返回找到的第一个最大值的索引。

This works: 这有效:

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)]

Gives: 得到:

[0, 1, 0, 1, 1]

In detail. 详细。 The generator: 发电机:

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]]

So the only thing needed is sorting and getting only the desired index: 因此,唯一需要的是排序并获得所需的索引:

[item[1] for item in sorted(by_index)]

Yet another way to get the argmax is: 获得argmax的另一种方法是:

def argmax(lst):
    return max(range(len(lst)), key=lst.__getitem__)

so this is a real quick and easy way to get that efficient version you are looking for: 所以这是一个真正快速简便的方法来获得您正在寻找的高效版本:

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))]

Here is it with your items to show that it works: 这是与您的项目,以显示它的工作原理:

>>> 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]

It's easy if you don't try to use the fact that the internal range lists are sorted 如果您不尝试使用内部范围列表进行排序的事实,这很容易

sorted(sum([ [(rng,) + i[1:] for rng in i[0]] for i in items ], []), lambda i: i[0][0])

It sounds like you want a function that returns the index of the smallest value though 听起来你想要一个返回最小值索引的函数

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

I'm not sure what happened but I think everyone's a bit off the mark. 我不确定发生了什么,但我认为每个人都有点不合时宜。 I'll blame it on doing a bad job explaining the problem I'm trying to solve. 我会把它归咎于做一个糟糕的工作来解释我正试图解决的问题。 Anyway, here's how much I've gotten: 无论如何,这是我得到了多少:

items[min(range(len(items)), key = lambda x: items[x][0][0])][0].pop(0)

This takes me most of the way there but what's left to deal with is treating the situation where one list has been exhausted. 这让我大部分都在那里,但剩下要处理的是处理一个列表已经用尽的情况。 Once that's taken care of it should be trivial to make this a generator as I can just put it in a loop and yield inside it, and also hopefully without too much more work this can be adapted into performing an efficient sort-merge over generators. 一旦处理好它应该是微不足道的,使它成为一个生成器,因为我可以把它放在一个循环中并在其中产生,并且也希望没有太多的工作,这可以适应于执行有效的排序合并生成器。

>>> 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

Update: 更新:

Assembling the proper subset of still-valid-items to min over is the ticket. 将剩余有效物品的适当子集组装成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                                           

Executed: 执行:

$ 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)
[]

I'll note this still can be improved, the idxs list is being rebuilt each iteration. 我会注意到这仍然可以改进,每次迭代都会重建idxs列表。 It does not need to be, but doing that does not improve asymptotic bound... Of course, one has to wonder if we really care about performance, whether the use of the lambda is a good idea either, though I really don't see a way around that without taking apart min , which is simply a descent into madness. 它不需要,但这样做并没有改善渐近界限...当然,人们不得不怀疑我们是否真的关心性能,是否使用lambda也是一个好主意,尽管我真的不这样做看看周围的方式,而不分开min ,这只是疯狂的下降。

最简单最有效的方式(O(n))

arg_max, maximum = max(list(enumerate(nums)), key=lambda x: x[1])  # Returns both the maximum element and it's index 

Yet another option:另一种选择:

max(zip(lst, range(len(lst))))[0]

This seems to be the fastest, together with这似乎是最快的,连同

max(range(len(lst)), key=lst.__getitem__)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM