Подтвердить что ты не робот

Поиск индекса значения min или max в Python

У меня есть структура формы:

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

(индексы кортежей, которые я хочу взять перед собой)

4b9b3361

Ответ 1

Это работает:

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

Ответ 2

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

Возвращает индекс самого большого элемента в items и самого элемента.

Ответ 3

Этот метод находит индекс максимального элемента любого итерабельного и не требует внешнего импорта:

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

Ответ 4

Индекс максимального списка:

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

Если в lst есть повторяющиеся максимальные значения, это вернет индекс первого найденного максимального значения.

Ответ 5

поэтому это быстрый и простой способ получить эффективную версию, которую вы ищете:

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]

Ответ 6

Еще один способ получить argmax:

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

Ответ 7

Это легко, если вы не пытаетесь использовать тот факт, что списки внутренних диапазонов отсортированы

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

Ответ 8

Я не уверен, что случилось, но я думаю, что все немного отстают. Я буду обвинять его в том, что вы плохо работаете, объясняя проблему, которую я пытаюсь решить. Во всяком случае, вот сколько я получил:

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

Update:

Сборка правильного подмножества неподвижных элементов до min over - это билет.

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 перестраивается на каждую итерацию. Это не обязательно, но это не улучшает асимптотические ограничения... Конечно, нужно задаться вопросом, действительно ли мы заботимся о производительности, будь то использование лямбда - хорошая идея, хотя я действительно не см. путь вокруг этого, не отделяя min, что просто сводится к безумию.