4

I have a large array of integers, and I need to print the maximum of every 10 integers and its corresponding index in the array as a pair.

ex. (max_value, index of max_value in array)

I can successfully find the maximum value and the corresponding index within the first 10 integers, however I am having trouble looping through the entire array.

I have tried using:

a = some array of integers

split = [a[i:i+10] for i in xrange(0, len(a), 10)] 

for i in split:
    j = max(i) 
    k = i.index(max(i))
    print (j,k)

The issue with this method is that it splits my array into chunks of 10 so the max_values are correct, but the indexes are inaccurate (all of the indexes are between 0-10.) I need to find a way of doing this that doesn't split my array into chunks so that the original indices are retained. I'm sure there is an easier way of looping through to find max values but I can't seem to figure it out.

Harald Nordgren
  • 11,693
  • 6
  • 41
  • 65
nikki_c
  • 117
  • 1
  • 3
  • 7

8 Answers8

5

You need to count the number of elements that appear before the current window. This will do the job:

a=list(range(5,35))
split = [a[i:i+10] for i in xrange(0, len(a), 10)] 

for ind,i in enumerate(split):
    j = max(i) 
    k = i.index(j)
    print (j,k+ind*10)

This prints

(14, 9)
(24, 19)
(34, 29)
Miriam Farber
  • 18,986
  • 14
  • 61
  • 76
5

A small modification to your current code:

a = some array of integers

split = [a[i:i+10] for i in xrange(0, len(a), 10)] 

for index, i in enumerate(split):
    j = max(i) 
    k = i.index(max(i))
    print (j, k+10*index)
Harald Nordgren
  • 11,693
  • 6
  • 41
  • 65
  • 1
    it seems everyone wants to calculate `j` twice, and I wince a little each time :P (not like it would have a real impact anyway...) – Aaron Aug 01 '17 at 20:52
  • Micro optimizations dude! But yes, you should re-use `j` in a production scenario. Here on Stack Overflow it's all about clarity and familiarity though. :) – Harald Nordgren Aug 01 '17 at 20:55
4

So with debugging with an example array, we find that split returns a 2d list like this one:

[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]]

And every time the for loop runs, it does through one of those lists in order. First it goes through the first inner list then the second one etc. So every time the for loop jumps into the next list, we simply add 10. Since the list can have over 2 lists in them, we store the number we need to add in a variable and add 10 to it every loop:

a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
split = [a[i:i+10] for i in xrange(0, len(a), 10)] 
counter = 0

for i in split:
    j = max(i) 
    k = i.index(max(i))
    print (j,k+counter)
    counter += 10

You can test it here

Anthony Pham
  • 3,096
  • 5
  • 29
  • 38
1

You will need to loop through in order to iterate through the list, however we could change your split's loop to make it more effective to what you want.

a = some array of integers

split = [a[i:i+10] for i in xrange(0, len(a), 10)] 

for i in range(len(split)):
    #Now instead of being the list, i is the index, so we can use 10*i as a counter
    j = max(split[i]) 
    #j = max(i) 
    k = split[i].index(j) + 10*i #replaced max(i) with j since we already calculated it.
    #k = i.index(max(i))
    print (j,k)

Though in the future, please make a new name for your split list since split is already a function in python. Perhaps split_list or separated or some other name that doesn't look like the split() function.

Davy M
  • 1,697
  • 4
  • 20
  • 27
  • @dawg there is no function named split.. only the method of strings, so no stomping. `whatever.split = something_else` would be a problem, but not this – Aaron Aug 01 '17 at 20:48
1

The toolz package has a partition_all function that divides a sequence up into equal-sized tuples, so you can do something like this.

import toolz
ns = list(range(25))
[max(sublist) for sublist in toolz.partition_all(10, ns)]

This will return [9, 19, 24].

W.P. McNeill
  • 16,336
  • 12
  • 75
  • 111
1

numpy solution for arbitrary input:

import numpy as np

a = np.random.randint(1,21,40)  #40 random numbers from 1 to 20

b = a.reshape([4,10])  #shape into chunks 10 numbers long

i = b.argsort()[:,-1]  #take the index of the largest number (last number from argsort) 
                       #  from each chunk. (these don't take into account the reshape)

i += np.arange(0,40,10)  #add back in index offsets due to reshape

out = zip(i, a[i])  #zip together indices and values
Aaron
  • 10,133
  • 1
  • 24
  • 40
1

You could simplify this by only enumerating once and using zip to partition your list into groups:

n=10
for grp in zip(*[iter(enumerate(some_list))]*n):
    grp_max_ind, grp_mv=max(grp, key=lambda t: t[1])
    k=[t[1] for t in grp].index(grp_mv)
    print grp_mv, (grp_max_ind, k)

Use izip in Python 2 if you want a generator (or use Python 3)

from itertools import izip 
for grp in izip(*[iter(enumerate(some_list))]*n):
    grp_max_ind, grp_mv=max(grp, key=lambda t: t[1])
    k=[t[1] for t in grp].index(grp_mv)
    print grp_mv, (grp_max_ind, k)

Zip will truncate the last group if not a length of n

dawg
  • 98,345
  • 23
  • 131
  • 206
1

An example using numpy. First let's generate some data, i.e., integers ranging from 1 to V and of length (number of values) L:

import numpy as np
V = 1000
L = 45 # method works with arrays not multiples of 10
a = np.random.randint(1, V, size=L)

Now solve the problem for sub-arrays of size N:

import numpy as np
N = 10 # example "split" size
sa = np.array_split(a, range(N, len(a), N))
sind = [np.argpartition(i, -1)[-1] for i in sa]
ind = [np.ravel_multi_index(i, (len(sa), N)) for i in enumerate(sind)]
vals = np.asarray(a)[np.asarray(ind)]
split_imax = zip(vals, ind) # <-- output
AGN Gazer
  • 8,025
  • 2
  • 27
  • 45