Map || Filter || Reduce in Python

Python lambda (Anonymous Function)

In Python, anonymous function implies that a function is without a name. As we definitely realize that def keyword is utilized to define the ordinary functions and the lambda keyword is utilized to make unknown functions. It has the accompanying syntax:

lambda arguments: expression
  • This function can have any number of arguments but only one expression, which is evaluated and returned.
  • One is free to use lambda functions wherever function objects are required.
  • You need to keep in your knowledge that lambda functions are syntactically restricted to a single expression.
  • It has various uses in particular fields of programming besides other types of expressions in functions.

Let’s look at this example and try to understand the difference between a normal def defined function and lambda function. This is a program that returns the cube of a given value:

def square(y): 
    return y*y 
  
g = lambda x: x*x
print(g(7)) 
  
print(square(5)) 

#output
#49
#25

What is Map, Filter and Reduce in Python?

Map, Filter, and Reduce are paradigms of functional programming. They allow the programmer (you) to write simpler, shorter code, without necessarily needing to bother about intricacies like loops and branching.

Essentially, these three functions allow you to apply a function across a number of iterables, in one full swoop. map and filter come built-in with Python (in the __builtins__ module) and require no importing. reduce, however, needs to be imported as it resides in the functional module. Let’s get a better understanding of how they all work, starting with map.

map()

Suppose you have a list containing some numbers:

numb = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Imagine you now want to obtain a new list that contains the numbers from the list above multiplied by 2.

You can of course always pass the numbers one by one and collect the output in a for loop:

doubled_num = [2*n for n in num]
 
print(doubled_num)

#output
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

However, there exists another way to do so with the help of the built-in map() function. Let’s take a look at its syntax:

map(function, iterable)

map() takes a function object and a list (or some other iterable, e.g., an array or a dictionary). The function is then applied to every element of that list, and an iterator is returned. You can explicitly convert it to a list utilizing the list() function to see the outcome.

So the code from the example above can be re-composed as follows:

def doubled(x):
    return 2*x
 
doubled_num = map(doubled, num)
 
print(list(doubled_num))

#output
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Note that we can always combine map() with lambda functions:

doubled_num = map(lambda x: 2*x, num)
 
print(list(doubled_num))

#output
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

map() proves to be useful when a function you need to apply several times takes various arguments. All things considered, you can simply pass more than one list to map(). The values for every argument will be then taken from the corresponding list.

The code below, for instance, figures the sum of three list:

x_list = [1, 2, 3] 
y_list = [4, 5, 6]
z_list = [7, 8, 9] 
 
s = list(map(lambda x, y, z: x + y + z, x_list, y_list, z_list))
 
print(s)

#output
#[12, 15, 18]

filter()

Let’s continue working with our list of numbers from 0 to 9:

num = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Imagine you need to obtain one more list which contains just the odd numbers from the above list.

With that in mind, the built-in filter() function proves to be useful.

filter() takes a Boolean function, i.e., a function that returns True or False, alongside a list, and constructs another iterator from the elements of the list for which the function returns True. This iterator can be then changed over to a list. The syntax is as per the following:

filter(boolean_function, iterable)

Thus, we can collect all the odd numbers from our list in the following way:

odd_num = filter(lambda x: x % 2, num)
 
print(list(odd_num))

#output
#[1, 3, 5, 7, 9]

As you might have already noticed, you can do the same thing in a for loop:

odd_num = [n for n in num if n % 2]
 
print(odd_num)

#output
#[1, 3, 5, 7, 9]

map() and filter() or list comprehensions?

As you have already seen, map() and filter() can generally be replaced by list comprehensions. All in all, what should be preferred?

Despite the fact that Python implements the functional programming functionality, for example, map() and filter(), is anything but a functional programming language. Consequently, numerous developers argue that more ‘pythonic’ ways should to be favored where conceivable. Additionally, a few developers discover list comprehensions progressively clear and straightforward.

Simultaneously, under certain conditions, map() and filter() can be quicker than list comprehensions, so it’s very worth remembering them.

reduce()

reduce applies a function of two arguments cumulatively to the elements of an iterable, optionally starting with an initial argument. It has the following syntax:

reduce(func, iterable[, initial])

Where func is the function on which every element in the iterable gets cumulatively applied to, and initial is the optional value that gets placed before the elements of the iterable in the calculation, and fills in as a default when the iterable is empty. The accompanying should be noted about reduce():

  • func requires two arguments, the first is the first element in iterable (if beginning isn’t provided) and the second the second component in iterable. If initial is provided, at that point it turns into the first argument to func and the first element in iterable turns into the second element.
  • reduce “reduces” iterable into a single worth.

Let’s consider an example using the built-in Python function sum()

from functools import reduce

num = [13, 44, 676, 9, 34, 12]

def custom_sum(first, second):
    return first + second

result = reduce(custom_sum, num)
print(result)

#output
#788

As usual, it’s everything about iterations: reduce takes the first and second elements in numbers and passes them to custom_sum. custom_sum computes their sum and returns it to reduce. reduce then takes that result and applies it as the first element to custom_sum and takes the following element (third) in numbers as the second element to custom_sum. It does this continuously (aggregately) until numbers is depleted.

We should perceive what happens when I utilize the optional initial value.

from functools import reduce

numbers = [13, 44, 676, 9, 34, 12]

def custom_sum(first, second):
    return first + second

result = reduce(custom_sum, numbers, 10)
print(result)

#output
#798

The result, as you’ll expect, is 78 because reduce, initially, uses 10 as the first argument to custom_sum.

Conclusion

  • map() and filter() come from the functional programming paradigm.
  • map() evaluates some function on multiple parameter values in a list.
  • filter() finds a subset of elements of a list that satisfy some condition.
  • map() and filter() can be replaced with list comprehensions. However, sometimes they can be more computationally efficient.

Now, try to solve a problem

You are given a list my_list that contains both negative and positive numbers.

Define a function find_positive(my_list) that would construct and return a new list containing only those numbers from my_list that are greater than 0. Zero itself should not be included in the new list.

Make use of map() or filter() where possible.

def find_positive(my_list):
    
    return list(filter(lambda x: x > 0, my_list))