What is the most elegant and concise way (without creating my own class with operator overloading) to perform tuple arithmetic in Python 2.7?
Lets say I have two tuples:
a = (10, 10)
b = (4, 4)
My intended result is
c = a - b = (6, 6)
I currently use:
c = (a[0] - b[0], a[1] - b[1])
I also tried:
c = tuple([(i - j) for i in a for j in b])
but the result was (6, 6, 6, 6). I believe the above works as a nested for loops resulting in 4 iterations and 4 values in the result.
If you're looking for fast, you can use numpy:
>>> import numpy
>>> numpy.subtract((10, 10), (4, 4))
array([6, 6])
and if you want to keep it in a tuple:
>>> tuple(numpy.subtract((10, 10), (4, 4)))
(6, 6)
One option would be,
>>> from operator import sub
>>> c = tuple(map(sub, a, b))
>>> c
(6, 6)
And itertools.imap can serve as a replacement for map.
Of course you can also use other functions from operator to add, mul, div, etc.
But I would seriously consider moving into another data structure since I don't think this type of problem is fit for tuples
Use zip and a generator expression:
c = tuple(x-y for x, y in zip(a, b))
Demo:
>>> a = (10, 10)
>>> b = (4, 4)
>>> c = tuple(x-y for x, y in zip(a, b))
>>> c
(6, 6)
Use itertools.izip for a memory efficient solution.
help on zip:
>>> print zip.__doc__
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
JFYI, execution time in my laptop in 100000 times iteration
np.subtract(a, b) : 0.18578505516052246
tuple(x - y for x, y in zip(a, b)) :
0.09348797798156738
tuple(map(lambda x, y: x - y, a, b)) : 0.07900381088256836
from operator import sub tuple(map(sub, a, b)) : 0.044342041015625
operator looks more elegant for me.
This can also be done just as nicely without an import at all, although lambda is often undesirable:
tuple(map(lambda x, y: x - y, a, b))
If you are looking to get the distance between two points on say a 2d coordinate plane you should use the absolute value of the subtraction of the pairs.
tuple(map(lambda x ,y: abs(x - y), a, b))
Since in your question there only examples of 2-number-tuples, for such coordinate-like tuples, you may be good with simple built-in "complex" class:
>>> a=complex(7,5)
>>> b=complex(1,2)
>>> a-b
>>> c=a-b
>>> c
(6+3j)
>>> c.real
6.0
>>> c.imag
3.0
As an addition to Kohei Kawasaki's answer, for speed, the original solution was actually the fastest (For a length two tuple at least).
>>> timeit.timeit('tuple(map(add, a, b))',number=1000000,setup='from operator import add; a=(10,11); b=(1,2)')
0.6502681339999867
>>> timeit.timeit('(a[0] - b[0], a[1] - b[1])',number=1000000,setup='from operator import add; a=(10,11); b=(1,2)')
0.19015854899998885
>>>
my element-wise tuple arithmetic helper
supported operations: +, -, /, *, d
operation = 'd' calculates distance between two points on a 2D coordinate plane
def tuplengine(tuple1, tuple2, operation):
"""
quick and dirty, element-wise, tuple arithmetic helper,
created on Sun May 28 07:06:16 2017
...
tuple1, tuple2: [named]tuples, both same length
operation: '+', '-', '/', '*', 'd'
operation 'd' returns distance between two points on a 2D coordinate plane (absolute value of the subtraction of pairs)
"""
assert len(tuple1) == len(tuple2), "tuple sizes doesn't match, tuple1: {}, tuple2: {}".format(len(tuple1), len(tuple2))
assert isinstance(tuple1, tuple) or tuple in type(tuple1).__bases__, "tuple1: not a [named]tuple"
assert isinstance(tuple2, tuple) or tuple in type(tuple2).__bases__, "tuple2: not a [named]tuple"
assert operation in list("+-/*d"), "operation has to be one of ['+','-','/','*','d']"
return eval("tuple( a{}b for a, b in zip( tuple1, tuple2 ))".format(operation)) \
if not operation == "d" \
else eval("tuple( abs(a-b) for a, b in zip( tuple1, tuple2 ))")
Related
Suppose I have the following list: A = [1,2,3,4], By using the reduce function, to find the product of the elements, I could do
prodA = reduce(lambda x,y: x*y, A)
However, if I have another list B=[9,8,7,6], is it still possible for me to use the reduce function to execute the following action? (from top to the bottom indicate the steps)
9
(9+1)* 2
((9+1) *2)+8
(((9+1) *2)+8)*3
((((9+1) *2)+8)*3)+7
(((((9+1) *2)+8)*3)+7)*4
((((((9+1) *2)+8)*3)+7)*4)+6
I'm not pretty sure if I could add something like an iterator for the list B to the reduce function. How I can do that? Thanks a lot!
This looks like a job for zip. Specifically, we're going to zip the two lists together, and then we'll express our reduction function as a function that takes tuples instead of simple integers.
Zipping our lists together gives us
>>> list(zip(A, B))
[(1, 9), (2, 8), (3, 7), (4, 6)]
And your function, at each step, multiplies by an element of A and then adds an element of B. So, starting with 1 (which is a sensible default since the first thing we do is multiply, so the 1 will be the identity for that first operation), multiply by the first element of the tuple and add the second.
reduce(lambda x, y: x * y[0] + y[1], zip(A, B), 1)
And, with your inputs, we get 370, which is equal to
((((9+1)*2)+8)*3+7)*4+6
You can do it using zip as the input and 1 as the initial value:
from functools import reduce
A = [1,2,3,4]
B = [9,8,7,6]
r = reduce(lambda r,ab: r*ab[0]+ab[1],zip(A,B),1)
print(r) # 370
I have a list of numpy arrays, say,
a = [np.random.rand(3, 3), np.random.rand(3, 3), np.random.rand(3, 3)]
and I have a test array, say
b = np.random.rand(3, 3)
I want to check whether a contains b or not. However
b in a
throws the following error:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
What is the proper way for what I want?
You can just make one array of shape (3, 3, 3) out of a:
a = np.asarray(a)
And then compare it with b (we're comparing floats here, so we should use isclose())
np.all(np.isclose(a, b), axis=(1, 2))
For example:
a = [np.random.rand(3,3),np.random.rand(3,3),np.random.rand(3,3)]
a = np.asarray(a)
b = a[1, ...] # set b to some value we know will yield True
np.all(np.isclose(a, b), axis=(1, 2))
# array([False, True, False])
As highlighted by #jotasi the truth value is ambiguous due to element-wise comparison within the array.
There was a previous answer to this question here. Overall your task can be done in various ways:
list-to-array:
You can use the "in" operator by converting the list to a (3,3,3)-shaped array as follows:
>>> a = [np.random.rand(3, 3), np.random.rand(3, 3), np.random.rand(3, 3)]
>>> a= np.asarray(a)
>>> b= a[1].copy()
>>> b in a
True
np.all:
>>> any(np.all((b==a),axis=(1,2)))
True
list-comperhension:
This done by iterating over each array:
>>> any([(b == a_s).all() for a_s in a])
True
Below is a speed comparison of the three approaches above:
Speed Comparison
import numpy as np
import perfplot
perfplot.show(
setup=lambda n: np.asarray([np.random.rand(3*3).reshape(3,3) for i in range(n)]),
kernels=[
lambda a: a[-1] in a,
lambda a: any(np.all((a[-1]==a),axis=(1,2))),
lambda a: any([(a[-1] == a_s).all() for a_s in a])
],
labels=[
'in', 'np.all', 'list_comperhension'
],
n_range=[2**k for k in range(1,20)],
xlabel='Array size',
logx=True,
logy=True,
)
Ok so in doesn't work because it's effectively doing
def in_(obj, iterable):
for elem in iterable:
if obj == elem:
return True
return False
Now, the problem is that for two ndarrays a and b, a == b is an array (try it), not a boolean, so if a == b fails. The solution is do define a new function
def array_in(arr, list_of_arr):
for elem in list_of_arr:
if (arr == elem).all():
return True
return False
a = [np.arange(5)] * 3
b = np.ones(5)
array_in(b, a) # --> False
This error is because if a and b are numpy arrays then a == b doesn't return True or False, but array of boolean values after comparing a and b element-wise.
You can try something like this:
np.any([np.all(a_s == b) for a_s in a])
[np.all(a_s == b) for a_s in a] Here you are creating list of boolean values, iterating through elements of a and checking if all elements in b and particular element of a are the same.
With np.any you can check if any element in your array is True
As pointed out in this answer, the documentation states that:
For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression x in y is equivalent to any(x is e or x == e for e in y).
a[0]==b is an array, though, containing an element-wise comparison of a[0] and b. The overall truth value of this array is obviously ambiguous. Are they the same if all elements match, or if most match of if at least one matches? Therefore, numpy forces you to be explicit in what you mean. What you want to know, is to test whether all elements are the same. You can do that by using numpy's all method:
any((b is e) or (b == e).all() for e in a)
or put in a function:
def numpy_in(arrayToTest, listOfArrays):
return any((arrayToTest is e) or (arrayToTest == e).all()
for e in listOfArrays)
Use array_equal from numpy
import numpy as np
a = [np.random.rand(3,3),np.random.rand(3,3),np.random.rand(3,3)]
b = np.random.rand(3,3)
for i in a:
if np.array_equal(b,i):
print("yes")
I'm trying to write a function for adding 2D vectors.
I'm trying to combine the map() function, getting a list using the zip() function (which will zip 2 tuples).
This is the code:
a = (1, 2)
b = (3, 4)
c = list(map(lambda x, y: x+y, list(zip(a, b))))
print(c)
So the way I see it, zip(a, b) returns a zip object containing the following tuples: (1, 3), (2, 4). It is then converted to a list. I'm getting the following error:
TypeError: () missing 1 required positional argument: 'y'
So my guess is that the lambda function is not taking the second number in each tuple.
Is there any way to do this?
Only one parameter for lambda like:
c = list(map(lambda x: sum(x), zip(a, b)))
But once we are using sum, we can map it directly like:
c = list(map(sum, zip(a, b)))
Test Code:
a = (1, 2)
b = (3, 4)
c = list(map(sum, zip(a, b)))
print(c)
Result:
[4, 6]
In Python2, you can use specific unpacking syntax in a lambda:
a = (1, 2)
b = (3, 4)
c = list(map(lambda (x, y): x+y, list(zip(a, b))))
However, in Python3, you will have to use sum:
c = list(map(lambda x_y: sum(x_y), list(zip(a, b))))
zip returns a tuples, so you could sum like this example:
list(map(lambda x: x[0] + x[1], zip(a, b)))
output:
[4, 6]
The lambda receives one parameter, which is a 2-tuple returned by zip.
You can access it element-wise: lambda pair: pair[0] + pair[1].
You can apply sum() to it.
In Python 2, you can unpack it right in the signature: lambda (x, y): x + y.
Is there anyway to sum two points not using "class point"
Input
a= (2,5)
b= (3,4)
c= a +b
Output
(5 , 9)
You can use a comprehension plus zip:
c = tuple(a_n + b_n for a_n, b_n in zip(a, b))
This is obviously cumbersome if you need to do it a lot (not to mention slightly inefficient). If you are going to be doing this sort of computation a lot, then you're better off using a library like numpy which allows arrays to be added as first-class objects.
import numpy as np
a = np.array([2, 5])
b = np.array([3, 4])
c = a + b
If you go the numpy route, converting to and from numpy arrays is a bit expensive so I'd recommend that you store your points as arrays rather than tuple.
If you'd like a functional approach:
t = tuple(map(sum, zip(a, b)))
import numpy
a = (2,5)
b = (3,4)
c = tuple(numpy.asarray(a) + numpy.asarray(b)) #Tuple convert is just because this is how your output defined. you can skip it...
Complex numbers are (2-)tuples in disguise:
>>> a = 2+5j
>>> b = 3+4j
>>> c = a + b
>>> c
(5+9j)
My solution:
reduce(lambda x, y: (x[0] + y[0], x[1] + y[1]), zip(a, b))
pt1 and pt2 are two tuple made up of ints.
pairs = zip(pt1, pt2)
sum_sq_diffs = sum((a - b)**2 for a, b in pairs)
return (sum_sq_diffs)
My question concerns the second line. What are a and b? If you print them by doing:
print list((a,b) for a, b in pairs))
you get [(pt1x,pt2x), (pt1y, pt2y)]
If I take two tuples and subtract them, you get an error. So how come sum_sq_diffs = sum((a - b)**2 for a, b in pairs) doesn't result in an error? It seems a is a tuple and b is a tuple.
You understand that pairs is a list of tuples.
Now, the second line is a list comprehension which is the equivalent of
sum_sq_diffs = 0
for a, b in pairs:
sum_sq_diffs += (a - b)**2
Now, while iterating through the individual elements, python would do a "tuple unpacking" for you , and extracts the (x, y) to local variables a and b respectively.
You can read more on tuple unpacking here
This is called, appropriately enough, tuple unpacking. Tuple unpacking requires that the list of variables on the left has the same number of elements as the length of the tuple. Note that multiple assignment is really just a combination of tuple packing and tuple unpacking!
Here is a quick demo which should demonstrate this:
>>> pt1 = [1, 2, 3]
>>> pt2 = [4, 5, 6]
>>> pairs = zip(pt1, pt2)
>>> pairs
[(1, 4), (2, 5), (3, 6)]
>>> sum_sq_diffs = sum((a - b)**2 for a, b in pairs)
>>> sum_sq_diffs
27
>>> sum_sq_diffs_2 = 0
>>> for a, b in pairs:
... print a, b
... sum_sq_diffs_2 += (a - b)**2
...
1 4
2 5
3 6
>>> sum_sq_diffs_2
27
>>>
pairs should be a list of tuples.
a, b should unpack those tuples, one at a time. So they should be scalars.
There is much to like about list comprehensions and generator expressions. One thing I don't like about them, is you tend to end up rewriting them as loops when you have a question about how they are behaving.
But I do suggest rewriting your comprehension as a loop, and inserting a print(a,b) or print a, b. It may help.