4
import numpy as np
import astropy as ap

def mass(FWHM, lumi):
    abs_lumi = bhm.L_1450(lumi)
    s1 = (FWHM/1000)
    s2 = ((abs_lumi)/(10**44))
    s = [(s1**2)*(s2**0.53)]
    #mass = np.log10((s1**2)*(s2**0.53)) + 6.66 #old way, didn't work
    mass = np.log10(s) + 6.66
    return mass

I'm trying to use the numpy log10 function, but I keep getting an error that reads:

AttributeError: 'float' object has no attribute 'log10'

I tried putting my argument into a list (s variable), but I got the same error message. FWHM and lumi are both numbers with decimal points (I think they're called floating point numbers).

Kaila
  • 63
  • 1
  • 1
  • 5
  • 3
    what is `bhm`? Why do you tag and import astropy? It's not used anywhere. What _exactly_ is your input (make it reproduciple)? If I ignore the `bhm.L_1450` call and just use `lumi` everything works fine with integers, floats and arrays. What numpy version are you using? – MSeifert Nov 13 '16 at 18:00

2 Answers2

6

The answer to this is a bit tricky and requires a bit of knowledge how Python handles integers and how numpy coerces types. Thanks @ali_m for your comment!

Assuming 64 bit integer the biggest representable integer is 9,223,372,036,854,775,807 (see for example Wikipedia) which is roughly 10**19. But Python falls back to unlimited integer representations as soon as this value is exceeded (like in your case 10**44). But this unlimited precision type is not nativly supported by NumPy, so results will fall back to objects and these object arrays don't support all (any?) ufuncs, like np.log10.

The solution is simple: Convert this large number to a floating point number:

>>> # Negative powers will result in floats: 44 -> -44, * instead of /
>>> np.array([10, 20]) * 10**-44
array([  1.00000000e-43,   2.00000000e-43])

>>> # you could also make the base a float: 10 -> 10.
>>> np.array([10, 20]) / 10.**44
array([  1.00000000e-43,   2.00000000e-43])

>>> # or make the exponent a float: 44 -> 44.
>>> np.array([10, 20]) / 10**44.
array([  1.00000000e-43,   2.00000000e-43])

>>> # manually cast the result to a float
>>> np.array([10, 20]) / float(10**44)
array([  1.00000000e-43,   2.00000000e-43])

>>> # not working, result is an object array which cannot be used for np.log10
>>> np.array([10, 20]) / (10**(44))
array([1e-43, 2e-43], dtype=object)
>>> #                       ^---------that's the problem!

All you need is to change the third line in your function:

import numpy as np

def mass(FWHM, lumi):
    s1 = FWHM / 1000
    s2 = lumi * 10**-44   # changed line, using the first option.
    s = s1**2 * s2**0.53
    mass = np.log10(s) + 6.66
    return mass

This works at least with all my test inputs, for example:

>>> mass(np.array([10., 20]), np.array([10., 20]))
array([-20.13      , -19.36839411])
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • 2
    I think the issue is related to the fact that `(10**(44))` yields an integer that is too large to be represented by any of numpy's integer dtypes. `np.array(10**44)` is an `np.object` array, containing a native Python integer. `np.iinfo(np.int64).max == 9223372036854775807`, which is only about 10¹⁹. If the exponentiation is performed on floats rather than ints then you will get an `np.float64` array instead - `np.array(10.0**44.0).dtype == np.float64`. Likewise, if the exponent is negative then you will, of course, get a float result. – ali_m Nov 13 '16 at 20:58
2

The reason for the problem is as explained above. One simple solution is to convert the type of array using .astype().

any_np_array = []
any_np_array = any_np_array.astype(float)
Vaibhav Vishal
  • 6,576
  • 7
  • 27
  • 48
sram022
  • 21
  • 1