Intermediate variable in a list comprehension for simultaneous filtering and transformation

dividebyzero picture dividebyzero · Nov 4, 2010 · Viewed 9.7k times · Source

I have a list of vectors (in Python) that I want to normalize, while at the same time removing the vectors that originally had small norms.

The input list is, e.g.

a = [(1,1),(1,2),(2,2),(3,4)]

And I need the output to be (x*n, y*n) with n = (x**2+y**2)**-0.5

If I just needed the norms, for example, that would be easy with a list comprehension:

an = [ (x**2+y**2)**0.5 for x,y in a ]

It would be also easy to store just a normalized x, too, for example, but what I want is to have this temporary variable "n", to use in two calculations, and then throw it away.

I can't just use a lambda function too because I also need the n to filter the list. So what is the best way?

Right now I am using this nested list comprehension here (with an expression in the inner list):

a = [(1,1),(1,2),(2,2),(3,4)]

[(x*n,y*n) for (n,x,y) in (( (x**2.+y**2.)**-0.5 ,x,y) for x,y in a) if n < 0.4]

# Out[14]: 
# [(0.70710678118654757, 0.70710678118654757),
#  (0.60000000000000009, 0.80000000000000004)]

The inner list generates tuples with an extra value (n), and then I use these values for the calculations and filtering. Is this really the best way? Are there any terrible inefficiencies I should be aware of?

Answer

Jochen Ritzel picture Jochen Ritzel · Nov 4, 2010
Is this really the best way?

Well, it does work efficiently and if you really, really want to write oneliners then it's the best you can do.

On the other hand, a simple 4 line function would do the same much clearer:

def normfilter(vecs, min_norm):
    for x,y in vecs:
        n = (x**2.+y**2.)**-0.5
        if min_norm < n:
            yield (x*n,y*n)

normalized = list(normfilter(vectors, 0.4))

Btw, there is a bug in your code or description - you say you filter out short vectors but your code does the opposite :p