Reputation: 1541
Suppose we have a list: [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
I want to write a function that returns the indices of the first 3 maximum values in order.
For example in this case the results would be: [8, 5, 7]
I know one way is to write nested-loops, however, can there be any other effective way to achieve this?
Upvotes: 2
Views: 170
Reputation: 1283
While all 6 answers (right now available after ~24h) are effective in providing the requested result I was wondering which solution is the most efficient one.
I used timeit
for the comparison (only runtime, no memory usage, 5k runs with an increased input list - the same for all).
Results in increasing order:
consecutive_max: 2.604 s
heapq_oneStep: 3.052 s
np_argsort: 3.234 s
lambda_unnamed: 6.182 s
heapq_nlargest: 4.522 s
lambda_function: 9.960 s
zip_sort: 15.402 s
Code of the evaluation:
import timeit
timeit_runs = 5_000
# heapq_oneStep by Mechanic Pic
# https://stackoverflow.com/a/73568080/11815313
def heapq_oneStep():
timeit_setup_heapq_oneStep = '''
import heapq
import random
random.seed(42)
integer_list = random.sample(range(-10_000, 10_000), 10_000)
lst = [x/100 for x in integer_list]
'''
testcode_heapq_oneStep = '''
heapq.nlargest(3, range(len(lst)), lst.__getitem__)
'''
return timeit.timeit(stmt=testcode_heapq_oneStep,
setup=timeit_setup_heapq_oneStep, number = timeit_runs)
# heapq_nlargest by Stef
# https://stackoverflow.com/a/73567876/11815313
def heapq_nlargest():
timeit_setup_heapq_nlargest = '''
from heapq import nlargest
from operator import itemgetter
import random
random.seed(42)
integer_list = random.sample(range(-10_000, 10_000), 10_000)
lst = [x/100 for x in integer_list]
'''
testcode_heapq_nlargest = '''
[i for i,_ in nlargest(3, enumerate(lst), itemgetter(1))]
'''
return timeit.timeit(stmt=testcode_heapq_nlargest,
setup=timeit_setup_heapq_nlargest, number = timeit_runs)
# zip_sort by Guy
# https://stackoverflow.com/a/73567874/11815313
def zip_sort():
timeit_setup_zip_sort = '''
import random
random.seed(42)
integer_list = random.sample(range(-10_000, 10_000), 10_000)
lst = [x/100 for x in integer_list]
'''
testcode_zip_sort = '''
[i for _, i in sorted(zip(lst, range(len(lst))), reverse=True)[:3]]
'''
return timeit.timeit(stmt=testcode_zip_sort,
setup=timeit_setup_zip_sort, number = timeit_runs)
# lambda_function by Mehmaam
# https://stackoverflow.com/a/73568027/11815313
def lambda_function():
timeit_setup_lambda_function = '''
import random
random.seed(42)
integer_list = random.sample(range(-10_000, 10_000), 10_000)
lst = [x/100 for x in integer_list]
'''
testcode_lambda_function = '''
def sort_index(lst, rev=True):
index = range(len(lst))
s = sorted(index, reverse=rev, key=lambda i: lst[i])
return s
sort_index(lst)[:3]
'''
return timeit.timeit(stmt=testcode_lambda_function,
setup=timeit_setup_lambda_function, number = timeit_runs)
# lambda_unnamed by uozcan12
# https://stackoverflow.com/a/73569339/11815313
def lambda_unnamed():
timeit_setup_lambda_unnamed = '''
import random
random.seed(42)
integer_list = random.sample(range(-10_000, 10_000), 10_000)
lst = [x/100 for x in integer_list]
'''
testcode_lambda_unnamed = '''
list(map(lambda x: lst.index(x), sorted(lst, reverse=True)[:3] ))
'''
return timeit.timeit(stmt=testcode_lambda_unnamed,
setup=timeit_setup_lambda_unnamed, number = timeit_runs)
# np_argsort by MagnusO_O
# https://stackoverflow.com/a/73567884/11815313
def np_argsort():
timeit_setup_np_argsort = '''
import numpy as np
import random
random.seed(42)
integer_list = random.sample(range(-10_000, 10_000), 10_000)
lst = [x/100 for x in integer_list]
lst_nparr = np.array(lst)
'''
testcode_np_argsort = '''
lst_nparr_max3 = np.argsort(lst_nparr)[::-1][0:3]
lst_nparr_max3.tolist()
'''
return timeit.timeit(stmt=testcode_np_argsort,
setup=timeit_setup_np_argsort, number = timeit_runs)
# consecutive_max by cards
def consecutive_max():
timeit_setup_consecutive_max = '''
import random
random.seed(42)
integer_list = random.sample(range(-10_000, 10_000), 10_000)
lst = [x/100 for x in integer_list]
'''
testcode_consecutive_max = '''
lst_cp = lst.copy()
indeces_max = []
for _ in range(3):
m = max(lst_cp)
indeces_max.append(lst.index(m))
lst_cp.remove(m)
'''
return timeit.timeit(stmt=testcode_consecutive_max,
setup=timeit_setup_consecutive_max, number = timeit_runs)
time_heapq_oneStep = heapq_oneStep()
time_heapq_nlargest = heapq_nlargest()
time_zip_sort = zip_sort()
time_lambda_function = lambda_function()
time_lambda_unnamed = lambda_unnamed()
time_np_argsort = np_argsort()
time_consecutive_max = consecutive_max()
print(f'''consecutive_max: {time_consecutive_max:.3f} s''')
print(f'''np_argsort: {time_np_argsort:.3f} s''')
print(f'''heapq_oneStep: {time_heapq_oneStep:.3f} s''')
print(f'''lambda_unnamed: {time_lambda_unnamed:.3f} s''')
print(f'''heapq_nlargest: {time_heapq_nlargest:.3f} s''')
print(f'''lambda_function: {time_lambda_function:.3f} s''')
print(f'''zip_sort: {time_zip_sort:.3f} s''')
Upvotes: 0
Reputation: 4975
Using consecutive max
to a copy of the list.
lst = [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
lst_cp = lst.copy()
indeces_max = []
for _ in range(3):
m = max(lst_cp)
indeces_max.append(lst.index(m))
lst_cp.remove(m)
del lst_cp # just to remember that is not needed
print(indeces_max)
Upvotes: 1
Reputation: 427
l = [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
list(map(lambda x: l.index(x), sorted(l, reverse=True)[:3] )) # [8, 5, 7]
Upvotes: 1
Reputation: 1283
You could use np.argsort
:
import numpy as np
ar = [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
ar_argsort = np.argsort(ar)
reversed_argsort = ar_argsort[::-1]
indices_3_max = reversed_argsort[0:3]
print(indices_3_max)
Result:
[8 5 7]
Concise version of above in one line:
ar_argsort = np.argsort(ar)[::-1][0:3]
Upvotes: 0
Reputation: 7736
This can generate the result in one step, but the less elegant dunder method is used as the key:
>>> lst = [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
>>> heapq.nlargest(3, range(len(lst)), lst.__getitem__)
[8, 5, 7]
Upvotes: 2
Reputation: 573
lambda function do this things easier.
Code:
def sort_index(lst, rev=True):
index = range(len(lst))
s = sorted(index, reverse=rev, key=lambda i: lst[i])
return s
score = [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
sort_index(score)[:3]
Result:
[8, 5, 7]
Upvotes: 1
Reputation: 15505
Use heapq.nlargest
to find the n largest values.
from heapq import nlargest
from operator import itemgetter
l = [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
indices_of_3_largest = [i for i,_ in nlargest(3, enumerate(l), itemgetter(1))]
print(indices_of_3_largest)
# [8, 5, 7]
Upvotes: 3
Reputation: 50864
zip
the list with the indices, sort it, and take the first 3 values
lst = [1, 3.5, -1, 7, 10, 20, 5, 17, 31, -5]
indices = [i for _, i in sorted(zip(lst, range(len(lst))), reverse=True)[:3]]
print(indices) # [8, 5, 7]
Upvotes: 2