Opposite of set.intersection in python? - python

In Python you can use a.intersection(b) to find the items common to both sets.
Is there a way to do the disjoint opposite version of this? Items that are not common to both a and b; the unique items in a unioned with the unique items in b?

You are looking for the symmetric difference; all elements that appear only in set a or in set b, but not both:
a.symmetric_difference(b)
From the set.symmetric_difference() method documentation:
Return a new set with elements in either the set or other but not both.
You can use the ^ operator too, if both a and b are sets:
a ^ b
while set.symmetric_difference() takes any iterable for the other argument.
The output is the equivalent of (a | b) - (a & b), the union of both sets minus the intersection of both sets.

a={1,2,4,5,6}
b={5,6,4,9}
c=(a^b)&b
print(c) # you got {9}

The best way is a list comprehension.
a = [ 1,2,3,4]
b = [ 8,7,9,2,1]
c = [ element for element in a if element not in b]
d = [ element for element in b if element not in a]
print(c)
# output is [ 3,4]
print(d)
# output is [8,7,9]
You can join both lists

Try this code for (set(a) - intersection(a&b))
a = [1,2,3,4,5,6]
b = [2,3]
for i in b:
if i in a:
a.remove(i)
print(a)
the output is [1,4,5,6]
I hope, it will work

e, f are two list you want to check disjoint
a = [1,2,3,4]
b = [8,7,9,2,1]
c = []
def loop_to_check(e,f):
for i in range(len(e)):
if e[i] not in f:
c.append(e[i])
loop_to_check(a,b)
loop_to_check(b,a)
print(c)
## output is [3,4,8,7,9]
This loops around to list and returns the disjoint list

Related

Remove an element from two lists

I have list_a and list_b. Both of these lists have items in an order.
Each time I encounter a 0 in list_b, I want to remove from list_a AND list_b the entry associated with that index. I am not sure how to do that.
# Before modification
list_a = [ '2019', '2020', '2021', '2022', '2023' ]
list_b = [ 40, 0, 30, 0, 9 ]
#After modification
list_a = [ '2019', '2021', '2023' ]
list_b = [ 40, 30, 9 ]
Any clue on how to approach this?
Good use case for itertools.compress and filter:
list_a[:] = compress(list_a, list_b)
list_b[:] = filter(None, list_b)
Try it online!
There are probably 100 ways to do this, and I'm sure you'll get diverse responses. If you're interested in learning this, you should try a couple...
Use a for-loop over an index. Before the loop, make 2 new lists like list_a_new, list_b_new and then use the for loop to loop over the index of the original list_b. test the object you get out. Use a conditional statement. If the object is not zero, get the items from the original lists at the same index position and add it to both of the new results by append()
Use a list comprehension for both of the new lists and use enumerate(list_b) inside to get the same type of info and see if you can do a list comprehension for both new lists
Make a "mask". numpy can do this or you can make your own, perhaps with a list comprehension again over list_b to make a mask of booleans like [False, True, False, True, ...] Use that mask as the basis of another list comprehension to get new_a and new_b
Try a couple and edit your post if you are stuck! You'll improve your skills.
As mentioned by other users there are many ways
list_a2 = [list_a[i] for i in range(len(list_b)) if list_b[i]!=0]
list_b2 = [list_b[i] for i in range(len(list_b)) if list_b[i]!=0]
Here's a solution using a traditional for-loop to iterate over items from list_a paired (using zip()) with items from list_b:
new_a = []
new_b = []
for a, b in zip(list_a, list_b):
if b != 0:
new_a.append(a)
new_b.append(b)
Or you could use a couple of list comprehensions:
new_a = [a for a, b in zip(list_a, list_b) if b != 0]
new_b = [b for b in list_b if b != 0]
You do it all in one line but readability suffers:
new_a, new_b = map(list, zip(*((a, b) for a, b in zip(list_a, list_b) if b != 0)))
If you don't mind modifying your original lists it becomes slightly less unreadable:
list_a[:], list_b[:] = zip(*((a, b) for a, b in zip(list_a, list_b) if b != 0))

Python extract list of unknow length

I have a string from user. It must containt a comma to split by it and assign to variables. But what if user miss a comma? Surely I can check len of splitted string in if-else branches, but maybe there is another way, I mean assignment during list has a values. For example
a, b, c, d, e = list(range(3)) # 'a' and 'b' are None or not exists
You could do something like this:
>>> alist=list(range(3))
>>> alist
[0, 1, 2]
>>> a,b,c,*d=alist
>>> a,b,c
(0, 1, 2)
>>> d
[]
If there are no more elements, d is an empty list. It uses the unpacking operator *. Not the best possible solution for large lists, so I would still define a function for that. For small cases, it works well. (You could assume that is d==[], there are no more elements in alist)For example, you could add:
return False if not d else return True
You are free to extend the list with None values before extraction.
li = list(range(3))
expected_size = 5
missing_size = expected_size - len(li)
none_li = [None] * missing_size
new_li = li + none_li
a, b, c, d, e = new_li

assert that all lists in a list of lists are equal

