Obtendo o índice do item máximo ou mínimo retornado usando max () / min () em uma lista

465

Estou usando Python maxe minfunções em listas para um algoritmo minimax e preciso do índice do valor retornado por max()or min(). Em outras palavras, eu preciso saber qual jogada produziu o valor máximo (no turn do primeiro jogador) ou mínimo (segundo jogador).

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)

Eu preciso ser capaz de retornar o índice real do valor mínimo ou máximo, não apenas o valor.

Kevin Griffin
fonte
32
O builtin divmodexiste para evitar ter que dizer [i / 3, i % 3]muito.
Mike Graham

Respostas:

416
se isMinLevel:
    return values.index (min (values))
outro:
    return values.index (max (values))
muito php
fonte
38
@ KevinGriffin, observe que isso oferece a você apenas uma das várias ocorrências possíveis do mínimo / máximo. Pode não ser o que você deseja, por exemplo, se é possível aumentar seu ganho da mesma maneira, mas um deles machuca mais o outro jogador. Não sei se esse é um caso que você precisa considerar.
Mike Graham
89
@ Kashyap Na verdade, é O (N), não O (N ^ 2). No caso de min, primeiro min (valores) é avaliado, que é O (N), então values.index () é chamado, que também é O (N). O (N) + O (N) = O (N). O argumento para indexar é avaliado apenas uma vez. É equivalente a:tmp = min(values); return values.index(tmp)
Tom Karzes
@ muito php o que fazer quando houver repetição de elementos.?
Shashi Tunga
@ShashiTunga [list] .index () retorna apenas a primeira ocorrência de algo, não é garantido que seja exclusivo, o valor mínimo pode não ser único na lista
Scott Anderson
473

Diga que você tem uma lista values = [3,6,1,5]e precisa do índice do menor elemento, ou seja index_min = 2, neste caso.

Evite a solução itemgetter()apresentada nas outras respostas e use

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

porque não exige import operatornem deve ser usado enumerate, e é sempre mais rápido (referência abaixo) do que uma solução usando itemgetter().

Se você estiver lidando com matrizes numpy ou puder pagar numpycomo uma dependência, considere também usar

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

Isso será mais rápido que a primeira solução, mesmo se você aplicá-la a uma lista Python pura se:

  • é maior que alguns elementos (cerca de 2 ** 4 elementos na minha máquina)
  • você pode comprar a cópia da memória de uma lista pura para uma numpymatriz

como esta referência indica: insira a descrição da imagem aqui

Executei o benchmark na minha máquina com python 2.7 para as duas soluções acima (azul: python puro, primeira solução) (solução vermelha e numpy) e para a solução padrão baseada em itemgetter()(solução preta de referência). A mesma referência do python 3.5 mostrou que os métodos comparam exatamente o mesmo do caso do python 2.7 apresentado acima

gg349
fonte
Um +1 muito forte. Adoro o benchmarking das soluções propostas e as regras práticas que você resumiu. Como sugeri em outra resposta abaixo, você poderia fornecer (ou vincular ao) seu código de teste para que outras pessoas possam reproduzir seus resultados? Máquinas e bibliotecas mudam com o tempo e permitiriam comparar com outras soluções.
Rakurai 14/01/19
3
Eu acho que pode haver um erro de digitação: xrange. Não deveria ser alcance?
Lindsay Fowler
6
@LindsayFowler xrange()agora está obsoleto, você pode usarrange()
davide
O np.argmin não funciona para carros alegóricos. somente a primeira sugestão funciona em entradas e flutuadores.
jimh 19/02
Eu acho que você está enganado, tente import numpy as np; x = [2.3, -1.4]; np.argmin(x). Você verá que também argminfunciona em carros alegóricos
gg349 24/02
332

Você pode encontrar o índice e o valor mínimo / máximo ao mesmo tempo se enumerar os itens da lista, mas execute o mínimo / máximo nos valores originais da lista. Igual a:

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

Dessa forma, a lista será percorrida apenas uma vez por min (ou max).

Matt Anderson
fonte
110
Ou use um lambda:key=lambda p: p[1]
scry
116

