Differentiating a tuple from a tuple of tuples - python

I have a tuple, and a tuple of tuples.
import numpy as np
a = ("Control", "Group1")
b = (("Control", "Group1"), ("Control", "Group1", "Group2))
How can I tell that a is fundamentally different from b? Both
print(len(a))
print(np.shape(a))
print(len(np.shape(a)))
and
print(len(b))
print(np.shape(b))
print(len(np.shape(b)))
produce the same output:
2
(2,)
1
Thanks in advance again!

You cannot, because they are not fundamentally different.
What should happen for the following?
c = (("Foo", "bar"), "baz")
It’s also a tuple, and it contains both "bare" values as well as another tuple.
If you need to detect tuples which only consist of tuples, use:
if all(isinstance(element, tuple) for element in a)
If you need to detect tuples which only consist of non-tuples, use:
if not any(isinstance(element, tuple) for element in a)
Both of the above are have a time complexity of O(n) (with n being the number of elements in a), which may not be desirable depending from where your data is coming. It is however unavoidable, unless you are willing to take the risk not actually having tuples of tuples.
Depending on what you’re doing with your data, you might actually want to check for a sequence of sequences. In that case, you should use the Sequence ABC (Python 2):
import collections.abc
if all(isinstance(element, collections.abc.Sequence) for element in a)

Use the equality operator, ==:
>>> a = ("Control", "Group1")
>>> b = (("Control", "Group1"), ("Control", "Group1", "Group2"))
>>> a == b
False
If you just want a vague idea of the general structure, and the string elements won't contain parentheses, you can count the parentheses:
>>> str(a).count('(')
1
>>> str(b).count('(')
3

Related

How to compare two list of dictionaries [duplicate]

a = [1, 2, 3, 1, 2, 3]
b = [3, 2, 1, 3, 2, 1]
a & b should be considered equal, because they have exactly the same elements, only in different order.
The thing is, my actual lists will consist of objects (my class instances), not integers.
O(n): The Counter() method is best (if your objects are hashable):
def compare(s, t):
return Counter(s) == Counter(t)
O(n log n): The sorted() method is next best (if your objects are orderable):
def compare(s, t):
return sorted(s) == sorted(t)
O(n * n): If the objects are neither hashable, nor orderable, you can use equality:
def compare(s, t):
t = list(t) # make a mutable copy
try:
for elem in s:
t.remove(elem)
except ValueError:
return False
return not t
You can sort both:
sorted(a) == sorted(b)
A counting sort could also be more efficient (but it requires the object to be hashable).
>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True
If you know the items are always hashable, you can use a Counter() which is O(n)
If you know the items are always sortable, you can use sorted() which is O(n log n)
In the general case you can't rely on being able to sort, or has the elements, so you need a fallback like this, which is unfortunately O(n^2)
len(a)==len(b) and all(a.count(i)==b.count(i) for i in a)
If you have to do this in tests:
https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual
assertCountEqual(first, second, msg=None)
Test that sequence first contains the same elements as second, regardless of their order. When they don’t, an error message listing the differences between the sequences will be generated.
Duplicate elements are not ignored when comparing first and second. It verifies whether each element has the same count in both sequences. Equivalent to: assertEqual(Counter(list(first)), Counter(list(second))) but works with sequences of unhashable objects as well.
New in version 3.2.
or in 2.7:
https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertItemsEqual
Outside of tests I would recommend the Counter method.
The best way to do this is by sorting the lists and comparing them. (Using Counter won't work with objects that aren't hashable.) This is straightforward for integers:
sorted(a) == sorted(b)
It gets a little trickier with arbitrary objects. If you care about object identity, i.e., whether the same objects are in both lists, you can use the id() function as the sort key.
sorted(a, key=id) == sorted(b, key==id)
(In Python 2.x you don't actually need the key= parameter, because you can compare any object to any object. The ordering is arbitrary but stable, so it works fine for this purpose; it doesn't matter what order the objects are in, only that the ordering is the same for both lists. In Python 3, though, comparing objects of different types is disallowed in many circumstances -- for example, you can't compare strings to integers -- so if you will have objects of various types, best to explicitly use the object's ID.)
If you want to compare the objects in the list by value, on the other hand, first you need to define what "value" means for the objects. Then you will need some way to provide that as a key (and for Python 3, as a consistent type). One potential way that would work for a lot of arbitrary objects is to sort by their repr(). Of course, this could waste a lot of extra time and memory building repr() strings for large lists and so on.
sorted(a, key=repr) == sorted(b, key==repr)
If the objects are all your own types, you can define __lt__() on them so that the object knows how to compare itself to others. Then you can just sort them and not worry about the key= parameter. Of course you could also define __hash__() and use Counter, which will be faster.
If the comparison is to be performed in a testing context, use assertCountEqual(a, b) (py>=3.2) and assertItemsEqual(a, b) (2.7<=py<3.2).
Works on sequences of unhashable objects too.
If the list contains items that are not hashable (such as a list of objects) you might be able to use the Counter Class and the id() function such as:
from collections import Counter
...
if Counter(map(id,a)) == Counter(map(id,b)):
print("Lists a and b contain the same objects")
Let a,b lists
def ass_equal(a,b):
try:
map(lambda x: a.pop(a.index(x)), b) # try to remove all the elements of b from a, on fail, throw exception
if len(a) == 0: # if a is empty, means that b has removed them all
return True
except:
return False # b failed to remove some items from a
No need to make them hashable or sort them.
I hope the below piece of code might work in your case :-
if ((len(a) == len(b)) and
(all(i in a for i in b))):
print 'True'
else:
print 'False'
This will ensure that all the elements in both the lists a & b are same, regardless of whether they are in same order or not.
For better understanding, refer to my answer in this question
You can write your own function to compare the lists.
Let's get two lists.
list_1=['John', 'Doe']
list_2=['Doe','Joe']
Firstly, we define an empty dictionary, count the list items and write in the dictionary.
def count_list(list_items):
empty_dict={}
for list_item in list_items:
list_item=list_item.strip()
if list_item not in empty_dict:
empty_dict[list_item]=1
else:
empty_dict[list_item]+=1
return empty_dict
After that, we'll compare both lists by using the following function.
def compare_list(list_1, list_2):
if count_list(list_1)==count_list(list_2):
return True
return False
compare_list(list_1,list_2)
from collections import defaultdict
def _list_eq(a: list, b: list) -> bool:
if len(a) != len(b):
return False
b_set = set(b)
a_map = defaultdict(lambda: 0)
b_map = defaultdict(lambda: 0)
for item1, item2 in zip(a, b):
if item1 not in b_set:
return False
a_map[item1] += 1
b_map[item2] += 1
return a_map == b_map
Sorting can be quite slow if the data is highly unordered (timsort is extra good when the items have some degree of ordering). Sorting both also requires fully iterating through both lists.
Rather than mutating a list, just allocate a set and do a left-->right membership check, keeping a count of how many of each item exist along the way:
If the two lists are not the same length you can short circuit and return False immediately.
If you hit any item in list a that isn't in list b you can return False
If you get through all items then you can compare the values of a_map and b_map to find out if they match.
This allows you to short-circuit in many cases long before you've iterated both lists.
plug in this:
def lists_equal(l1: list, l2: list) -> bool:
"""
import collections
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
ref:
- https://stackoverflow.com/questions/9623114/check-if-two-unordered-lists-are-equal
- https://stackoverflow.com/questions/7828867/how-to-efficiently-compare-two-unordered-lists-not-sets
"""
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
set_comp = set(l1) == set(l2) # removes duplicates, so returns true when not sometimes :(
multiset_comp = compare(l1, l2) # approximates multiset
return set_comp and multiset_comp #set_comp is gere in case the compare function doesn't work

How to combine list and individual elements into one tuple in python?

I have a few variables in python3:
a = 1
b = [2,3,4]
c = 5
I want to get a tuple which is from above variables, like: (1,2,3,4,5)
what is the easiest way to do that in python3?
Creating a tuple in Python is as simple as putting the stuff you need in a tuple in parentheses:
my_tuple = (1, 2, 3)
a = 1
b = 2
c = 3
another_tuple = (a, b, c) # also works with variables, or anything else with a value
And if what you want in the tuple is in something else that can be unpacked, like a list or a tuple itself, you can use the unpacking operator * to unpack it into the new tuple:
a = 1
b = [2,3,4]
c = 5
my_tuple = (a, *b, c)
Not your question, but note that you can also get stuff from a tuple without using the * operator, as it's implied in an assignment statement:
x, _, z = my_tuple # continued from before
In this example, what was in a (1) is now also in x and what was in c also in z. What was in b and in the second position of the tuple gets discards (that's what the underscore _ here means, "don't care".)
You use the unpack operator in cases where you explicitly need to unpack and you're constructing some new variable, or need the elements of the tuple separately where they could also be used as a tuple. For example, when calling a function:
a_tuple = ('Hi there', 'John')
def greeting(phrase='Hello', name='world'):
print(f'{phrase}, {name}!')
greeting(*a_tuple)
In this example, calling greeting as greeting(a_tuple) would give you the very nasty result of ('Hi there', 'John'), world!, clearly not what you want, but you can still use the tuple with the unpack operator.
And the other obvious example is one like the one solving OP's question.
Simply create a new tuple as shown below.
newTuple=(a, *b, c)
Note: *b unpacks list b into variables and add each variable to indexes of newTuple
One of the ways is shown below
from functools import reduce
import operator
# create a bunch of lists, reduce them to one list and finally convert to tuple
result = tuple(reduce(operator.add, ([a], b, [c])))
print(result)
This is the most pythonic solution:
a = 1
b = [2,3,4]
c = 5
res = a,*b,c
# output: (1,2,3,4,5)
Note: To create the new tuple, round brackets are not necessary in Python3

comparing contents of two lists python [duplicate]

a = [1, 2, 3, 1, 2, 3]
b = [3, 2, 1, 3, 2, 1]
a & b should be considered equal, because they have exactly the same elements, only in different order.
The thing is, my actual lists will consist of objects (my class instances), not integers.
O(n): The Counter() method is best (if your objects are hashable):
def compare(s, t):
return Counter(s) == Counter(t)
O(n log n): The sorted() method is next best (if your objects are orderable):
def compare(s, t):
return sorted(s) == sorted(t)
O(n * n): If the objects are neither hashable, nor orderable, you can use equality:
def compare(s, t):
t = list(t) # make a mutable copy
try:
for elem in s:
t.remove(elem)
except ValueError:
return False
return not t
You can sort both:
sorted(a) == sorted(b)
A counting sort could also be more efficient (but it requires the object to be hashable).
>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True
If you know the items are always hashable, you can use a Counter() which is O(n)
If you know the items are always sortable, you can use sorted() which is O(n log n)
In the general case you can't rely on being able to sort, or has the elements, so you need a fallback like this, which is unfortunately O(n^2)
len(a)==len(b) and all(a.count(i)==b.count(i) for i in a)
If you have to do this in tests:
https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual
assertCountEqual(first, second, msg=None)
Test that sequence first contains the same elements as second, regardless of their order. When they don’t, an error message listing the differences between the sequences will be generated.
Duplicate elements are not ignored when comparing first and second. It verifies whether each element has the same count in both sequences. Equivalent to: assertEqual(Counter(list(first)), Counter(list(second))) but works with sequences of unhashable objects as well.
New in version 3.2.
or in 2.7:
https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertItemsEqual
Outside of tests I would recommend the Counter method.
The best way to do this is by sorting the lists and comparing them. (Using Counter won't work with objects that aren't hashable.) This is straightforward for integers:
sorted(a) == sorted(b)
It gets a little trickier with arbitrary objects. If you care about object identity, i.e., whether the same objects are in both lists, you can use the id() function as the sort key.
sorted(a, key=id) == sorted(b, key==id)
(In Python 2.x you don't actually need the key= parameter, because you can compare any object to any object. The ordering is arbitrary but stable, so it works fine for this purpose; it doesn't matter what order the objects are in, only that the ordering is the same for both lists. In Python 3, though, comparing objects of different types is disallowed in many circumstances -- for example, you can't compare strings to integers -- so if you will have objects of various types, best to explicitly use the object's ID.)
If you want to compare the objects in the list by value, on the other hand, first you need to define what "value" means for the objects. Then you will need some way to provide that as a key (and for Python 3, as a consistent type). One potential way that would work for a lot of arbitrary objects is to sort by their repr(). Of course, this could waste a lot of extra time and memory building repr() strings for large lists and so on.
sorted(a, key=repr) == sorted(b, key==repr)
If the objects are all your own types, you can define __lt__() on them so that the object knows how to compare itself to others. Then you can just sort them and not worry about the key= parameter. Of course you could also define __hash__() and use Counter, which will be faster.
If the comparison is to be performed in a testing context, use assertCountEqual(a, b) (py>=3.2) and assertItemsEqual(a, b) (2.7<=py<3.2).
Works on sequences of unhashable objects too.
If the list contains items that are not hashable (such as a list of objects) you might be able to use the Counter Class and the id() function such as:
from collections import Counter
...
if Counter(map(id,a)) == Counter(map(id,b)):
print("Lists a and b contain the same objects")
Let a,b lists
def ass_equal(a,b):
try:
map(lambda x: a.pop(a.index(x)), b) # try to remove all the elements of b from a, on fail, throw exception
if len(a) == 0: # if a is empty, means that b has removed them all
return True
except:
return False # b failed to remove some items from a
No need to make them hashable or sort them.
I hope the below piece of code might work in your case :-
if ((len(a) == len(b)) and
(all(i in a for i in b))):
print 'True'
else:
print 'False'
This will ensure that all the elements in both the lists a & b are same, regardless of whether they are in same order or not.
For better understanding, refer to my answer in this question
You can write your own function to compare the lists.
Let's get two lists.
list_1=['John', 'Doe']
list_2=['Doe','Joe']
Firstly, we define an empty dictionary, count the list items and write in the dictionary.
def count_list(list_items):
empty_dict={}
for list_item in list_items:
list_item=list_item.strip()
if list_item not in empty_dict:
empty_dict[list_item]=1
else:
empty_dict[list_item]+=1
return empty_dict
After that, we'll compare both lists by using the following function.
def compare_list(list_1, list_2):
if count_list(list_1)==count_list(list_2):
return True
return False
compare_list(list_1,list_2)
from collections import defaultdict
def _list_eq(a: list, b: list) -> bool:
if len(a) != len(b):
return False
b_set = set(b)
a_map = defaultdict(lambda: 0)
b_map = defaultdict(lambda: 0)
for item1, item2 in zip(a, b):
if item1 not in b_set:
return False
a_map[item1] += 1
b_map[item2] += 1
return a_map == b_map
Sorting can be quite slow if the data is highly unordered (timsort is extra good when the items have some degree of ordering). Sorting both also requires fully iterating through both lists.
Rather than mutating a list, just allocate a set and do a left-->right membership check, keeping a count of how many of each item exist along the way:
If the two lists are not the same length you can short circuit and return False immediately.
If you hit any item in list a that isn't in list b you can return False
If you get through all items then you can compare the values of a_map and b_map to find out if they match.
This allows you to short-circuit in many cases long before you've iterated both lists.
plug in this:
def lists_equal(l1: list, l2: list) -> bool:
"""
import collections
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
ref:
- https://stackoverflow.com/questions/9623114/check-if-two-unordered-lists-are-equal
- https://stackoverflow.com/questions/7828867/how-to-efficiently-compare-two-unordered-lists-not-sets
"""
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
set_comp = set(l1) == set(l2) # removes duplicates, so returns true when not sometimes :(
multiset_comp = compare(l1, l2) # approximates multiset
return set_comp and multiset_comp #set_comp is gere in case the compare function doesn't work

best way of combining the values of 2 different tuples

I want to add together the values of 2 tuples (of any size), and create an output tuple.
For example:
a = (1,4)
b = (2,3)
output: (3,7)
Is there a better way to do it than just:
output = (a[0] + b[0], a[1]+b[1])
How about using a generator expression?
output = tuple(a[i] + b[i] for i in range(len(a)))
If you don't know that the tuples are the same length, you could try using something more fancy like zip (which will stop at the length of the shorter tuple), or itertools.izip (which will allow you to control how to handle different length tuples).
tuple(x+y for (x,y) in zip(a,b))
If you want to stick with 2-tuples, what you have is fine (and probably best). You might consider using a different data structure, one where the + operator adds element-wise. For example:
complex numbers add like 2-vectors (using .real and .imag components)
numpy arrays
Write your own Point class, overriding the __add__ magic method
If you wan't to do it in a way that doesn't require you spell out all elements, go with something functional:
output = tuple(map(sum, zip(a,b)))
or, a list-comp which you, again, must supply to tuple:
output = tuple([i+j for i,j in zip(a,b)])
you could always substitute zip with zip_longest from itertools, using a fill value of 0, if the sizes might differ.
tuple(map(lambda x, y: x + y, a, b))
import operator
tuple(map(operator.add, a, b))

iterate over list of tuples in two notations

I'm iterating over a list of tuples, and was just wondering if there is a smaller notation to do the following:
for tuple in list:
(a,b,c,d,e) = tuple
or the equivalent
for (a,b,c,d,e) in list:
tuple = (a,b,c,d,e)
Both of these snippits allow me to access the tuple per item as well as as a whole. But is there a notation that somehow combines the two lines into the for-statement? It seems like such a Pythonesque feature that I figured it might exist in some shape or form.
The pythonic way is the first option you menioned:
for tup in list:
a,b,c,d,e = tup
This might be a hack that you could use. There might be a better way, but that's why it's a hack. Your examples are all fine and that's how I would certainly do it.
>>> list1 = [(1, 2, 3, 4, 5)]
>>> for (a, b, c, d, e), tup in zip(list1, list1):
print a, b, c, d, e
print tup
1 2 3 4 5
(1, 2, 3, 4, 5)
Also, please don't use tuple as a variable name.
There isn't anything really built into Python that lets you do this, because the vast majority of the time, you only need to access the tuple one way or the other: either as a tuple or as separate elements. In any case, something like
for t in the_list:
a,b,c,d,e = t
seems pretty clean, and I can't imagine there'd be any good reason to want it more condensed than that. That's what I do on the rare occasions that I need this sort of access.
If you just need to get at one or two elements of the tuple, say perhaps c and e only, and you don't need to use them repeatedly, you can access them as t[2] and t[4]. That reduces the number of variables in your code, which might make it a bit more readable.

Categories