So i know that comparisons on tuples work lexicographically:
Tuples and lists are compared lexicographically using comparison of corresponding elements. This means that to compare equal, each element must compare equal and the two sequences must be of the same type and have the same length.
If not equal, the sequences are ordered the same as their first differing elements. For example, cmp([1,2,x], [1,2,y]) returns the same as cmp(x,y). If the corresponding element does not exist, the shorter sequence is ordered first (for example, [1,2] < [1,2,3]).
So from this:
>>> a = (100, 0)
>>> b = (50, 50)
>>> a > b
True
But i want to compare all elements of 2 tuples in order, so functionally i want something akin to (using values from above):
>>> a > b
(True, False) #returned tuple containing each comparison
>>> all(a > b)
False
As an example in practice, for something like screen coordinates, if you wanted to check if something was 'inside' the screen at (0,0), but done a comparison like coord > (0,0), if the x coord was bigger than 0, but the y coord was smaller it would still return true, which isn't what is needed in this case.
As sort of a sub question/discussion:
I am not sure why comparing 2 tuples of different values is returned in such a way. You are not given any sort of index, so the only thing you get from comparing a tuple (that isn't testing equality) is that at some point in the tuple, one of the comparisons will throw a true or false value when they are not equal. How could you take advantage of that?
You can achieve this with a list comprehension and the zip built-in:
>>> a = (100, 0)
>>> b = (50, 50)
>>> [(a > b) for a, b in zip(a,b)]
[True, False]
You can use all() or any() on the returned list.
Replace a > b with tuple(i > j for i, j in zip(a,b)) in your second code sample.
>>> a = (100, 0)
>>> b = (50, 50)
>>> tuple(i > j for i, j in zip(a,b))
(True, False)
>>> all(i > j for i, j in zip(a,b))
False
You might consider using the following vectorized approach, which is usually more performant, and syntactically/semantically very clear:
>>> import numpy
>>>
>>> a = (100, 0)
>>> b = (50, 50)
>>> numpy.array(a) > b
array([ True, False], dtype=bool)
>>>
>>> (numpy.array(a) > b).any()
True
>>> (numpy.array(a) > b).all()
False
numpy is quite performant, and the resulting objects above also embed the any()/all() query methods you want. If you will be performing vector-like operations (as your screen coordinates example suggests), you may consider working with 'a' and 'b' as numpy arrays, instead of tuples. That results in the most efficient implementation of what you seek: no pre-conversion necessary, and Python-based loops are replaced with efficient numpy-based loops. This is worth highlighting because there are two and potentially three loops involved: (1) a preprocessing loop during conversion (which you can eliminate); (2) an item-by-item comparison loop; and (3) a query loop to answer the any/all question.
Note that I could've also created a numpy array from 'b', but not doing so eliminated one conversion step and pre-processing time. Since that approach results in one operand being a numpy array and the other a tuple, as the sequences grow, that may/may-not result in less speedy item-by-item comparisons (which strict numpy-to-numpy is good at). Try it. :)
I felt like the use of map and lambda functions was missing from the answers
>>> a = (100, 0)
>>> b = (50, 50)
>>> all(map(lambda x,y: x > y, a, b))
False
To get the described behavior, try:
[ai > bi for ai,bi in zip(a,b)]
The reason that comparisons of tuples are returned in that way is that you might want to write something like:
if a >= (0.,0.):
print "a has only positive values"
else:
print "a has at least one negative value"
If Python were to return the tuple that you describe, then the else would never happen. Try
if (False,False):
print "True!" # This is what is printed.
else:
print "False!"
I hope this helps.
Related
I noticed a piece of code recently directly comparing two lists of integers like so:
a = [10,3,5, ...]
b = [5,4,3, ...,]
if a > b:
...
which seemed a bit peculiar, but I imagined it would return True if all of list_a's elements are larger then list_b's and False if each element is equal or list_b's elements are larger then list_a's. So I tested it:
>>> a=[3,3,3,3]
>>> b=[4,4,4,4]
>>> a>b
False
>>> b>a
True
Ok that works. As does:
>>> b = [1,1,1,1]
>>> a = [1,1,1,1]
>>> a>b
False
>>> b>a
False
but when it gets more fuzzy:
>>> a=[1,1,3,1]
>>> b=[1,3,1,1]
>>> a>b
False
>>> b>a
True
or:
>>> a=[1,3,1,1]
>>> b=[1,1,3,3]
>>> a>b
True
>>> b>a
False
the results are a bit stranger. What is python actually doing? It seems that it's returning the result in favour of the first list in which the left most element is greater then the corresponding?
From Comparing Sequences and Other Types in the Python tutorial:
The comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted.
See also the Wikipedia article about lexicographical order.
Since I didn't find the explanation of list/tuple comparison using "lexicographical ordering" particularly illuminating at first, here's an attempt to explain it "in my own words". First, here are some example lists that are referred to in the explanation below:
a = [1, 2, 3]
b = [1, 2, 10]
c = [1, 2, 3, 100]
d = [1, 2, 3]
e = [1, 2, 3, 4, 'a']
f = ['a', 'b', 'c']
The pair of items at each index are compared in turn. So, comparing a to b will result in 1 being compared to 1, 2 being compared to 2, and 3 being compared to 10.
The comparison of pairs will stop when either an unequal pair of items is found or--if the lists are different lengths--the end of the shorter list is reached.
For example, when comparing a and b, comparisons will stop when 3 and 10 are compared. When comparing b and c, comparisons will stop when 10 and 3 are compared.
As soon as an unequal pair is found, the overall result is the result of comparing the unequal items. This applies whether the lists are the same length or not--for example, list b is greater than list c because the 100 in c never comes into play.
For example, when comparing a to b, the overall result will be the result of comparing 3 to 10. a < b -> True because 3 is less than 10. a > b -> False because 3 is not greater than 10. a == b -> False because 3 does not equal 10.
If one of the lists is shorter and its N items are equal to the first N items of the longer list, as with a and c, the shorter list will be considered less than the longer list (so a is less than c).
Two lists will compare as equal only if they're the same length and all pairs of items compare as equal.
Note about types: if the items in a pair aren't comparable, the comparison will fail with a TypeError as usual. For example, comparing list a to f will fail when 1 is compared to 'a'. But also note that lists d and e can be compared since the 'a' in e is never compared to anything in d.
The given answers don't account for duplicates in the larger list, you can iterate over the bigger list and slice it each time to compare it with the sub list, this will maintain the order as well as account for duplicates.
This code would work:
def check_sublist(List,Sublist)
for i in range(len(List)):
if List[i:] ==Sublist:
return True
return False
Yes this is not time efficient but this is the only solution I could think since using set() would not maintain the order
I noticed a piece of code recently directly comparing two lists of integers like so:
a = [10,3,5, ...]
b = [5,4,3, ...,]
if a > b:
...
which seemed a bit peculiar, but I imagined it would return True if all of list_a's elements are larger then list_b's and False if each element is equal or list_b's elements are larger then list_a's. So I tested it:
>>> a=[3,3,3,3]
>>> b=[4,4,4,4]
>>> a>b
False
>>> b>a
True
Ok that works. As does:
>>> b = [1,1,1,1]
>>> a = [1,1,1,1]
>>> a>b
False
>>> b>a
False
but when it gets more fuzzy:
>>> a=[1,1,3,1]
>>> b=[1,3,1,1]
>>> a>b
False
>>> b>a
True
or:
>>> a=[1,3,1,1]
>>> b=[1,1,3,3]
>>> a>b
True
>>> b>a
False
the results are a bit stranger. What is python actually doing? It seems that it's returning the result in favour of the first list in which the left most element is greater then the corresponding?
From Comparing Sequences and Other Types in the Python tutorial:
The comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted.
See also the Wikipedia article about lexicographical order.
Since I didn't find the explanation of list/tuple comparison using "lexicographical ordering" particularly illuminating at first, here's an attempt to explain it "in my own words". First, here are some example lists that are referred to in the explanation below:
a = [1, 2, 3]
b = [1, 2, 10]
c = [1, 2, 3, 100]
d = [1, 2, 3]
e = [1, 2, 3, 4, 'a']
f = ['a', 'b', 'c']
The pair of items at each index are compared in turn. So, comparing a to b will result in 1 being compared to 1, 2 being compared to 2, and 3 being compared to 10.
The comparison of pairs will stop when either an unequal pair of items is found or--if the lists are different lengths--the end of the shorter list is reached.
For example, when comparing a and b, comparisons will stop when 3 and 10 are compared. When comparing b and c, comparisons will stop when 10 and 3 are compared.
As soon as an unequal pair is found, the overall result is the result of comparing the unequal items. This applies whether the lists are the same length or not--for example, list b is greater than list c because the 100 in c never comes into play.
For example, when comparing a to b, the overall result will be the result of comparing 3 to 10. a < b -> True because 3 is less than 10. a > b -> False because 3 is not greater than 10. a == b -> False because 3 does not equal 10.
If one of the lists is shorter and its N items are equal to the first N items of the longer list, as with a and c, the shorter list will be considered less than the longer list (so a is less than c).
Two lists will compare as equal only if they're the same length and all pairs of items compare as equal.
Note about types: if the items in a pair aren't comparable, the comparison will fail with a TypeError as usual. For example, comparing list a to f will fail when 1 is compared to 'a'. But also note that lists d and e can be compared since the 'a' in e is never compared to anything in d.
The given answers don't account for duplicates in the larger list, you can iterate over the bigger list and slice it each time to compare it with the sub list, this will maintain the order as well as account for duplicates.
This code would work:
def check_sublist(List,Sublist)
for i in range(len(List)):
if List[i:] ==Sublist:
return True
return False
Yes this is not time efficient but this is the only solution I could think since using set() would not maintain the order
I was asked if it was possible to compare two (say) lists without invoking operators, to determine if they were the same (or rather, contained the same elements).
I first entertained using
x in y
before I realised that it would not care for order, but for mere presence. Of course, if the lists were to contain purely numbers it would be trivial to do a modulus test or so, but lists can contain strings. (is didn't work either, but I didn't really expect it to, considering it tests identity...)
So I was wondering if it's (even) possible to pull off equality tests without using operators (==, !=)?
It was a mere rhetorical question, but it's been gnawing at me for some time and I've rather given up trying to solve it myself with my not-very extensive python knowledge.
Sure it is, just bypass the operators and go straight for the __eq__ special method:
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> x.__eq__(y)
True
>>> z = [42]
>>> x.__eq__(z)
False
You can also use the operator module:
>>> import operator
>>> operator.eq(x, y)
True
>>> operator.eq(x, z)
False
In Python 2, you could use looping with any() and cmp(), with itertools.izip_longest() to make sure we don't ignore uneven lengths:
>>> from itertools import izip_longest
>>> not any(cmp(i, j) for i, j in izip_longest(x, y, fillvalue=object()))
True
>>> not any(cmp(i, j) for i, j in izip_longest(x, z, fillvalue=object()))
False
This works because cmp() returns 0 for values that are equal. any() returns False only if all results are false (e.g. 0).
Hell, go straight for cmp() without looping:
>>> not cmp(x, y)
True
>>> not cmp(x, z)
False
For Python 3 you'd have to create your own cmp() function, perhaps using .__lt__ and .__gt__ if you want to avoid the < and > operators too.
For lists with only integers, you can forgo the cmp() function and go straight to subtraction; let's use map() here and include the list lengths:
>>> not (len(x) - len(y)) and not any(map(lambda i, j: i - j, x, y))
True
>>> not (len(x) - len(z)) and not any(map(lambda i, j: i - j, x, z))
False
This works because map() zips up the values in the lists and passes these pairs to the first argument, a callable. That subtracts the values and only if the integers are equal do we get all 0 values and any() returns False.
Apart from Martijn Pieters's answer, i could think of following options:
using XOR:
x = [1, 2, 3]
y = [1, 2, 3]
result = "list equal"
if len(x)-len(y):
result = "list not equal"
else:
for i,j in zip(x,y):
if i ^ j:
result = "list is not equal"
break
print result
Using set:
if set(x).difference(set(y)):
print "list not equal"
else:
print "list equal"
I noticed a piece of code recently directly comparing two lists of integers like so:
a = [10,3,5, ...]
b = [5,4,3, ...,]
if a > b:
...
which seemed a bit peculiar, but I imagined it would return True if all of list_a's elements are larger then list_b's and False if each element is equal or list_b's elements are larger then list_a's. So I tested it:
>>> a=[3,3,3,3]
>>> b=[4,4,4,4]
>>> a>b
False
>>> b>a
True
Ok that works. As does:
>>> b = [1,1,1,1]
>>> a = [1,1,1,1]
>>> a>b
False
>>> b>a
False
but when it gets more fuzzy:
>>> a=[1,1,3,1]
>>> b=[1,3,1,1]
>>> a>b
False
>>> b>a
True
or:
>>> a=[1,3,1,1]
>>> b=[1,1,3,3]
>>> a>b
True
>>> b>a
False
the results are a bit stranger. What is python actually doing? It seems that it's returning the result in favour of the first list in which the left most element is greater then the corresponding?
From Comparing Sequences and Other Types in the Python tutorial:
The comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted.
See also the Wikipedia article about lexicographical order.
Since I didn't find the explanation of list/tuple comparison using "lexicographical ordering" particularly illuminating at first, here's an attempt to explain it "in my own words". First, here are some example lists that are referred to in the explanation below:
a = [1, 2, 3]
b = [1, 2, 10]
c = [1, 2, 3, 100]
d = [1, 2, 3]
e = [1, 2, 3, 4, 'a']
f = ['a', 'b', 'c']
The pair of items at each index are compared in turn. So, comparing a to b will result in 1 being compared to 1, 2 being compared to 2, and 3 being compared to 10.
The comparison of pairs will stop when either an unequal pair of items is found or--if the lists are different lengths--the end of the shorter list is reached.
For example, when comparing a and b, comparisons will stop when 3 and 10 are compared. When comparing b and c, comparisons will stop when 10 and 3 are compared.
As soon as an unequal pair is found, the overall result is the result of comparing the unequal items. This applies whether the lists are the same length or not--for example, list b is greater than list c because the 100 in c never comes into play.
For example, when comparing a to b, the overall result will be the result of comparing 3 to 10. a < b -> True because 3 is less than 10. a > b -> False because 3 is not greater than 10. a == b -> False because 3 does not equal 10.
If one of the lists is shorter and its N items are equal to the first N items of the longer list, as with a and c, the shorter list will be considered less than the longer list (so a is less than c).
Two lists will compare as equal only if they're the same length and all pairs of items compare as equal.
Note about types: if the items in a pair aren't comparable, the comparison will fail with a TypeError as usual. For example, comparing list a to f will fail when 1 is compared to 'a'. But also note that lists d and e can be compared since the 'a' in e is never compared to anything in d.
The given answers don't account for duplicates in the larger list, you can iterate over the bigger list and slice it each time to compare it with the sub list, this will maintain the order as well as account for duplicates.
This code would work:
def check_sublist(List,Sublist)
for i in range(len(List)):
if List[i:] ==Sublist:
return True
return False
Yes this is not time efficient but this is the only solution I could think since using set() would not maintain the order
I'd like to achieve following effect
a=[11, -1, -1, -1]
msg=['one','two','tree','four']
msg[where a<0]
['two','tree','four']
In similar simple fashion (without nasty loops).
PS. For curious people this if statement is working natively in one of functional languages.
//EDIT
I know that below text is different that the requirements above, but I've found what I wonted to acheave.
I don't want to spam another answer in my own thread, so I've also find some nice solution,
and I want to present it to you.
filter(lambda x: not x.endswith('one'),msg)
You can use list comprehensions for this. You need to match the items from the two lists, for which the zip function is used. This will generate a list of tuples, where each tuple contains one item from each of the original lists (i.e., [(11, 'one'), ...]). Once you have this, you can iterate over the result, check if the first element is below 0 and return the second element. See the linked Python docs for more details about the syntax.
[y for (x, y) in zip(a, msg) if x < 0]
The actual problem seems to be about finding items in the msg list that don't contain the string "one". This can be done directly:
[m for m in msg if "one" not in m]
[m for m, i in zip(msg, a) if i < 0]
The answers already posted are good, but if you want an alternative you can look at numpy and at its arrays.
>>> import numpy as np
>>> a = np.array([11, -1, -1, -1])
>>> msg = np.array(['one','two','tree','four'])
>>> a < 0
array([False, True, True, True], dtype=bool)
>>> msg[a < 0]
array(['two', 'tree', 'four'], dtype='|S4')
I don't know how array indexing is implemented in numpy, but it is usually fast and problably rewritten in C. Compared to the other solutions, this should be more readable, but it requires numpy.
I think [msg[i] for i in range(len(a)) if a[i]<0] will work for you.