-1

I finally figured out why I was getting a "weird" error in a function call. But I do not understand WHY I got the error or how to avoid it in the future.

The error was:

rates  = Qi*np.exp(-Di*(days-T0))

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

This question was asked over 5 years ago (Numpy AttributeError: 'float' object has no attribute 'exp' and it is similar to Different behavior of arithmetics on dtype float 'object' and 'float'), but neither answer tells me how to arbitrarily avoid the problem.

I am using pandas and numpy. The function is called as follows:

df3['Model_EXP'], df3['Model_EXPcum'] =arps.arps(days = df3[dayscol], Qi=parmexp[0], Di=parmexp[1], T0=parmexp[2])

where df3 is a dataframe, the column df3[dayscol] is a column of type int64, and parmexp is a tuple containing parameters for the function.

The function was originally written for numpy (not pandas) and expects numpy vectors. The start of the function def is as follows:

def arps(days = None, Qi = None, Di = None, Bi = 0., T0=None, DTMIN=-5., QMIN = 1., Oil=True):
   if (not Di or not Qi):
      print("Nominal Decline or Rate not entered in routine arps.  Stopping.")
      sys.exit()
   if (np.isnan(np.sum(days))):
          print("Error in days passed to routine arps.  Some non-days in vector.  Stopping.")
      sys.exit()
   if (Di < 0.):
      Di = -1.*Di                                         # just in case someone got their signs wrong
   #
   rates   = np.zeros(len(days))
   cums    = np.zeros(len(days))
   dtval   = np.zeros(len(days))
   if (T0 == None):
      T0 = days[0]
   if (Bi == 0.):                                             #  Exponential Decline
      rates  = Qi*np.exp(-Di*(days-T0))                       #  THIS IS WHERE THINGS GET INTERESTING

I have called this function multiple times in my application. It had always worked. I added another function to call this routine with another dataframe. However, even though this works during the same execution, I started getting the following error:

  File "...Local\conda\conda\envs\py36\lib\tkinter\__init__.py", line 1699, in __call__
    return self.func(*args)
  File "decline_curve_analysis.py", line 993, in predpred
    df3['Model_EXP'], df3['Model_EXPcum'] =arps.arps(days = df3[dayscol], Qi=parmexp[0], Di=parmexp[1], T0=parmexp[2])
  File "...\PetroPy\DeclineCurves.py", line 110, in arps
    rates  = Qi*np.exp(-Di*(days-T0))
AttributeError: 'float' object has no attribute 'exp'

I didn't understand why, so I printed out the dataframe just before the function call. The "dayscol" column had different values, but in both working and non-working cases the days vector (which is the df3[dayscol] column) in "int64". However, when I made the following change in the called function:

      print("Type of days",days.dtype)
      newexp = (-Di*(days-T0))
      print("Type of newexp",newexp.dtype)
      rates  = Qi*np.exp(-Di*(days-T0))                       #  THIS IS WHERE THINGS GET INTERESTING

I discovered that I got the following response on the calls which worked:

   Type of days int64
   Type of newexp float64

but got the following for those which failed:

   Type of days int64
   Type of newexp object

which I've now solved by rewriting my original function to be:

      newexp = (-Di*(days-T0))
      newexp = newexp.astype(np.float64)
      rates  = Qi*np.exp(-Di*(days-T0))                       #  THIS IS WHERE THINGS GET INTERESTING

QUESTION:

Is there a way I can ensure that this doesn't occur again? It's taken me over a day to debug this, and I'm concerned because I have a lot of non-pandas functions that I don't want to fail when I use them in other projects. Is this even related to the way the function is called?

Thanks.

BobM
  • 1
  • 1
  • `pandas` readily produces object dtype series. When you use code like this you need to test dtype of the arguments, and if possible convert them a numeric dtype. – hpaulj Oct 13 '18 at 23:45
  • I haven't used `pandas` enough to know why or when it switches to object dtype. String elements in a series seem to do it. Rather than make the whole series string (as `np.array` would) it goes the mixed object route. `None` or `nan` values might also do it (even though `nan` is a float). – hpaulj Oct 14 '18 at 01:09

1 Answers1

-2

The underlying type of a numpy array can not be devised.

Python is only dynamically strongly typed (Is Python strongly typed? )

That's it's main strength and one of it's main frustrating weakness.

There is no real way around it in your case. In some case you can Force a function parameter type in Python? but in your case you can not specify the underlying type in numpy.ndarray.

What is troubling is

It's taken me over a day to debug this

error should tip you off to directly check the types in line 110

File "...\PetroPy\DeclineCurves.py", line 110, in arps
rates = Qi * np.exp(-Di*(days-T0))
AttributeError: 'float' object has no attribute 'exp'

Oh I get it big data, slow loading time,... Well check your code when you use transcendental functions.

PilouPili
  • 2,601
  • 2
  • 17
  • 31