Summing tuples in python? - python

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.

Related

remove pairs of equal elements from two lists in sympy

I am quite new to Sympy (running with Python 3).
Given two lists of integers, in general not of equal lengths and whose elements might repeat, I need to eliminate all pairs of equal elements between the two lists.
For example, given
List_a=[1, 2, 3, 4, 4], List_b=[0, 2, 2, 4]
I shall eliminate the pairs (2,2) and (4,4) and output
List_ar=[1,3,4], List_br=[0,2]
If the two lists are equal I shall get two empty lists as output.
I have been trying to compare the lists element by element ( "for" and "while" loops) and when found equal to delete the pair from both lists. Upon that to repeat the procedure on the reduced lists till no deletion is made.
But lacking of a "goto" control I do not know how to handle the variable length lists.
Thanks for helping.
I you convert them to multiset you can compare the counts of common keys:
>>> from sympy.utilities.iterables import multiset
>>> ma = multiset(lista)
>>> mb = multiset(listb)
>>> for k in set(ma) & set(mb):
... n = min(ma[k],mb[k])
... ma[k] -= n
... mb[k] -= n
...
>>> lista = [i for k in ma for i in [k]*ma[k]]
>>> listb = [i for k in mb for i in [k]*mb[k]]
You could also treat this like a merge sort but the above is direct.
A general Python solution:
def g(a, b):
def ensure2(xs):
ys = [list(x) for x in xs]
if ys == []:
return [[], []]
else:
return ys
n = min(len(a), len(b))
c, d = ensure2(
zip(*
filter(lambda x: x[0] != x[1],
zip(a, b))))
return c + a[n:], d + b[n:]
a = [1,2,3,4,4]
b = [0,2,2,4]
c = [0,2,2,4]
d = []
# two different lists
ar, br = g(a, b)
print(ar) # [1, 3, 4]
print(br) # [0, 2]
# two identical lists
br, cr = g(b, c)
print(br) # []
print(cr) # []
# one empty list
ar, dr = g(a, d)
print(ar) # [1, 2, 3, 4, 4]
print(dr) # []
Use zip() to create the pairs
Use filter() to remove pairs with equal elements
Use zip(*...) to split the remaining pairs back to two lists
Use ensure2() to guard against empty lists
Use + list[n:] to append excessive elements back to the originally longer list

Python swap elements between two tuples of two ints

The problem is: swapping the second element between two tuples meeting all the following criteria: consisting of two ints, each in range(5), elements in same positions aren't equal
If they are two lists I can simply use variable swapping with indexing but tuples are immutable so I unpacked the two tuples into a single list and unpacked that list into four variables and reordered the variables...
Example Code
(See the initial values the variables are declared with, the printed values are the intended result.)
a, b = [1,2], [3,4]
a[1], b[1] = b[1], a[1]
print(a)
print(b)
l1, l2 = (1,2), (3,4)
a, b, c, d = [*l1, *l2]
print((a, d))
print((c, b))
In [161]: a, b = [1,2], [3,4]
...: a[1], b[1] = b[1], a[1]
...: print(a)
...: print(b)
...:
...: l1, l2 = (1,2), (3,4)
...: a, b, c, d = [*l1, *l2]
...: print((a, d))
...: print((c, b))
[1, 4]
[3, 2]
(1, 4)
(3, 2)
I don't want to cast the tuples into lists, I wonder if there is a better way to swap the second element between the two tuples?
You're right that the tuples are immutable, and you won't be able to get around creating new tuples. If you just want to do this without using unpacking notation, you could do something like
l1, l2 = (1,2), (3,4)
a = (l1[0],l2[1])
b = (l2[0],l1[1])
If speed is a concern here, running this with pypy will be very efficient.
Try this:
t = (1, 2)
swapped = t[::-1]
# true
assert type(swapped) == tuple
assert swapped == (2, 1)

Extended sequence unpacking in python3

I create a list as:
>>> seq = [1, 2, 3, 4]
Now i assign the variables as follows:
>>> a, b, c, d, *e = seq
>>> print(a, b, c, d, e)
I get output as:
>>> 1 2 3 4 []
Now i change the sequence in which i assign variables as:
>>> a, b, *e, c, d = seq
>>> print(a, b, c, d, e)
I get output as:
>>> 1, 2, 3, 4, []
So my question is Why *e variable above is always assigned an empty list regardless of where it appears?
It was a design choice, according to PEP 3132 (with my bold):
A tuple (or list) on the left side of a simple assignment (unpacking is not defined for augmented assignment) may contain at most one expression prepended with a single asterisk (which is henceforth called a "starred" expression, while the other expressions in the list are called "mandatory"). This designates a subexpression that will be assigned a list of all items from the iterable being unpacked that are not assigned to any of the mandatory expressions, or an empty list if there are no such items.
Indeed, the first example in the PEP illustrates your point:
>>> a, *b, c = range(5)
>>> a
0
>>> c
4
>>> b
[1, 2, 3]
In the first case
a, b, c, d, *e = seq
since the sequence has only four elements, a, b, c and d get all of them and the rest of them will be stored in e. As nothing is left in the sequence, e is an empty list.
In the second case,
a, b, *e, c, d = seq
First two elements will be assigned to a and b respectively. But then we have two elements after the *e. So, the last two elements will be assigned to them. Now, there is nothing left in the seq. That is why it again gets an empty list.
a, b, *e, c, d = [1, 2, 3, 4]
*e says "what's left put into e". Because you have 4 elements, empty sequence is put into e.

Count of pair element in list

a = [2,7,9]
b = [[7,9],[1,2],[2,9]]
How many pairs in list [a] matches pair tuple [b]
Notice both pair [7,9] and [2,9] in list [a]. Even though pair [1,2] contains a digit of 2, it doesn't get counted because both digits are not in list [a].
The return value should be 2, the len of the matching pairs.
len(filter(lambda l:if_in(b,l),a))
Need help creating a if_in function or a simpler way of writing this function all in one. How can I make this function work no matter the size of a or b.
Make a a set and use set.issuperset, checking if a is a superset of each sublist:
a = [2,7,9]
b = [[7,9],[1,2],[2,9]]
st = set(a)
print(sum(st.issuperset(sub) for sub in b))
2
You can go two ways, either making all the sublists sets or as above just make a set, when a is a superset of a sublist then the sublist is a subset of a:
In [6]: a = [2,7,9]
In [7]: b = [[7,9],[1,2],[2,9]]
In [8]: st = set(b[0])
In [9]: st.issubset(a)
Out[9]: True
In [10]: st = set(b[1])
In [11]: st.issubset(a)
Out[11]: False
In [13]: st = set(a)
In [13]: st.issuperset(b[0])
Out[13]: True
In [14]: st.issuperset(b[1])
Out[14]: False
Obviously as there are only a few numbers in your a list it make more sense to make a set from a and use superset.
If you want to work with the subsets of a, you can use itertools.combinations(iterable, r).
Quoting the docs, returns r length subsequences of elements from the input iterable.
from itertools import combinations
a = [2, 7, 9]
b = [(7, 9), (1, 2), (2, 9)]
# combinations returns a collection of tuples
# so changed your b items to tuples.
len(filter(lambda x: x in b, combinations(a ,2))

Elegant way to perform tuple arithmetic

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 ))")

Categories