I want to check if all lists in a list of lists are equal.
One example for which I succeeded is a lists of two lists l2 for which
all([a == b for a, b in zip(*l2)])
correctly returns True if l2 = [[1,2],[1,2]] and Falsewhen l2 = [[1,2],[1,666]].
I expected to be able to directly use this code in the case in which the list of lists l has more lists in it by using the same code, but it seems to not work.
For example, when
l=[[1,2],[1,2],[1,2]]
all([a == b for a, b in zip(*l)])
returns the following error:
ValueError: too many values to unpack (expected 2)
I do not understand why this is the case as the result of zip(*l) looks like it should work:
list(zip(*l))
>> [(1, 1, 1), (2, 2, 2)]
Using the observation a == b and a == c imply a == c. You should test the first list with the other lists.
def equalLists(lists):
return not lists or all(lists[0] == b for b in lists[1:])
>>> equalLists([])
True
>>> equalLists([1,2],[1,2])
True
>>> equalLists([1,2],[1,2],[1,2])
True
>>> equalLists([1,2],[1,2],[1,3])
False
You could create a set of tuples and check the size is 1, otherwise they are not all the same:
len(set(tuple(elem) for elem in l)) == 1
This will work for a list of lists of any length. It will also be more efficient than linear time comparisons.
(You have to convert to a tuple first because a list is not hashable and a set requires its members to be hashable.)
Your method (and the other answers here) don't consider that if the lists' lengths vary, zip will shorten them to the length of the shortest:
all(a == b for a,b in zip([1,2], [1,2,3]))
>>> True
Firstly note that it's not necessary to construct a list in all like all([...]) as this adds an extra iteration after list creation, whereas as I've done above uses a generator which evaluates as it goes along.
If each list has hashable elements, I'd exploit set to calculate the distinct elements and check there's only 1:
len(set(tuple(x) for x in l)) == 1
If the elements aren't hashable, but do have the equals method defined on them (unlike your examples, since int is hashable) I'd compare each list to the first, possibly using a generator if you want to avoid comparing the first to itself:
li = iter(l)
first = next(li)
all(x == first for x in li)
This still makes use of python's built-in list equals method and won't do more comparisons than any zip methods in the case that all the lists are equal.
The only case where the above is inefficient is if you have a list of long lists, where most but not all are equal. In that case it's possible a zip method would be quicker:
from itertools import zip_longest
all(len(set(x)) == 1 for x in zip_longest(*l))
Here I used zip_longest for the case the list lengths are unequal. If you knw the lengths are equal you can use zip. By default it fills values with None from the shorter lists once they 'run out' in the iterator, so only use this if your lists have no legitimate Nones! (In that case you can set zip_longest(..., fillvalue="<something not in the lists>").
Equivalent for non-hashable list elements (with equals method):
all(all(i == x[0] for i in x[1:]) for x in zip_longest(*l))
l=[[1,2],[1,2],[1,2]]
all([a == b == c for a, b, c in zip(*l)])
You're telling zip() there's 2 values to unpack but zip has 3 lists to unpack.
You have three item after zip (1,1,1)
Use
l=[[1,2],[1,2],[1,2]]
all([a == b for a, b, c in zip(*l)])
or
l=[[1,2],[1,2],[1,2]]
all([a == b for a, b, *_ in zip(*l)])
# OR all([a == b for a, b, _ in zip(*l)])
# OR all([i[0] == i[1] for i in zip(*l)])
Edit as per comment.
To test all sub elements are equal
all([len(set(i)) == 1 for i in zip(*l)])

Map the max function for list of the lists

I stack with the following problem, I need to finding maximum between equal positions between lists. Map function works pretty well, but how to make it work for the list of the lists? using map(max,d) gave the max of the every list. The problem is that the number of the lists in the list is variable. Any suggestions are welcome!
Input for the problem is d not an a,b,c, d - is a list of the lists, and the comparison is pairwise per position in the list.
a = [0,1,2,6]
b = [5,1,0,7]
c = [3,8,0,8]
map(max,a,b,c)
# [5,8,2,8]
d = [a,b,c]
map(max,d)
[6,7,8]
a = [0,1,2,6]
b = [5,1,0,7]
c = [3,8,0,8]
print [max(itm) for itm in zip(a, b, c)]
or even shorter:
print map(max, zip(a, b, c))
How about this:
max(map(max,d))

How to copy data in Python

After entering a command I am given data, that I then transform into a list. Once transformed into a list, how do I copy ALL of the data from that list [A], and save it - so when I enter a command and am given a second list of data [B], I can compare the two; and have data that is the same from the two lists cancel out - so what is not similar between [A] & [B] is output. For example...
List [A]
1
2
3
List [B]
1
2
3
4
Using Python, I now want to compare the two lists to each other, and then output the differences.
Output = 4
Hopefully this makes sense!
You can use set operations.
a = [1,2,3]
b = [1,2,3,4]
print set(b) - set(a)
to output the data in list format you can use the following print statement
print list(set(b) - set(a))
>>> b=[1,2,3,4]
>>> a=[1,2,3]
>>> [x for x in b if x not in a]
[4]
for element in b:
if element in a:
a.remove(element)
This answer will return a list not a set, and should take duplicates into account. That way [1,2,1] - [1,2] returns [1] not [].
Try itertools.izip_longest
import itertools
a = [1,2,3]
b = [1,2,3,4]
[y for x, y in itertools.izip_longest(a, b) if x != y]
# [4]
You could easily modify this further to return a duple for each difference, where the first item in the duple is the position in b and the second item is the value.
[(i, pair[1]) for i, pair in enumerate(itertools.izip_longest(a, b)) if pair[0] != pair[1]]
# [(3, 4)]
For entering the data use a loop:
def enterList():
result = []
while True:
value = raw_input()
if value:
result.append(value)
else:
return result
A = enterList()
B = enterList()
For comparing you can use zip to build pairs and compare each of them:
for a, b in zip(A, B):
if a != b:
print a, "!=", b
This will truncate the comparison at the length of the shorter list; use the solution in another answer given here using itertools.izip_longest() to handle that.

Categories

Resources