Kodeclik Blog
Python Reduce Function
Like the Python map function, Python’s reduce function is easy to motivate. One key difference is that while map is built-in into Python, to get the reduce functionality you will need to import a Python module, like functools.
Summing numbers in a list with reduce()
Let us consider a simple application. Assume we want to sum the numbers in a list. We can write a simple for loop such as:
mylist = [2,3,6,9]
sum = 0
for i in mylist:
sum = sum + i
print(sum)
This produces the output:
20
This task can be elegantly achieved using Python reduce. Here is how that would work.
from functools import reduce
def sum(x,y):
return(x+y)
mylist = [2,3,6,9]
print(reduce(sum,mylist))
The output is:
20
Note that we are passing two arguments to reduce(). The first is the function sum which adds two numbers. The second is the list whose elements we desire to add. Even though sum takes only two elements, reduce() applies it incrementally over the full list, accumulating the partial sums, and finally arriving at the answer of 20.
Let us try to use reduce with an empty list:
from functools import reduce
def sum(x,y):
return(x+y)
mylist = [2,3,6,9]
print(reduce(sum,[]))
This produces:
Traceback (most recent call last):
File "main.py", line 7, in <module>
print(reduce(sum,[]))
TypeError: reduce() of empty sequence with no initial value
Woah! What happened? As the error message indicates, given an empty list, the reduce() function is not sure what to output because you haven’t specified any initial value. You should use a third argument to specify an initial, default, value. Consider the following program:
from functools import reduce
def sum(x,y):
return(x+y)
mylist = [2,3,6,9]
print(reduce(sum,[],3))
print(reduce(sum,mylist,3))
This outputs:
3
23
In both applications of reduce() above, just for illustration purposes, we use 3 as the default, initial, value. This is why with an empty list, we get 3 as the answer. With the list mylist, 3 is added to the sum of the elements in the list, giving 23 (instead of 20).
Finding the maximum in a list with reduce()
Let us adapt the same concepts and use reduce() to find the maximum elements in a list. Remember that the function that is used as argument to reduce() takes just two arguments. In our case, this must be a function that takes two elements and returns the greater element.
from functools import reduce
def max(x,y):
if (x>y):
return (x)
else:
return (y)
mylist = [3, 15, -1, 8, 29, 4]
print(reduce(max,mylist))
This outputs:
29
Note that, again, this will not work if you give an empty list. You will need to adopt a “default” convention.
Summing squares of numbers using reduce()
Let us use reduce() to take a list of numbers and find the sum of squares of the numbers in the list. Here’s how it would work. You first write a function that takes two numbers and outputs the sum of squares of just these two numbers. Then you use reduce() to apply it to the full list. Here is how that might work:
from functools import reduce
def sum_squares(x,y):
return(x*x + y*y)
mylist = [1,2,3,4]
print(reduce(sum_squares,mylist))
This outputs:
1172
Woah! Something went wrong. The correct answer should be 30. Let us try to debug it. Let us try it with just a singleton element list:
from functools import reduce
def sum_squares(x,y):
return(x*x + y*y)
mylist = [5]
print(reduce(sum_squares,mylist))
This outputs:
5
This is still incorrect! What is happening?
The reason this is happening is because when you give a list containing non-zero elements, the first element’s value is used as the initial setting. Then the function is iterated over the remaining items in the list. In this case, there are no other items, so the function exits. As a result, you obtain the result 5. We should instead specify an initial value of 0:
from functools import reduce
def sum_squares(x,y):
return(x*x + y*y)
mylist = [5]
print(reduce(sum_squares,mylist,0))
This works. It outputs:
25
as expected. Let us try it with 2 elements.
from functools import reduce
def sum_squares(x,y):
return(x*x + y*y)
mylist = [1,2]
print(reduce(sum_squares,mylist,0))
This outputs (as expected):
5
Let us now try it with 3 elements:
from functools import reduce
def sum_squares(x,y):
return(x*x + y*y)
mylist = [1,2,3]
print(reduce(sum_squares,mylist,0))
This outputs:
34
Aha - this is where things go wrong! The reason you get 34 is that after computing the partial sum of 5, the sum_squares() function is called with arguments 5 and the remaining element in the list, which is 3. As a result you get 25+9=34.
So we really need to write our function as follows:
from functools import reduce
def sum_squares(x,y):
return(x + y*y)
mylist = [1,2,3]
print(reduce(sum_squares,mylist,0))
We get the output we expected:
14
Try it also with an empty list, singleton list, and so on.
If you have seen our blogpost on Python's map() function, you can combine that with reduce to directly take a list of numbers, square each of them, and then sum them up like so:
from functools import reduce
def square(x):
return(x*x)
def sum(x,y):
return (x+y)
mylist = [1,2,3]
print(reduce(sum,map(square,mylist)))
This outputs, again:
14
In more detail, the above code first uses the map() function to apply the square() function on each element of the list. This yields another list which is used as the second argument to the reduce() function. The first argument to reduce() is the sum() function.
Using Python reduce to compute the factorial
Here is a program that uses reduce() to compute the factorial of a number.
from functools import reduce
n=10
print(reduce(lambda x, y: x * y,range(1,n+1)))
Note that we are using a lambda function and the second argument to the reduce() function is a list of numbers from 1 to the number we wish to compute the factorial of (which is “n”). Recall that we need to use n+1 as the argument to range because the end range parameter is not inclusive. This program outputs 10! As expected:
120
So what have we learnt in this blog post? The reduce() function is a way to distill all the elements of an iterable (like a list) into a single value by repeatedly applying a function, and aggregating the partial results into the final answer. If you liked this blogpost, learn also about Python's sum() function and the Python range() function. Closely related is Python numpy's sum() function. Also see how to use these concepts to flatten a nested list in Python.
Interested in more things Python? See our blogposts on Python's enumerate() and Python pointers. Also if you like Python+math content, see our blogpost on Magic Squares. Finally, master the Python print function!
Want to learn Python with us? Sign up for 1:1 or small group classes.