9

With

input = [0,0,5,9,0,4,10,3,0]

as list I need an output, which is going to be two highest values in input while setting other list elements to zero.

output = [0,0,0,9,0,0,10,0,0]

The closest I got:

from itertools import compress
import numpy as np
import operator
input= [0,0,5,9,0,4,10,3,0]
top_2_idx = np.argsort(test)[-2:]
test[top_2_idx[0]]
test[top_2_idx[1]]

Can you please help?

2

5 Answers 5

8

You can sort, find the two largest values, and then use a list comprehension:

input = [0,0,5,9,0,4,10,3,0]
*_, c1, c2 = sorted(input)
result = [0 if i not in {c1, c2} else i for i in input]

Output:

[0, 0, 0, 9, 0, 0, 10, 0, 0]
Sign up to request clarification or add additional context in comments.

2 Comments

While it's neat, this is O(n log(n)), but can be done in O(n) with a single traversal
@Ajax1234 @Olivier For an O(n) solution you could use heapq.nlargest rather than sorted.
5

Not as pretty as Ajax's solution but a O(n) solution and a little more dynamic:

from collections import deque

def zero_non_max(lst, keep_top_n):
    """
    Returns a list with all numbers zeroed out
    except the keep_top_n.
    >>> zero_non_max([0, 0, 5, 9, 0, 4, 10, 3, 0], 3)
    >>> [0, 0, 5, 9, 0, 0, 10, 0, 0]
    """

    lst = lst.copy()

    top_n = deque(maxlen=keep_top_n)

    for index, x in enumerate(lst):
        if len(top_n) < top_n.maxlen or x > top_n[-1][0]:
            top_n.append((x, index))
        lst[index] = 0

    for val, index in top_n:
        lst[index] = val

    return lst

lst = [0, 0, 5, 9, 0, 4, 10, 3, 0]
print(zero_non_max(lst, 2))

Output:

[0, 0, 0, 9, 0, 0, 10, 0, 0]

Comments

1

Pure numpy approach:

import numpy as np

arr = np.array([0, 0, 5, 9, 0, 4, 10, 3, 0])
top_2_idx = np.argsort(arr)[-2:]
np.put(arr, np.argwhere(~np.isin(arr, arr[top_2_idx])), 0)
print(arr)

The output:

[ 0  0  0  9  0  0 10  0  0]

Numpy.put

Comments

1

It's possible to achieve this with a single list traversal, making the algorithm O(n):

  • First find the two highest values with a single traversal;

  • Then create a list of zeros and add in the found maxima.

Code

def two_max(lst):
    # Find two highest values in a single traversal
    max_i, max_j = 0, 1
    for i in range(len(lst)):
        _, max_i, max_j = sorted((max_i, max_j, i), key=lst.__getitem__)

    # Make a new list with zeros and replace both maxima
    new_lst = [0] * len(lst)
    new_lst[max_i], new_lst[max_j] = lst[max_i], lst[max_j]

    return new_lst


lst = [0, 0, 5, 9, 0, 4, 10, 3, 0]

print(two_max(lst)) # [0, 0, 0, 9, 0, 0, 10, 0, 0]

Note that if the maximum value in the list appears more than twice, only the two left-most values will appear.

As a sidenote, do not use names such as input in your code as this overshadows the built-in function of the same name.

Comments

1

Here is another numpy-based solution that avoids sorting the entire array, which takes O(nlogn) time.

import numpy as np

arr = np.array([0,0,5,9,0,4,10,3,0])
arr[np.argpartition(arr,-2)[:-2]] = 0

If you want to create a new array as output:

result = np.zeros_like(arr)
idx = np.argpartition(arr,-2)[-2:]
result[idx] = arr[idx]

A corresponding Python-native solution is to use heap.nlargest, which also avoids sorting the entire array.

import heapq

arr = [0,0,5,9,0,4,10,3,0]
l = len(arr)
idx1, idx2 = heapq.nlargest(2, range(l), key=arr.__getitem__)
result = [0] * l
result[idx1] = arr[idx1]
result[idx2] = arr[idx2]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.