tags:

views:

66

answers:

3

I have several lists having all the same number of entries (each specifying an object property):

property_a = [545., 656., 5.4, 33.]
property_b = [ 1.2,  1.3, 2.3, 0.3]
...

and list with flags of the same length

good_objects = [True, False, False, True]

(which could easily be substituted with an equivalent index list:

good_indices = [0, 3]

What is the easiest way to generate new lists property_asel, property_bsel, ... which contain only the values indicated either by the True entries or the indices?

property_asel = [545., 33.]
property_bsel = [ 1.2, 0.3]
+3  A: 

You could just use list comprehension:

property_asel = [val for is_good, val in zip(good_objects, property_a) if is_good]

or

property_asel = [property_a[i] for i in good_indices]

The latter one is faster because there are fewer good_indices than the length of property_a.

KennyTM
Does using `zip` here introduce a performance penalty?
fuenfundachtzig
@fuen: Yes. Causes a lot on Python 2 (use [itertools.izip](http://docs.python.org/library/itertools.html#itertools.izip) instead), not so much on Python 3. This is because the `zip` in Python 2 will create a new list, but on Python 3 it will just return a (lazy) generator.
KennyTM
OK, so I should stick to your 2nd proposal then, because this makes up the central part of my code.
fuenfundachtzig
@85: why are you worrying about performance? Write what you have to do, if it is slow, then test to find bottlenecks.
PreludeAndFugue
@PreludeAndFugue: If there are two equivalent options it's good to know which one is faster, and use that one right away.
fuenfundachtzig
I suspect the second is *slower*, because where did that good_indices list come from in the first place? Probably by enumerating over all of good_objects and saving the indices where good_objects[i] is True. So no savings after all, plus you had to build a second list. Use the first option, with izip in Py2 or zip in Py3, read both lists once, and directly create the desired output with no intermediate lists.
Paul McGuire
You can just use `from itertools import izip` and use that instead of `zip` in the first example. That creates an iterator, same as Python 3.
Chris B.
@Paul McGuire: You're right, I'm looping over the properties and applying some tests to figure out which objects are good. So in principle it would be possible to build the lists directly in that loop. This is also probably the fastest way.
fuenfundachtzig
+2  A: 

I see 2 options.

  1. Using numpy:

    property_a = numpy.array([545., 656., 5.4, 33.])
    property_b = numpy.array([ 1.2,  1.3, 2.3, 0.3])
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = property_a[good_objects]
    property_bsel = property_b[good_indices]
    
  2. Using a list comprehension and zip it:

    property_a = [545., 656., 5.4, 33.]
    property_b = [ 1.2,  1.3, 2.3, 0.3]
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = [x for x, y in zip(property_a, good_objects) if y]
    property_bsel = [property_b[i] for i in good_indices]
    
WoLpH
Use 8 spaces to format code within a list.
KennyTM
Thanks Kenny :)I was wondering why it wasn't working ;)
WoLpH
Using Numpy is a good suggestion since the OP seems to want to store numbers in lists. A two-dimensional array would be even better.
Philipp
+2  A: 

Use the built in function zip

property_asel = [a for (a, truth) in zip(property_a, good_objects) if truth]

EDIT

Just looking at the new features of 2.7. There is now a function in the itertools module which is similar to the above code.

http://docs.python.org/library/itertools.html#itertools.compress

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F
PreludeAndFugue
I'm underwhelmed by the use of `itertools.compress` here. The list comprehension is *far* more readable, without having to dig up what the heck compress is doing.
Paul McGuire
Hm, I find the code using compress much more readable :) Maybe I'm biased, because it does exactly what I want.
fuenfundachtzig