Python - Failed Refactoring for-loop into list comprehension - Suggestions?

Hello,

I was trying to practice using list comprehensions after looking over section 5.6. (Looping Techniques) of the Python documentation:

import math
raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
filtered_data = []
for value in raw_data:
    if not math.isnan(value):
        filtered_data.append(value)

filtered_data
#  prints [56.2, 51.7, 55.3, 52.5, 47.8]

In Repl.it, when I tried to refactor the above loop into a list comprehension, but then this happened:

list_comp = [filtered_data.append(value) for value in raw_data if not math.isnan(value)]

print(list_comp) # prints [None, None, None, None, None]

I tried searching online for reasons why this code doesn’t print as I expect it to, but I can’t seem to come up with anything. My guess is that it has something to do with the not segment, but even it that is true, I’m still not sure why/how that is the case.

Would anyone with a better understanding of list comprehensions be so kind as to explain to me what I’ve done incorrectly? I am 13% into the Data Science track btw, just finished the python loops module.

We cannot perform an append action. It is not an expression.

Consider how the filter() function works (hopefully you’ve seen this already; we’re only looking for logic to model after)…

>>> from math import isnan
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = [*filter(lambda x: not isnan(x), raw_data)]
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
>>> 

This is partially implemented in your code, but for the expression. Remember, we are working from inside of a list structure. Results are added to the list.

>>> filtered_data = [x for x in raw_data if not isnan(x)]
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
>>> 

See how that works?

Both models work with the same predicate, not isnan(x), and surprisingly enough they are both comprehensions (of a sort), though the list comprehension is slicker, and likely faster.

1 Like

This made it so clear. Thanks for pointing me in the right direction. So, to my understanding (and please correct me):

The Goal: Return a list that does not include NaN values.

  • There is no need to append the list because elements are being added to the defined list comprehension as the reference list is being looped through.
  • But that’s only so long as the condition returns true (like one might need when using a filter() function argument
  • If .isnan() is already being imported so there’s no need to reference math again in the condition

So as tested:

from math import isnan
raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
list_comp = [value for value in raw_data if not isnan(value)]

print(list_comp)  # prints [56.2, 51.7, 55.3, 52.5, 47.8]

Thanks so much! Now onto understanding Lambda.

1 Like

It’s an anonymous function. Don’t get sidetracked, but, yeah, this is good to learn. We can do a lot with expressions, aka, declarative programming.

1 Like

Just to add, the difference between a defined function and a lambda is that we defined the function in the source code, there’s no going back during run time. A lambda is defined during run time, with the parameters we resolve in our running logic. We derive a function from the existing states.

Let’s say we have an arc question. There are two variables, the angle in radians, and the radius. We want to break this down to a function with a fixed angle into which we can give radius values. That means factory function.

1 Like

I will refer back to this as I learn. Thank for the knowledge! :grinning_face_with_smiling_eyes:

1 Like
>>> from math import pi as PI  # alias as a GLOBAL_CONSTANT
>>> def arc_factory(theta):
    return lambda r: r * theta

>>> two_pi = arc_factory(2 * PI)
>>> circumference = two_pi(1)
>>> circumference
6.283185307179586
>>> 
1 Like

It follows we can fix the radius and let the angle become the variable…

>>> def arc_r_factory(r):
	return lambda theta: r * theta

>>> one_r = arc_r_factory(1)
>>> one_r(PI)
3.141592653589793
>>> 
1 Like

How handy. Yeah I definitely went down the rabbit hole into lambda calculus and what that might mean for programing. :smiley: Being able to call a function that houses a lambda function to my understanding means one could spits out different outputs depending on what is input, and how you customize how that input is processed. Nested lambda functions are possible apparently - I think its called currying - which I think could be crazy useful.

I still have a looooong way to got in terms of understanding and seeing its full utility - but so far it looks really cool.

1 Like