tags:

views:

252

answers:

6

I have a list:

a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50,
             35, 41, 49, 37, 19, 40, 41, 31]

max element is 55 (two elements on position 9 and 12)

I need to find on which position the maximum elements are situated. Please, help.

+8  A: 
a.index(max(a))

will tell you the index of the first instance of the largest valued element of list a.

Nathon
This will only get you the first instance though and he asked for all the indexes where the largest value is found. You'd have to loop on that using slice to get the remaining list in each case and handling the exception when it's not found any longer.
jaydel
I did mention that it would only give the first instance. If you want all of them, SilentGhost's solution is much prettier and less error prone.
Nathon
+17  A: 
>>> m = max(a)
>>> [i for i, j in enumerate(a) if j == m]
[9, 12]
SilentGhost
nice :) my first thought didn't include enumerate. I've become very rusty...oh, the horror! D:
townsean
Nice short answer if you don't mind making multiple passes through the list -- which is likely.
martineau
@SilentGhost: It's "OK Corral" time ... see my answer.
John Machin
+2  A: 

Here is the max value and the indexes it appears at:

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50, 35, 41, 49, 37, 19, 40, 41, 31]
>>> for i, x in enumerate(a):
...     d[x].append(i)
... 
>>> k = max(d.keys())
>>> print k, d[k]
55 [9, 12]

Later: for the satisfaction of @SilentGhost

>>> from itertools import takewhile
>>> import heapq
>>> 
>>> def popper(heap):
...     while heap:
...         yield heapq.heappop(heap)
... 
>>> a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50, 35, 41, 49, 37, 19, 40, 41, 31]
>>> h = [(-x, i) for i, x in enumerate(a)]
>>> heapq.heapify(h)
>>> 
>>> largest = heapq.heappop(h)
>>> indexes = [largest[1]] + [x[1] for x in takewhile(lambda large: large[0] == largest[0], popper(h))]
>>> print -largest[0], indexes
55 [9, 12]
hughdbrown
you do realise how inefficient this is?
SilentGhost
Rationalizations: (1) "Premature optimization is the ... etc." (2) It probably doesn't matter. (3) It's still a good solution. Maybe I'll recode it to use `heapq` -- finding the max there would be trivial.
hughdbrown
@hugh: while I'd love to see your `heapq` solution, I doubt it would work.
SilentGhost
@silentGhost: It works!
hughdbrown
+2  A: 

The chosen answer (and others) require at least two passes through the list. Here's a one pass solution.

Edited: To address the two deficiencies pointed out by @John Machin. For (2) I attempted to optimize the tests based on guesstimated probability of occurrence of each condition and inferences allowed from predecessors. It was a little tricky figuring out the proper initialization value formax_indices which worked for all possible cases, especially if the max happened to be the first value in the list.

a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50,
             35, 41, 49, 37, 19, 40, 41, 31]

def maxelements(seq):
    ''' Return list of position(s) of largest element '''
    max_indices = []
    if len(seq):
        max_val = seq[0]
        for i,val in ((i,val) for i,val in enumerate(seq) if val >= max_val):
            if val == max_val:
                max_indices.append(i)
            else:
                max_val = val
                max_indices = [i]

    return max_indices
martineau
(1) The empty list handling needs attention. Should return `[]` as advertised ("Return list"). Code should be simply `if not seq: return []`. (2) Testing scheme in loop is sub-optimal: on average in random lists, condition `val < maxval` will be the most common but the above code takes 2 tests instead of one.
John Machin
+1 to @John Machin's comment for catching the inconsistency with the docstring and not letting me get away with posting sub-optimal code. To be truthful, since an answer was already accepted, I lost a bit of motivation to continue working on my answer, since I assumed hardly anyone further would even look at it -- and it's so much longer than everyone else's.
martineau
@martineau: "accepted" answers are not necessarily "acceptable". I generally read all answers. Including your revision. Which does 3 tests now in the rare case of `==` instead of 2 -- your `elif` condition will always be true.
John Machin
@John Machin: I got really inspired and revised it even further. Now it's down minimumal additional tests, plus a few other tweaks. Thanks for your comments and constructive criticisms. I caught the always True `elif` myself, FWIW. ;-)
martineau
@martineau: See my answer.
John Machin
@John Machin: Hmmm, your timing results seem to contradict my own, so I will remove what I said in my answer about timing so I can look into what's going on further. Thanks for the heads-up. Actually I think a "real" timing test would need to use random list values.
martineau
@John Machin: To follow up, yep, I made a dumb mistake when performing my timing tests, so the results were totally bogus. Anyway, with regards to your more accurate results, I guess there's almost no beating the built-ins for pure speed short of writing an external C module.
martineau
+1  A: 

