Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does numpy addition work?

Tags:

python

numpy

I have the following unexpected behaviour

import numpy as np
class Test:
    def __radd__(self, other):
        print(f'value: {other}')

[1,2,3] + Test()
# prints: value: [1,2,3]
np.array([1,2,3]) + Test()
# prints
# value: 1
# value: 2
# value: 3

I would expect the second addition to behave in the same way as the first one, but it doesn't. The only logical explanation I can see is that the numpy + operator somehow iterates over the arguments first, and tries to add each one of them to Test(), and the second addition (int + Test) falls back to the Test.__radd__

So

  • is there any other explanation that I'm missing?
  • if not, how does a + b work in numpy
  • is it equivalent to np.add(a, b)?
like image 255
blue_note Avatar asked May 14 '18 13:05

blue_note


People also ask

How does NumPy calculate sum?

sum() function is available in the NumPy package of Python. This function is used to compute the sum of all elements, the sum of each row, and the sum of each column of a given array. Essentially, this sum ups the elements of an array, takes the elements within a ndarray, and adds them together.

How do I sum two NumPy arrays?

To add the two arrays together, we will use the numpy. add(arr1,arr2) method. In order to use this method, you have to make sure that the two arrays have the same length. If the lengths of the two arrays are​ not the same, then broadcast the size of the shorter array by adding zero's at extra indexes.

How does NumPy work in Python?

NumPy aims to provide an array object that is up to 50x faster than traditional Python lists. The array object in NumPy is called ndarray , it provides a lot of supporting functions that make working with ndarray very easy. Arrays are very frequently used in data science, where speed and resources are very important.

What do you get if you apply NumPy sum () to a list that contains only Boolean values?

sum receives an array of booleans as its argument, it'll sum each element (count True as 1 and False as 0) and return the outcome. for instance np. sum([True, True, False]) will output 2 :) Hope this helps.


2 Answers

This is because of NumPy 'broadcasting'.

Your explanation is pretty much correct as you can see in the docs for np.add.

Notes

Equivalent to x1 + x2 in terms of array broadcasting.

I find this makes a little more sense if you play around with NumPy and see how np.array differs from the built-in list.

Python 3.5.2 (default, Jul  6 2016, 16:37:16)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy as np

In [2]: [1, 2, 3] + [1, 2, 3]
Out[2]: [1, 2, 3, 1, 2, 3]

In [3]: np.array([1, 2, 3]) + np.array([1, 2, 3])
Out[3]: array([2, 4, 6])

Looking at the sourcecode here for the NDArrayOperatorsMixin which implements the special methods for almost all of Python's builtin operators defined in the `operator` module. It looks like __add__, __radd__, __iadd__ are all set to the .add umath function. But I'm not sure if the actual ndarray makes use of the mix-in, I think you'd have to dig through the C code to figure out how that's handled.

like image 98
Aaron Critchley Avatar answered Oct 14 '22 23:10

Aaron Critchley


If I expand your class a bit we may get more of an idea of when __add__ is used, and when __radd__:

class Test:
    def __radd__(self, other):
        print(f'r value: {other}, {type(other)}')
        return f'{other}'
    def __add__(self, other):
        print(f'a value: {other}, {type(other)}')
        return other+3

With a list

In [285]: [1,2,3]+Test()
r value: [1, 2, 3], <class 'list'>    # use Test.__radd__
Out[285]: '[1, 2, 3]'
In [286]: Test()+[1,2,3]             # tries to use Test.__add__
a value: [1, 2, 3], <class 'list'>
....
<ipython-input-280-cd3f564be47a> in __add__(self, other)
      5     def __add__(self, other):
      6         print(f'a value: {other}, {type(other)}')
----> 7         return other+3
      8 
TypeError: can only concatenate list (not "int") to list

With an array:

In [287]: np.arange(3)+Test()     # use Test.__radd__ with each array element
r value: 0, <class 'int'>
r value: 1, <class 'int'>
r value: 2, <class 'int'>
Out[287]: array(['0', '1', '2'], dtype=object)
In [288]: Test()+np.arange(3)
a value: [0 1 2], <class 'numpy.ndarray'>
Out[288]: array([3, 4, 5])    # use Test.__add__ on whole array

With itself, a double use of Test.__add__:

In [289]: Test()+Test()
a value: <__main__.Test object at 0x7fc33a5a7a20>, <class '__main__.Test'>
a value: 3, <class 'int'>
Out[289]: 6

As I commented it can be tricky sorting out the __add__ v __radd__ delegation, and separating that from ndarray action.


add with Test() second gives the same output as [287]:

In [295]: np.add(np.arange(3),Test())
r value: 0, <class 'int'>
r value: 1, <class 'int'>
r value: 2, <class 'int'>
Out[295]: array(['0', '1', '2'], dtype=object)

np.add with Test() first is different from [288] above:

In [296]: np.add(Test(),np.arange(3))
a value: 0, <class 'int'>
a value: 1, <class 'int'>
a value: 2, <class 'int'>
Out[296]: array([3, 4, 5], dtype=object)
like image 44
hpaulj Avatar answered Oct 14 '22 23:10

hpaulj