List comprehensions...

2473
4
02-03-2016 04:00 AM
Labels (1)
DanPatterson_Retired
MVP Emeritus
4 4 2,473

List comprehensions (LCs)    Note:  Resurrected from another one of my places.

  • Do you wince when you use map and filter because you can't figure out LCs?
  • Do you rail against modernity and stick with for and if 's?
  • Do you always have to have that smoking one-liner?  Are you ashamed when you can't get it?
  • Did you know that you can put comments in LCs ?
  • Did you know that LCs can be split on to separate lines?  Is that sacrilege?
  • Which do you like better ... form (a), (b) or (c) ?

Basic LC forms

Traditional for loop

c = []
for i in range(20):
    if (i > 5) and (i < 10):
        c.append(i**2)

The smokin' cryptic one-liner

a = [i**2 for i in range(20) if i > 5 and i < 10]

The stacked, commented multi-liner

b = [i**2               # do this
     for i in range(20) # using these
     if (i > 5) and     # where this and
        (i < 10)        # this is good
     ]

Condition checking with numbers and conditions

Again, the three methods, but this time the condition checking is done upfront with an if-else clause.  This query is simply checking to see whether elements in a list are all numbers.  If True, return the number, if False, assign -999.

  nums = [0,1,None,3,None,5,6]   # the list of numbers to check

conventional approach

>>> good = []
>>> for val in nums:
...  if isinstance(val, (int,float)):
...   good.append(val)
...  else:
...   good.append(-999)
...

basic list comprehension

>>> good = [ val if isinstance(val,(int,float)) else -999 for val in nums]
>>> 

stacked list comprehension

>>> good = [
... val         # return the value
... if isinstance(val,(int,float))   # if this is true
... else -999   # return this otherwise
... for val in nums  # for each value in the list
... ]

in all cases

>>> good
[0, 1, -999, 3, -999, 5, 6]

More on condition checking

Some times you want to perform an operation given certain conditions.  In the example below, a check is made to ensure that "b" is not zero to avoid division by zero.  If it is zero, then an alternate value is supplied.

There are two variants of the LC shown and the results are compared as a final step...examine closely.

outer = [1,2]
inner = [2,0,4]
c = [[a, b, a*b, a*b/1.0]  # divide 2 numbers (outer/inner)
     if b                  # if != 0 (0 is a boolean False)
     else [a,b,a*b,"N/A"]  # if equal to zero, do this
     for a in outer        # for each value in the outer list
     for b in inner        # for each value in the inner list
     ]
for val in c:
    # val[0],val[1],val[2]))
    print("a({}), b({}), a*b({}) a/b({})".format(*val )) 

d = [[[a,b,a*b,"N/A"],           # do if False
      [a,b,a*b,a*b/1.0]][b!=0]   # do if True ... then slice
     for a in outer
     for b in inner
     ]
print("d == c??? {}".format(d==c))

a(1), b(2), a*b(2) a/b(2.0) 
a(1), b(0), a*b(0) a/b(N/A) 
a(1), b(4), a*b(4) a/b(4.0) 
a(2), b(2), a*b(4) a/b(4.0) 
a(2), b(0), a*b(0) a/b(N/A) 
a(2), b(4), a*b(8) a/b(8.0) 

d == c??? True

The examples shown here are just the beginning.  The reader should be aware that there are dictionary and set comprehensions as well.  This topic will also serve as a gentle introduction to generators.

If you would like to see other examples, please send me a note.

Additions

What is the difference between a list comprehension and a set comprehension.  Basically, a process step is the only thing sometimes.  As seen below, the set_comp results are completed during the query, whereas the list_comp results in line 04 still need to have a set() operation applied after the fact, should a set truly be required.

>>> a = [1,2,1,1,2,3,3,2,1,1,1,2,3,4,9,8,1,7,7,7,7,7]
>>> list_comp = [ i for i in a if i >3 ]
>>> set_comp = { i for i in a if i > 3 }
>>> list_comp
[4, 9, 8, 7, 7, 7, 7, 7]
>>> set_comp
set([8, 9, 4, 7])

>>> a = [5,6,5,6,5,6,5,6,5,6,4,4] 
>>> b = [5,6] 
>>> lc = [ x*y for x in a for y in b] 
>>> sc = { x*y for x in a for y in b } 
>>> lc [25, 30, 30, 36, 25, 30, 30, 36, 25, 30, 30, 
        36, 25, 30, 30, 36, 25, 30, 30, 36, 20, 24, 20, 24] 
>>> sc {24, 25, 36, 20, 30} 
>>>
4 Comments
About the Author
Retired Geomatics Instructor at Carleton University. I am a forum MVP and Moderator. Current interests focus on python-based integration in GIS. See... Py... blog, my GeoNet blog...
Labels