stackuser
stackuser

Reputation: 869

Python -- list comprehension with try/exception and nested conditional

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.

Upvotes: 0

Views: 2634

Answers (5)

Christian Tapia
Christian Tapia

Reputation: 34166

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]

Upvotes: 1

Blckknght
Blckknght

Reputation: 104772

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.

Upvotes: 0

jonrsharpe
jonrsharpe

Reputation: 122106

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).

Upvotes: 1

Slater Victoroff
Slater Victoroff

Reputation: 21914

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]

Upvotes: 2

SeanTater
SeanTater

Reputation: 182

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]

Upvotes: 6

Related Questions