2

Hi guys im just a rookie in python (even in programming) so my question might sound very basic but i have a hard time to understand this.

Why is the selective behavior on arithmetics on 'float object'?

import numpy as np

a = np.random.normal(size=10)
a = np.abs(a)
b = np.array(a, dtype=object)

np.square(a) # this works
np.square(b) # this works

np.sqrt(a) # this works
np.sqrt(b) # AttributeError: 'float' object has no attribute 'sqrt'

The image link is my run result in local jupyter notebook:

jupyter notebook run result enter image description here

Appreciate useful insights! thanks


edit 050418 09:53 --add a link that i think is similar issue Numpy AttributeError: 'float' object has no attribute 'exp'

jesse
  • 105
  • 1
  • 10
  • the author apologize in advance since he does not know how to output the ipython console to show the results on screen directly ><; – jesse Apr 05 '18 at 01:32
  • With object dtype arrays, `numpy` delegates the calculation to the elements (sort of like a list comprehension). The result is that some calculations work, others don't. You have found a pair that illustrate this. Both are compiled, so it isn't easy to see how the delegation is different. – hpaulj Apr 05 '18 at 01:41
  • hi @hpaulj...do you mean compiled in the documentation? I did not find anything last time I search inside it. Can you provide any links? tq – jesse Apr 05 '18 at 01:48
  • 1
    I check for Python code with the `ipython` double question mark:`??` As a general rule, don't count on doing math on object dtype arrays. It's hit and miss. And even when it works, it is slower than with numeric dtypes. – hpaulj Apr 05 '18 at 01:51
  • 1
    numpy `square` knows that it can delegate the operation to the object's `__mul__` method, if it has one, which a Python `float` object does. If an object has a `sqrt` method, `numpy.sqrt` will delegate to that. The Python `float` doesn't have a `sqrt` method. – Warren Weckesser Apr 05 '18 at 01:52
  • Thanks everyone. Great help! – jesse Apr 05 '18 at 01:56

3 Answers3

1

@Warren points out that square 'delegates' to multiply. I verified this by making an object array that includes a list:

In [524]: arr = np.array([np.arange(3), 3, [3,4]])
In [525]: np.square(arr)
TypeError: can't multiply sequence by non-int of type 'list'

square works on the rest of the array:

In [526]: np.square(arr[:2])
Out[526]: array([array([0, 1, 4]), 9], dtype=object)

sqrt doesn't work on any of these:

In [527]: np.sqrt(arr)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-527-b58949107b3d> in <module>()
----> 1 np.sqrt(arr)

AttributeError: 'numpy.ndarray' object has no attribute 'sqrt'

I can make sqrt work with a custom class:

class Foo(float):
    def sqrt(self):
        return self**0.5

In [539]: arr = np.array([Foo(3), Foo(2)], object)
In [540]: np.square(arr)
Out[540]: array([9.0, 4.0], dtype=object)
In [541]: np.sqrt(arr)
Out[541]: array([1.7320508075688772, 1.4142135623730951], dtype=object)
hpaulj
  • 221,503
  • 14
  • 230
  • 353
0

Not sure if this is the reason but my guess is:

What if your objects were instances of your own homemade class? Numpy cannot do anything else but hope that you have defined a sqrt method in your class and let it do the work. So I assume that's what it does here, but in your case your objects are floats, and although np.sqrt(some_random_float) makes sense, some_random_float.sqrt() doesn't because the AttributeError: 'float' object has no attribute 'sqrt'.

Typically numpy is designed and optimized for numerical computations, while using it with generic dtype=object can be sometime convenient, you shouldn't assume everything will always run seamlessly in this case...

Julien
  • 13,986
  • 5
  • 29
  • 53
  • Okay that is logic. But how about the instance like .square()? Does it mean that np has listed some of common instances, in this case square() is included? – jesse Apr 05 '18 at 01:50
0

In Python, everything is an object.

Operators are implemented on objects either as special methods (as seen from the __ prefix in the method name, such as a + b is syntactic sugar for a.__add__(b) or they are aliases for builtin functions that take the objects as arguments (such as a ** b is syntatic sugar for pow(a, b). And often, the functions themselves are aliases back to special methods on the objects (like iter(a) just returns a.__iter__()).

Numpy adds further syntactic sugar, where it implements functions that behave based on the type of numpy object. As stated above, a numpy array with dtype of object pushes the implementation of the operator back down to the object type of the element in the array (so basically, np.square(a) is semantically similar to array = map(lambda x: x*x, array) which is really evaluated as array = map(lambda x: x.__mult__(x), array).

Note that sqrt does not exist as a builtin function (it is either imported from the math module in the standard library or np's implementation or by using **0.5 (which is really pow(x, 0.5)), and therefore a normal float object will not have a method that implements it.

cowbert
  • 3,212
  • 2
  • 25
  • 34
  • great explanation thanks! the example shown in the hpaulj answer is the testament to this – jesse Apr 05 '18 at 02:28