How to order an array and count it in Python?

Viewed 554

I want to find only the top 3 distinct items in descending order. If there's a tiebreaker, sort by alphabetical order. If there are 3 items or fewer, returning the distinct list of items is sufficient.

So if I have input of: ["a","a","b","b","c","c","c","d","d","d","d"]

The output will be ["d","c","a"]

Because d has 4 counts, c 3 counts, a and b have the same frequency, but a is alphabetically first.

In MySQL, I would usually use this:

SELECT id, COUNT(*) as frequency FROM mylist GROUP BY id ORDER BY frequency, id

How can I do that in Python?

I use this code based on SAI SANTOH CHIRAG's solution:

def main(output):
    arr = sorted(output,key=lambda i:[output.count(i),-ord(i)],reverse=True)
    out = []
    for i in arr: 
        if i not in out: out.append(i) 
        print(out[:3])

but why is the result like this:

Input (stdin) = a a a b b c d d d d
output = ['d']
['d']
['d']
['d']
['d', 'a']
['d', 'a']
['d', 'a']
['d', 'a', 'b']
['d', 'a', 'b']
['d', 'a', 'b']

instead of what I want, which would be:

['d','a','b']
4 Answers

You use sorted and key for that. Try in this way:

arr = sorted(x,key=lambda i:[x.count(i),-ord(i)],reverse=True)

With this you get all the elements in the sorted order in the increase of count and then alphabetical order. Then do this to get all elements only once:

out = []
for i in arr:
    if i not in out:
        out.append(i)
print(out[:3])

collections.Counter will do:

the_list = ["a","a","b","b","c","c","c","d","d","d","d"]
counter = Counter(sorted(the_list))
top_3 = counter.most_common(3)

at this point, top_3 is of the form [(<entry>, <freq>)] e.g.

[('d', 4), ('c', 3), ('a', 2)]

Take out the first elements from it via list comprehension:

result = [item for item, freq in top_3]

and we get

['d', 'c', 'a']

Notes:

  1. We pass the sorted list to the Counter because otherwise it will break the ties according to the insertion order; sorting forces the insertion order to be the alphabetical order in a way.

  2. .most_common(3) will return at most 3 elements so we are fine e.g. even if only 2 unique entries are there. E.g. if the_list = ["b", "a"], result will be ["a", "b"] even though number of unique elements is less than 3.

you can use Counter from collections

from collections import Counter

inputs = ["a","a","b","b","c","c","c","d","d","d","d"]
counts = Counter(x)
counts.most_common(3)

final = [i[0] for i in counts.most_common]

output for counts.most_common()

[('d', 4), ('c', 3), ('a', 2), ('b', 2)]

Simpler and more efficient than the accepted answer.

>>> a = ["a","a","b","b","c","c","c","d","d","d","d"]

>>> sorted(set(a), key=lambda s: (-a.count(s), s))[:3]
['d', 'c', 'a']

This removes duplicates first and thus counts each string only once. Also, much better to simply negate the count instead of the character code and using reverse sorting. If the strings had multiple characters, you couldn't even use the character code at all.

Another, using two simpler sorts (might actually be faster):

>>> sorted(sorted(set(a)), key=a.count, reverse=True)[:3]
['d', 'c', 'a']
Related