Python element-wise tuple operations like sum
Asked Answered
C

15

148

Is there anyway to get tuple operations in Python to work like this:

>>> a = (1,2,3)
>>> b = (3,2,1)
>>> a + b
(4,4,4)

instead of:

>>> a = (1,2,3)
>>> b = (3,2,1)
>>> a + b
(1,2,3,3,2,1)

I know it works like that because the __add__ and __mul__ methods are defined to work like that. So the only way would be to redefine them?

Conquest answered 31/1, 2009 at 0:51 Comment(0)
S
163
import operator
tuple(map(operator.add, a, b))
Sara answered 31/1, 2009 at 0:52 Comment(8)
I'd say this is the most pythonic solution.Pale
Except that map() is semi-deprecated. See artima.com/weblogs/viewpost.jsp?thread=98196 for an article by Guido where it mentions how map is better written as a list comprehension.Coessential
It also blows up if a & b don't contain the same number of elements, or aren't "addable" (ex: map(operator.add, (1,2), ("3", "4"))Coessential
tuple([item1 + item2 for item1, item2 in zip(a, b)]) would be the equivalent as a list comprehension.Coessential
@Adam Does the list comprehension also evaluate lazily? I'm all for a single way to do things so long as everything is evaluated lazily until I choose otherwise.Classis
@Eyal: I don't believe it's lazily evaluated, but I'm not entirely sure.Coessential
@AdamParkin, generator comprehensions are even better tuple(item1 + item2 for item1, item2 in zip(a, b)).Concord
You can also make it way shorter if you know the class cls of the objects tuple(map(cls.__add__, a, b)). For example if you know you have ints, tuple(map(int.__add__, a, b)). And maybe even generalize for any operation tuple(map(cls.__<your operator>__, a, b))Aretina
F
174

Using all built-ins..

tuple(map(sum, zip(a, b)))
Footwall answered 31/1, 2009 at 2:49 Comment(4)
This seems to be the simpler, superior answer. Why isn't it accepted?Randeerandel
it's good, but technically not what's asked for because map returns a list, not a tuple... so: tuple(map(sum,zip(a,b))Mesognathous
The syntax is mystic.Margenemargent
The benefit of this one is that you can extend it to: tuple(map(sum,zip(a,b, c))Cruciferous
S
163
import operator
tuple(map(operator.add, a, b))
Sara answered 31/1, 2009 at 0:52 Comment(8)
I'd say this is the most pythonic solution.Pale
Except that map() is semi-deprecated. See artima.com/weblogs/viewpost.jsp?thread=98196 for an article by Guido where it mentions how map is better written as a list comprehension.Coessential
It also blows up if a & b don't contain the same number of elements, or aren't "addable" (ex: map(operator.add, (1,2), ("3", "4"))Coessential
tuple([item1 + item2 for item1, item2 in zip(a, b)]) would be the equivalent as a list comprehension.Coessential
@Adam Does the list comprehension also evaluate lazily? I'm all for a single way to do things so long as everything is evaluated lazily until I choose otherwise.Classis
@Eyal: I don't believe it's lazily evaluated, but I'm not entirely sure.Coessential
@AdamParkin, generator comprehensions are even better tuple(item1 + item2 for item1, item2 in zip(a, b)).Concord
You can also make it way shorter if you know the class cls of the objects tuple(map(cls.__add__, a, b)). For example if you know you have ints, tuple(map(int.__add__, a, b)). And maybe even generalize for any operation tuple(map(cls.__<your operator>__, a, b))Aretina
R
45

This solution doesn't require an import:

tuple(map(lambda x, y: x + y, tuple1, tuple2))
Rabelaisian answered 3/10, 2012 at 6:20 Comment(1)
This solution is also faster than the other no-import, one-liner solution (map(sum, zip(a, b)))Idle
D
23

Sort of combined the first two answers, with a tweak to ironfroggy's code so that it returns a tuple:

import operator

class stuple(tuple):
    def __add__(self, other):
        return self.__class__(map(operator.add, self, other))
        # obviously leaving out checking lengths

>>> a = stuple([1,2,3])
>>> b = stuple([3,2,1])
>>> a + b
(4, 4, 4)

Note: using self.__class__ instead of stuple to ease subclassing.

Danu answered 31/1, 2009 at 1:10 Comment(0)
O
22
from numpy import array

a = array( [1,2,3] )
b = array( [3,2,1] )

print a + b

gives array([4,4,4]).

See http://www.scipy.org/Tentative_NumPy_Tutorial

Oteliaotero answered 31/1, 2009 at 22:25 Comment(1)
This will work, but it's a bit heavy to import numpy just for a simple addition operation.Wodge
T
20

Generator comprehension could be used instead of map. Built-in map function is not obsolete but it's less readable for most people than list/generator/dict comprehension, so I'd recommend not to use map function in general.

tuple(p+q for p, q in zip(a, b))
Tournament answered 14/1, 2016 at 20:22 Comment(0)
M
6

simple solution without class definition that returns tuple

import operator
tuple(map(operator.add,a,b))
Monjan answered 24/6, 2010 at 16:10 Comment(0)
O
6

All generator solution. Not sure on performance (itertools is fast, though)

import itertools
tuple(x+y for x, y in itertools.izip(a,b))
Oteliaotero answered 26/9, 2012 at 21:25 Comment(0)
I
6

even simpler and without using map, you can do that

>>> tuple(sum(i) for i in zip((1, 2, 3), (3, 2, 1)))
(4, 4, 4)
Injudicious answered 12/6, 2016 at 23:17 Comment(0)
D
3

Yes. But you can't redefine built-in types. You have to subclass them:

class MyTuple(tuple):
    def __add__(self, other):
         if len(self) != len(other):
             raise ValueError("tuple lengths don't match")
         return MyTuple(x + y for (x, y) in zip(self, other))
Doan answered 31/1, 2009 at 0:55 Comment(1)
but then you can't use the tuple syntax.Zielsdorf
S
3

I currently subclass the "tuple" class to overload +,- and *. I find it makes the code beautiful and writing the code easier.

class tupleN(tuple):
    def __add__(self, other):
        if len(self) != len(other):
             return NotImplemented
        else:
             return tupleN(x+y for x,y in zip(self,other))
    def __sub__(self, other):
        if len(self) != len(other):
             return NotImplemented
        else:
             return tupleN(x-y for x,y in zip(self,other))
    def __mul__(self, other):
        if len(self) != len(other):
             return NotImplemented
        else:
             return tupleN(x*y for x,y in zip(self,other))


t1 = tupleN((1,3,3))
t2 = tupleN((1,3,4))
print(t1 + t2, t1 - t2, t1 * t2, t1 + t1 - t1 - t1)
(2, 6, 7) (0, 0, -1) (1, 9, 12) (0, 0, 0)
Spiegleman answered 25/6, 2019 at 18:6 Comment(0)
C
0

Here is another handy solution if you are already using numpy. It is compact and the addition operation can be replaced by any numpy expression.

import numpy as np
tuple(np.array(a) + b)
Conversationalist answered 17/12, 2020 at 16:23 Comment(0)
H
0

I keep coming back to this question, and I don't particularly like any of the answers as they are all answering the question for the general case, and I'm normally looking for the answer to a special case: I'm normally using a fixed tuple count, e.g. for n-dimensions.

   # eg adding a dx/dy to an xy point.
   # if I have a point xy and another point dxdy
   x, y = xy
   dx, dy = dxdy
   return x+dx, y+dy

while I normally shudder at unnecessary variables, the reason why I am unpacking a tuple is normally because I am working on the elements as individuals, and that is what is happening with tuple addition as requested above.

Halbeib answered 28/12, 2021 at 22:32 Comment(0)
K
0

minimal class for all common numeric binary and unary operators
what i would want by default from a tuple-like data structure in any language

from math import ceil,floor,trunc
from operator import (add,and_,eq,floordiv,ge,gt,invert,le,lshift,lt,mod,mul,ne,
  neg,or_,pos,rshift,sub,truediv,xor,)
from itertools import repeat
from typing import Iterable
class ntuple(tuple):
  def __lt__(a,b): return ntuple(map(lt,a,a._b(b)))
  def __le__(a,b): return ntuple(map(le,a,a._b(b)))
  def __eq__(a,b): return ntuple(map(eq,a,a._b(b)))
  def __ne__(a,b): return ntuple(map(ne,a,a._b(b)))
  def __gt__(a,b): return ntuple(map(gt,a,a._b(b)))
  def __ge__(a,b): return ntuple(map(ge,a,a._b(b)))
  def __add__(a,b): return ntuple(map(add,a,a._b(b)))
  def __sub__(a,b): return ntuple(map(sub,a,a._b(b)))
  def __mul__(a,b): return ntuple(map(mul,a,a._b(b)))
  def __matmul__(a,b): return sum(map(mul,a,a._b(b)))
  def __truediv__(a,b): return ntuple(map(truediv,a,a._b(b)))
  def __floordiv__(a,b): return ntuple(map(floordiv,a,a._b(b)))
  def __mod__(a,b): return ntuple(map(mod,a,a._b(b)))
  def __divmod__(a,b): return ntuple(map(divmod,a,a._b(b)))
  def __pow__(a,b,m=None): return ntuple(pow(a,b,m) for a,b in zip(a,a._b(b)))
  def __lshift__(a,b): return ntuple(map(lshift,a,a._b(b)))
  def __rshift__(a,b): return ntuple(map(rshift,a,a._b(b)))
  def __and__(a,b): return ntuple(map(and_,a,a._b(b)))
  def __xor__(a,b): return ntuple(map(xor,a,a._b(b)))
  def __or__(a,b): return ntuple(map(or_,a,a._b(b)))
  def __neg__(a): return ntuple(map(neg,a))
  def __pos__(a): return ntuple(map(pos,a))
  def __abs__(a): return ntuple(map(abs,a))
  def __invert__(a): return ntuple(map(invert,a))
  def __round__(a,n=None): return ntuple(round(e,n) for e in a)
  def __trunc__(a): return ntuple(map(trunc,a))
  def __floor__(a): return ntuple(map(floor,a))
  def __ceil__(a): return ntuple(map(ceil,a))
  def _b(a,b): return b if isinstance(b,Iterable) else repeat(b,len(a))
ntuple((-1, 0, 2)) + (6, 4, 2) = (5, 4, 4)

ntuple((-1, 0, 2)) +  2 = (1, 2, 4)
ntuple((-1, 0, 2)) ** 2 = (1, 0, 4)
ntuple(( 1, 2, 3)) << 2 = (4, 8, 12)

-ntuple((-1, 0, 2)) = (1, 0, -2)

round(ntuple((-1.5, 0.6, 2.4))) = (-2, 1, 2)

sum(ntuple(a...)*(b...)) == ntuple(a...)@(b...)

Koval answered 29/6, 2023 at 18:22 Comment(0)
V
-2

In case someone need to average a list of tuples:

import operator 
from functools import reduce
tuple(reduce(lambda x, y: tuple(map(operator.add, x, y)),list_of_tuples))
Victory answered 20/1, 2018 at 4:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.