tags:

views:

180

answers:

3

Background

The algorithm manipulates financial analytics. There are multiple lists of the same size and they are filtered into other lists for analysis. I am doing the same filtering on different by parallel lists. I could set it up so that a1,b1,c2 occur as a tuple in a list but then the analytics have to stripe the tuples the other way to do analysis (regression of one list against the other, beta, etc.).

What I want to do

I want to generate two different lists based on a third list:

>>> a = list(range(10))
>>> b = list(range(10,20))
>>> c = list(i & 1 for i in range(10))
>>>
>>> aprime = [a1 for a1, c1 in zip(a,c) if c1 == 0]
>>> bprime = [b1 for b1, c1 in zip(b,c) if c1 == 0]
>>> aprime
[0, 2, 4, 6, 8]
>>> bprime
[10, 12, 14, 16, 18]

It seems there should be a pythonic/functional programming/itertools way to create the two lists and iterate over the three lists only once. Something like:

aprime, bprime = [a1, b1 for a1, b1, c1 in zip(a,b,c) if c1 == 0]

But of course this generates a syntax error.

The question

Is there a pythonic way?

Micro-optimization shootout

The ugly but pythonic-to-the-max one-liner edges out the "just use a for-loop" solution and my original code in the ever popular timeit cage match:

>>> import timeit
>>> timeit.timeit("z2(a,b,c)", "n=100;a = list(range(n)); b = list(range(10,10+n)); c = list(i & 1 for i in range(n));\ndef z2(a,b,c):\n\treturn zip(*[(a1,b1) for a1,b1,c1 in zip(a,b,c) if c1==0])\n")
26.977873025761482
>>> timeit.timeit("z2(a,b,c)", "n=100;a = list(range(n)); b = list(range(10,10+n)); c = list(i & 1 for i in range(n));\ndef z2(a,b,c):\n\taprime, bprime = [], [];\n\tfor a1, b1, c1 in zip(a, b, c):\n\t\tif c1 == 0:\n\t\t\taprime.append(a1);  bprime.append(b1);\n\treturn aprime, bprime\n")
32.232914169258947
>>> timeit.timeit("z2(a,b,c)", "n=100;a = list(range(n)); b = list(range(10,10+n)); c = list(i & 1 for i in range(n));\ndef z2(a,b,c):\n\treturn [a1 for a1, c1 in zip(a,c) if c1 == 0], [b1 for b1, c1 in zip(b,c) if c1 == 0]\n")
32.37302275847901
A: 

There's no way to create multiple lists at a time with list comprehensions--if you only want to iterate once you're going to need to do it some other way--possible with a loop.

You could use a list comprehension to create a list of tuples, with the first element belonging to one list, the second to the other. But if you do want them as separate lists, you're going to have to use another operation to split it, anyway.

Sam DeFabbia-Kane
+3  A: 

Just use a for loop:

aprime = []
bprime = []
for a1, b1, c1 in zip(a, b, c):
    if c1 == 0:
        aprime.append(a1) 
        bprime.append(b1)
DoR
+2  A: 

This might win the ugliest code award, but it works in one line:

aprime, bprime = zip(*[(a1,b1) for a1,b1,c1 in zip(a,b,c) if c1==0])
sepp2k
zip--unzip
kaizer.se
@kaizer.se: what does this comment mean?
hughdbrown
Maybe that zip can be used to unzip if you use an asterisk?zip(*zip(a,b)) == a,b
ACoolie
zip--unzip, it feels like it could be a python idiom but in the end I think it is rarely used; a for loop is clearer. By the way, you can use a generator expression instead of list comprehension (I'm not sure it matters here), and then there is itertools.izip if the lists are very long.
kaizer.se