0

This code should find the mode of a list in O(n) linear time. I want to turn this into a list comprehension because I'm teaching myself Python, and am trying to improve my list comprehension skills.
These were informative but don't really answer my question:

Convert nested loops and conditions to a list comprehension

`elif` in list comprehension conditionals

Nested list comprehension equivalent

The problem that I'm running into is nesting the if's and the try/except. I'm sure this is simple question so a junior Python programmer might have the answer quickly.

def mode(L):
    # your code here
    d = {}; mode = 0; freq = 0
    for j in L:
        try:
            d[j] += 1
            if d[j] > freq:
                mode = j; freq = d[j]
        except(KeyError): d[j] = 1
    return mode

Note that L parameter is a list of ints like this:

L = [3,4,1,20,102,3,5,67,39,10,1,4,34,1,6,107,99]

I was thinking something like:

[try (d[j] += 1) if d[j] > freq (mode = j; freq = d[j]) except(KeyError): d[j] = 1 for j in L]

But I don't have enough duct tape to fix how badly the syntax is off with that thing.

Community
  • 1
  • 1
stackuser
  • 869
  • 16
  • 34

5 Answers5

6

I know you're learning comprehensions, but you can do this with a default dictionary, or a Counter too.

import collections
def mode(L):
    # your code here
    d = collections.defaultdict(lambda: 1); mode = 0; freq = 0
    for j in L:
            d[j] += 1
            if d[j] > freq:
                mode = j; freq = d[j]
    return mode

Better still, when you are not trying to learn comprehensions:

import collections
def mode(L):
    collections.Counter(L).most_common(1)[0][0]
SeanTater
  • 182
  • 1
  • 5
2

While it might not be possible directly do this within a list comprehension, there's also no reason to. You only really want to be checking for errors when you're actually retrieving the results. As such, you really want to use a generator instead of a list comprehension.

Syntax is largely the same, just using parens instead instead of brackets, so you would do something like this:

generator = (do something)
try:
    for thing in generator
except KeyError:
   etc...

That said, you really don't want to do this for you particular application. You want to use a counter:

from collections import Counter
d = Counter(L)
mode = Counter.most_common(1)[0]
Slater Victoroff
  • 21,376
  • 21
  • 85
  • 144
1

It's not possible to use try-except expressions in list comprenhension.

Quoting this answer:

It is not possible to handle exceptions in a list comprehension for a list comprehension is an expression containing other expression, nothing more (i.e., no statements, and only statements can catch/ignore/handle exceptions).

Edit 1:

What you could do instead of using the try-except clause, is use the get method from the dictionary:

def mode(L):
    d = {}
    mode = 0
    freq = 0
    for j in L:
        d[j] = d.get(j, 0) + 1
        if d[j] > freq:
            mode = j
            freq = d[j]
    return mode

From Python docs:

get(key[, default]): Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.

Edit 2:

This is my list comprenhension approach, not very efficient, just for fun:

r2 = max(zip(L, [L.count(e) for e in L]), key = lambda x: x[1])[0]
Community
  • 1
  • 1
Christian Tapia
  • 33,620
  • 7
  • 56
  • 73
  • I think you want `d[j] = d.get(j, 0) + 1`, with a default of zero rather than one. – Blckknght Dec 31 '13 at 19:55
  • Can you post a list comprehension of your solution? LC is really what I'm trying to improve skills on, and why I posted originally. – stackuser Dec 31 '13 at 21:43
  • I don't really know much about list comprenhension, but I cannot think in a way to do this with a list comprenhension (I mean in a similar way of the code you gave). Instead I tried another solution, not the most efficient. See edit. – Christian Tapia Dec 31 '13 at 22:36
1

You can't incorporate try: except: in a list comprehension. However, you can get around it by refactoring into a dict comprehension:

d = {i: L.count(i) for i in L}

You can then determine the maximum and corresponding key in a separate test. However, this would be O(n**2).

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
0

Since you're trying to find the value that appears most often, an easy way to do that is with max:

def mode(L):
   return max(L, key=L.count)

This is a bit less efficient than the other answers that suggest using collections.Counter (it is O(N^2) rather than O(N)), but for a modest sized list it will probably be fast enough.

Blckknght
  • 100,903
  • 11
  • 120
  • 169