Se você deseja encontrar o índice max dentro de uma lista de números (o que parece ser o seu caso), sugiro que você use numpy:

import numpy as np
ind = np.argmax(mylist)
dr.haz
fonte
No caso de múltiplas ocorrências dos valores máximos, os índices correspondentes à primeira ocorrência são retornados.
Cohensius
41

Possivelmente, uma solução mais simples seria transformar a matriz de valores em uma matriz de valores, pares de índices e tirar o máximo / min disso. Isso daria o maior / menor índice com max / min (ou seja, os pares são comparados comparando primeiro o primeiro elemento e, em seguida, comparando o segundo elemento se os primeiros forem iguais). Observe que não é realmente necessário criar a matriz, porque min / max permitem geradores como entrada.

values = [3,4,5]
(m,i) = max((v,i) for i,v in enumerate(values))
print (m,i) #(5, 2)
Ant6n
fonte
30
list=[1.1412, 4.3453, 5.8709, 0.1314]
list.index(min(list))

Dará a você o primeiro índice do mínimo.

Andy
fonte
18

Eu acho que a melhor coisa a fazer é converter a lista em um numpy arraye usar esta função:

a = np.array(list)
idx = np.argmax(a)
Akshaya Natarajan
fonte
14

Eu também estava interessado nisso e comparei algumas das soluções sugeridas usando o perfplot (um projeto de estimação para mim).

Acontece que o argmin desse numpy ,

numpy.argmin(x)

é o método mais rápido para listas grandes o suficiente, mesmo com a conversão implícita da entrada listem a numpy.array.

insira a descrição da imagem aqui


Código para gerar o gráfico:

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,
    )
Nico Schlömer
fonte
Observe que a mesma conclusão já foi postada acima na minha resposta, há mais de 2 anos, com mais informações sobre quando e por que o argmin pode ser usado ou não. Considere excluir a resposta, o que também não está dando mérito ao que já foi proposto nesta mesma página. Considere também revisar suas outras respostas no SO para obter um comportamento semelhante: você parece não citar a resposta real, fornecendo a melhor solução em suas análises de desempenho. Isso é bastante ruim, especialmente para alguém com mais de 10.000 representantes que já existe há tempo suficiente para conhecer melhor.
gg349 3/01/19
@ gg349, pontos muito bons, mas ele fornece o código-fonte para gerar os resultados, tornando-o facilmente reproduzível e adaptável à comparação de outras soluções. Concordo que ele possa considerar remover esta resposta como duplicada, mas talvez você possa agregar valor à sua resposta incluindo ou vinculando o código que você usou?
Rakurai 14/01/19
8

Use uma matriz numpy e a função argmax ()

 a=np.array([1,2,3])
 b=np.argmax(a)
 print(b) #2
John Misquita
fonte
8

Depois de obter os valores máximos, tente o seguinte:

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

Muito mais simples do que muitas opções.

alpha_989
fonte
6

Acho que a resposta acima resolve o seu problema, mas pensei em compartilhar um método que fornece o mínimo e todos os índices em que o mínimo aparece.

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

Isso passa na lista duas vezes, mas ainda é bastante rápido. No entanto, é um pouco mais lento do que encontrar o índice do primeiro encontro do mínimo. Portanto, se você precisar de apenas um dos mínimos, use a solução de Matt Anderson ; se precisar de todos, use isso.

Burak Bağdatlı
fonte
1
I como este, porque ele usa Python base, e acho lista compreensão mais fácil de entender do que itemgetter, lambda etc. (e suficientemente flexível para resolver uma variedade de tarefas, como este ....)
James
cru. Eu prefiro isso.
precisa saber é
6

Use a função do módulo numpy numpy.where

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

Para índice de valor mínimo:

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

Para índice de valor máximo:

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

De fato, essa função é muito mais poderosa. Você pode representar todos os tipos de operações booleanas para índice de valor entre 3 e 60:

