Home » Python » Best way to find the intersection of multiple sets?

# Best way to find the intersection of multiple sets?

Questions:

I have a list of sets:

``````setlist = [s1,s2,s3...]
``````

I want s1 ∩ s2 ∩ s3 …

I can write a function to do it by performing a series of pairwise `s1.intersection(s2)`, etc.

Is there a recommended, better, or built-in way?

From Python version 2.6 on you can use multiple arguments to `set.intersection()`, like

``````u = set.intersection(s1, s2, s3)
``````

If the sets are in a list, this translates to:

``````u = set.intersection(*setlist)
``````

where `*a_list` is list expansion

Questions:

As of 2.6, `set.intersection` takes arbitrarily many iterables.

``````>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s3 = set([2, 4, 6])
>>> s1 & s2 & s3
set([2])
>>> s1.intersection(s2, s3)
set([2])
>>> sets = [s1, s2, s3]
>>> set.intersection(*sets)
set([2])
``````

Questions:

If you don’t have Python 2.6 or higher, the alternative is to write an explicit for loop:

``````def set_list_intersection(set_list):
if not set_list:
return set()
result = set_list[0]
for s in set_list[1:]:
result &= s
return result

set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print set_list_intersection(set_list)
# Output: set([1])
``````

You can also use `reduce`:

``````set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print reduce(lambda s1, s2: s1 & s2, set_list)
# Output: set([1])
``````

However, many Python programmers dislike it, including Guido himself:

About 12 years ago, Python aquired lambda, reduce(), filter() and map(), courtesy of (I believe) a Lisp hacker who missed them and submitted working patches. But, despite of the PR value, I think these features should be cut from Python 3000.

So now reduce(). This is actually the one I’ve always hated most, because, apart from a few examples involving + or *, almost every time I see a reduce() call with a non-trivial function argument, I need to grab pen and paper to diagram what’s actually being fed into that function before I understand what the reduce() is supposed to do. So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it’s better to write out the accumulation loop explicitly.

Questions:

Clearly `set.intersection` is what you want here, but in case you ever need a generalisation of “take the sum of all these”, “take the product of all these”, “take the xor of all these”, what you are looking for is the `reduce` function:

``````from operator import and_
from functools import reduce
print(reduce(and_, [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
``````

or

``````print(reduce((lambda x,y: x&y), [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
``````

Questions:

Here I’m offering a generic function for multiple set intersection trying to take advantage of the best method available:

``````def multiple_set_intersection(*sets):
"""Return multiple set intersection."""
try:
return set.intersection(*sets)
except TypeError: # this is Python < 2.6 or no arguments
pass

try: a_set= sets[0]
except IndexError: # no arguments
return set() # return empty set

return reduce(a_set.intersection, sets[1:])
``````

Guido might dislike `reduce`, but I’m kind of fond of it 🙂