Scipy 0.11.0 to 0.12.0 modifies the linear scipy.interpolate.interp1d, breaks my constantly updated interpolator

I played with a package that uses linear scipy.interpolate.interp1d to create a history function for ode solver in scipy described here.

The corresponding bit of code looks something like this:

def update(self, ti, Y): """ Add one new (ti, yi) to the interpolator """ self.itpr.x = np.hstack([self.itpr.x, [ti]]) yi = np.array([Y]).T self.itpr.y = np.hstack([self.itpr.y, yi]) #self.itpr._y = np.hstack([self.itpr.y, yi]) self.itpr.fill_value = Y 

Where "self.itpr" is initialized in __init __:

 def __init__(self, g, tc=0): """ g(t) = expression of Y(t) for t<tc """ self.g = g self.tc = tc # We must fill the interpolator with 2 points minimum self.itpr = scipy.interpolate.interp1d( np.array([tc-1, tc]), # X np.array([self.g(tc), self.g(tc)]).T, # Y kind='linear', bounds_error=False, fill_value = self.g(tc)) 

Where g is some function that returns an array of values ​​that are solutions for a set of differential equations, and tc is the current time.

This seems pleasant to me because a new interpolator object does not need to be re-created every time I want to update value ranges (which happens at every explicit time step during the simulation). This interpolator update method works well under scipy v 0.11.0. However, after upgrading to v 0.12.0, I ran into problems. I see that the new interpolator now includes an _y array, which seems to be just another copy of the original. Is it safe and / or reasonable to just update _y as above? Is there an easier and more pythonic way to resolve this issue, which I hope will be more reliable for future updates in scipy? Again, in v 0.11 everything works well, and the expected results are produced, and in v 0.12 I get a IndexError when _y referenced , because it is not updated in my function, but y itself is.

Any help / pointers would be appreciated!

+4
source share
1 answer

It seems that _y is just a copy of y that has been changed to interp1d._reshape_yi() . Therefore, you just need to update it using:

  self.itpr._y = self.itpr._reshape_yi(self.itpr.y) 

In fact, as far as I can tell, only _y , which is used internally by the interpolator, so I think you could leave without actually updating y at all.

A more elegant solution would be to make the _y an interpolator property that returns a suitable reformatted copy of y . It is possible to achieve this by mounting your specific instance of interp1d after creating it (see Alex Martelli's answer here ):

 x = np.arange(100) y = np.random.randn(100) itpr = interp1d(x,y) # method to get self._y from self.y def get_y(self): return self._reshape_yi(self.y) meth = property(get_y,doc='reshaped version of self.y') # make this a method of this interp1d instance only basecls = type(itpr) cls = type(basecls.__name__, (basecls,), {}) setattr(cls, '_y', meth) itpr.__class__ = cls # itpr._y is just a reshaped version of itpr.y print itpr.y.shape,itpr._y.shape >>> (100,) (100, 1) 

Now itpr._y updated when itpr._y is updated

 itpr.x = np.arange(110) itpr.y = np.random.randn(110) print itpr._y.shape >>> (110,) (110, 1) 

This is quite complex and not very Pythonic - it is much easier to fix the scipy source code (scipy / interpolate / interpolate.py). All you have to do is remove the last line from interp1d.__init__() , where it sets:

 self._y = y 

and add the following lines:

 @property def _y(self): return self._reshape_yi(self.y) 
+3
source

Source: https://habr.com/ru/post/1489979/


All Articles