idx = n.where((x>3)&(x<60))[0]
idx
array([2, 3, 4, 5])
x[idx]
array([ 4,  7,  4, 56])
Ishan Tomar
fonte
index in começa python em 0. índice retornado será de 6 (65), enquanto seus retornos de código 7 (pergunta de OP foi "Conseguir o índice ...")
tagoma
No comando, consultei o índice de valor mínimo (aqui: 1) cujo índice é 7. 65 é o valor máximo de elementos na matriz. Se você digitar: n.where (x == x.max ()) [0], obterá o índice de máx. valor que é 65 aqui. Seu índice será 6
Ishan Tomar
uso de numpy: provavelmente proibido nesta aplicação. Mas se você vai usar numpy, é muito melhor usar apenas ao argmin()invés do que fez aqui.
RBF06 09/04
Obrigado @ RBF06 vou dar uma olhada.
Ishan Tomar
5

Isso é simplesmente possível usando o built-in enumerate()and max()function e o keyargumento opcional da max()função e uma simples expressão lambda:

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

Nos documentos para max()ele diz que o keyargumento espera uma função como na list.sort()função. Consulte também o Guia de classificação .

Funciona da mesma maneira para min(). Entre, ele retorna o primeiro valor máximo / mínimo.

Simon Hänisch
fonte
Resposta tardia, mas melhor (se você não precisa de velocidade).
MMJ
5

Digamos que você tenha uma lista como:

a = [9,8,7]

Os dois métodos a seguir são maneiras bastante compactas de obter uma tupla com o elemento mínimo e seu índice. Ambos têm uma abordagem semelhante tempo ao processo. Eu gosto mais do método zip, mas esse é o meu gosto.

método 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)

enumerar método

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)
Pablo MPA
fonte
4

Desde que você saiba como usar o lambda e o argumento "key", uma solução simples é:

max_index = max( range( len(my_list) ), key = lambda index : my_list[ index ] )
Veiga
fonte
Muito limpo! E, diferentemente da resposta aceita, isso é verdade O (n), certo? Eu sei que O (2n) é considerado O (n), mas para muito grande npode ser visivelmente mais lento.
Kevlarr #
4

Simples assim :

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

index = stuff.index(max(stuff))
dctremblay
fonte
3

Por que se preocupar em adicionar índices primeiro e depois revertê-los? A função Enumerate () é apenas um caso especial de uso da função zip (). Vamos usá-lo de maneira apropriada:

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)
sofista
fonte
2

Apenas uma pequena adição ao que já foi dito. values.index(min(values))parece retornar o menor índice de min. A seguir, obtém o maior índice:

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

A última linha pode ser deixada de fora se o efeito colateral da reversão no local não importar.

Para percorrer todas as ocorrências

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

Por uma questão de brevidade. Provavelmente, é uma idéia melhor armazenar em cache min(values), values.count(min)fora do loop.

hiperbólico
fonte
2
reversed(…)em vez de ….reverse()provavelmente é preferível, pois não muda e retorna um gerador de qualquer maneira. E todas as ocorrências também poderia serminv = min(values); indices = [i for i, v in enumerate(values) if v == minv]
HoverHell
2

Uma maneira simples de encontrar os índices com valor mínimo em uma lista, se você não deseja importar módulos adicionais:

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

Em seguida, escolha, por exemplo, o primeiro:

choosen = indexes_with_min_value[0]
antoninstuppa
fonte
1

Não tem representante alto o suficiente para comentar a resposta existente.

Mas para https://stackoverflow.com/a/11825864/3920439 answer

Isso funciona para números inteiros, mas não para matrizes flutuantes (pelo menos no python 3.6). TypeError: list indices must be integers or slices, not float

ThePianoDentist
fonte
0

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

Se vários itens forem máximos, a função retornará o primeiro encontrado. Isso é consistente com outras ferramentas de preservação da estabilidade de classificação, comosorted(iterable, key=keyfunc, reverse=True)[0]

Para obter mais do que apenas o primeiro, use o método de classificação.

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)]
The Demz
fonte
0

Que tal isso:

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

Ele cria um dicionário a partir dos itens em acomo chaves e seus índices como valores; assim, dict(zip(a,range(len(a))))[max(a)]retorna o valor que corresponde à chave max(a)que é o índice do máximo em a. Como sou iniciante em python, não conheço a complexidade computacional dessa solução.

Dr.Simplisist
fonte