I can't reproduce the @SilentGhost-beating performance quoted by @martineau. Here's my effort with comparisons:

=== maxelements.py ===

a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50,
             35, 41, 49, 37, 19, 40, 41, 31]
b = range(10000)
c = range(10000 - 1, -1, -1)
d = b + c

def maxelements_s(seq): # @SilentGhost
    ''' Return list of position(s) of largest element '''
    m = max(seq)
    return [i for i, j in enumerate(seq) if j == m]

def maxelements_m(seq): # @martineau
    ''' Return list of position(s) of largest element '''
    max_indices = []
    if len(seq):
        max_val = seq[0]
        for i, val in ((i, val) for i, val in enumerate(seq) if val >= max_val):
            if val == max_val:
                max_indices.append(i)
            else:
                max_val = val
                max_indices = [i]
    return max_indices

def maxelements_j(seq): # @John Machin
    ''' Return list of position(s) of largest element '''
    if not seq: return []
    max_val = seq[0] if seq[0] >= seq[-1] else seq[-1]
    max_indices = []
    for i, val in enumerate(seq):
        if val < max_val: continue
        if val == max_val:
            max_indices.append(i)
        else:
            max_val = val
            max_indices = [i]
    return max_indices

Results from a beat-up old laptop running Python 2.7 on Windows XP SP3:

>\python27\python -mtimeit -s"import maxelements as me" "me.maxelements_s(me.a)"
100000 loops, best of 3: 6.88 usec per loop

>\python27\python -mtimeit -s"import maxelements as me" "me.maxelements_m(me.a)"
100000 loops, best of 3: 11.1 usec per loop

>\python27\python -mtimeit -s"import maxelements as me" "me.maxelements_j(me.a)"
100000 loops, best of 3: 8.51 usec per loop

>\python27\python -mtimeit -s"import maxelements as me;a100=me.a*100" "me.maxelements_s(a100)"
1000 loops, best of 3: 535 usec per loop

>\python27\python -mtimeit -s"import maxelements as me;a100=me.a*100" "me.maxelements_m(a100)"
1000 loops, best of 3: 558 usec per loop

>\python27\python -mtimeit -s"import maxelements as me;a100=me.a*100" "me.maxelements_j(a100)"
1000 loops, best of 3: 489 usec per loop
John Machin
A: 
import operator

def max_positions(iterable, key=None, reverse=False):
  if key is None:
    def key(x):
      return x
  if reverse:
    better = operator.lt
  else:
    better = operator.gt

  it = enumerate(iterable)
  for pos, item in it:
    break
  else:
    raise ValueError("max_positions: empty iterable")
    # note this is the same exception type raised by max([])
  cur_max = key(item)
  cur_pos = [pos]

  for pos, item in it:
    k = key(item)
    if better(k, cur_max):
      cur_max = k
      cur_pos = [pos]
    elif k == cur_max:
      cur_pos.append(pos)

  return cur_max, cur_pos

def min_positions(iterable, key=None, reverse=False):
  return max_positions(iterable, key, not reverse)

>>> L = range(10) * 2
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> max_positions(L)
(9, [9, 19])
>>> min_positions(L)
(0, [0, 10])
>>> max_positions(L, key=lambda x: x // 2, reverse=True)
(0, [0, 1, 10, 11])
Roger Pate