How to get X- oder Y-Coordinate from Scipy mathematical function - python

When I create a simple mathematical function like..
f(X) = 2*X
using scipy.interpolate - how can I get the X-coordinate of a corresponding Y-coordinate?
Here is the scipy-function:
from scipy import interpolate
testfunc = scipy.interpolate.interp1d([1,2], [2,4], kind='linear')
I want to get the X-value belonging to Y = 4 (should be 2).
I tried:
testfunc.x(4)

Would it work to just do reverse interpolation?
x,y = ...
testfunc_inverse = scipy.interpolate.interp1d(y, x, kind='linear')
testfunc_inverse(4)

I may be missing the point here, however, if you follow the scipy.interpolate.interp1d example you will end up with two numpy arrays of equal length. Seems like you should be able to index the y array against the x array and get your answer. I don't know if you would consider this approach 'pythonic' but it works in this case.

Related

Is there a method for finding the root of a function represented as an narray?

I have a function represented as a narray i. e. y = f(x), where y and x are two narrays.
I am searching for a method that find the roots of f(x).
Reading the scipy documentation, I was able to find just methods that works on user defined functions, like scipy.optimize.root_scalar. I thought about using scipy.interpolate.interp1d to get an interpolated version of my function to be used in scipy.optimize.root_scalar, but I'm not sure it can work and it seems pretty complicated.
Is it there some other function that I can use instead?
You have to interpolate a function defined by numpy arrays as all the solvers require a function that can return a value for any input x, not just those in your array. But this is not complicated, here is an example
from scipy import optimize
from scipy import interpolate
# our xs and ys
xs = np.array([0,2,5])
ys = np.array([-3,-1,2])
# interpolated function
f = interpolate.interp1d(xs, ys)
sol = optimize.root_scalar(f, bracket = [xs[0],xs[-1]])
print(f'root is {sol.root}')
# check
f0 = f(sol.root)
print(f'value of function at the root: f({sol.root})={f0}')
output:
root is 3.0
value of function at the root: f(3.0)=0.0
You may also want to interpolate with higher-degree polynomials for higher accuracy of your root-finding, eg How to perform cubic spline interpolation in python?

scipy.misc.derivative for uneven space points

I want to calculate the second derivative for each point (except the first and last one) in a points set. This points set has a data type of dictionary, which is like points = {x1:y1, x2:y2, ... xn:yn} where all the x are positive integers but it is in uneven spacing, e.g., x1=1, x2=2, x3=3, x4=5, x5=7 the x numbers are not increased linearly and the gap could be random, i.e., x_{i+1} - x_{i} could be any positive integer.
For this dictionary of points, I want to get second derivative for each point, so I did the coding like:
import numpy as np
from scipy.misc import derivative
def wrapper(x):
return np.array([points[int(i)] for i in x])
y_d2 = derivative(wrapper, np.array(list(points.keys()))[1:-1], dx=1.0, n=2)
In this case, I will get KeyError: 4 at return np.array([points[int(i)] for i in x]). It is becasue the x=4 does not exist in the points dictionary so that it has a key error. How could I use scipy.misc.derivative for this situation? How to set the dx parameter (spacing) for scipy.misc.derivative?
Do you have to use scipy.misc.derivative? Because it is very easy to calculate second derivatives without
Let's say you have your data as a dictionary:
points = {1:2, 2:2, 4:4, 5:5}
Then all you do is first get them into x,y lists:
x,y = list(points.keys()), list(points.values())
and then calculate derivs using numpy diff
dy_dx = np.diff(y)/np.diff(x)
d2y_dx2 = np.diff(dy_dx)/np.diff(x[:-1])
output for d2y_dx2 is
array([1., 0.])
as expected.
Of course there are more sophisticated versions of this if you want to use higher-accuracy formulas for derivatives, eg you can create a spline from your x,y and calculate derivatives of the spline. But I would start with the basic scheme above unless there are compelling reasons for anything else

Scipy interp2d function produces z = f(x,y), I would like to solve for x

I am using the 2d interpolation function in scipy to smooth a 2d image. As I understand it, interpolate will return z = f(x,y). What I want to do is find x with known values of y and z. I tried something like this;
f = interp2d(x,y,z)
index = (np.abs(f(:,y) - z)).argmin()
However the interp2d object does not work that way. Any ideas on how to do this?
I was able to figure this out. yvalue, zvalue, xmin, and xmax are known values. By creating a linspace out of the possible values x can take on, a list can be created with all of the corresponding function values. Then using argmin() we can find the closest value in the list to the known z value.
f = interp2d(x,y,z)
xnew = numpy.linspace(xmin, xmax)
fnew = f(xnew, yvalue)
xindex = (numpy.abs(fnew - zvalue)).argmin()
xvalue = xnew(xindex)

SciPy interp2D for pairs of coordinates

I'm using scipy.interpolate.interp2d to create an interpolation function for a surface. I then have two arrays of real data that I want to calculate interpolated points for. If I pass the two arrays to the interp2d function I get an array of all the points, not just the pairs of points.
My solution to this is to zip the two arrays into a list of coordinate pairs and pass this to the interpolation function in a loop:
f_interp = interpolate.interp2d(X_table, Y_table,Z_table, kind='cubic')
co_ords = zip(X,Y)
out = []
for i in range(len(co_ords)):
X = co_ords[i][0]
Y = co_ords[i][1]
value = f_interp(X,Y)
out.append(float(value))
My question is, is there a better (more elegant, Pythonic?) way of achieving the same result?
Passing all of your points at once will probably be quite a lot faster than looping over them in Python. You could use scipy.interpolate.griddata:
Z = interpolate.griddata((X_table, Y_table), Z_table, (X, Y), method='cubic')
or one of the scipy.interpolate.BivariateSpline classes, e.g. SmoothBivariateSpline:
itp = interpolate.SmoothBivariateSpline(X_table, Y_table, Z_table)
# NB: choose grid=False to get an (n,) rather than an (n, n) output
Z = itp(X, Y, grid=False)
CloughTocher2DInterpolator also works in a similar fashion, but without the grid=False parameter (it always returns a 1D output).
Try *args and tuple packing/unpacking
points = zip(X, Y)
out = []
for p in points:
value = f_interp(*p)
out.append(float(value))
or just
points = zip(X, Y)
out = [float(f_interp(*p)) for p in points]
or just
out = [float(f_interp(*p)) for p in zip(X, Y)]
as a side note, the "magic star" allows zip to be its own inverse!
points = zip(x, y)
x, y = zip(*points)
For one, you can do
for Xtmp,Ytmp in zip(X,Y):
...
in your loop. Or even better, just
out = [float(f_interp(XX,YY)) for XX,YY in zip(X,Y)]
replacing the loop.
On a different note, I suggest using interpolate.griddata instead. It tends to behave much better than interp2d, and it accepts arbitrary-shaped points as input. As you've seen, interp2d interpolators will only return you values on a mesh.
Inspired by this thread where someone recommends using the internal weights of the interp2d function, I've created the following wrapper which has exactly the same interface as interp2d but the interpolant evaluate pairs of inputs and return a numpy array of the same shape of its inputs. The performances should be better than for loops or list comprehension, but when evaluated on a grid it will be slightly outperformed by the scipy interp2d.
import scipy.interpolate as si
def interp2d_pairs(*args,**kwargs):
""" Same interface as interp2d but the returned interpolant will evaluate its inputs as pairs of values.
"""
# Internal function, that evaluates pairs of values, output has the same shape as input
def interpolant(x,y,f):
x,y = np.asarray(x), np.asarray(y)
return (si.dfitpack.bispeu(f.tck[0], f.tck[1], f.tck[2], f.tck[3], f.tck[4], x.ravel(), y.ravel())[0]).reshape(x.shape)
# Wrapping the scipy interp2 function to call out interpolant instead
return lambda x,y: interpolant(x,y,si.interp2d(*args,**kwargs))
# Create the interpolant (same interface as interp2d)
f = interp2d_pairs(X,Y,Z,kind='cubic')
# Evaluate the interpolant on each pairs of x and y values
z=f(x,y)

Python griddata meshgrid

in Python I want to interpolate some data using scipy.interpolate.griddata(x,y,z,xi,yi).
Since I want my unequal spaced original data on the X-Y grid map on an equal spaced XI-YI grid I have to use a meshgrid as:
X, Y = numpy.meshgrid([1,2,3], [2,5,6,8])
XI,YI = numpy.meshgrid([1,2,3],[4,5,6,7])
print scipy.interpolate.griddata(X,Y,X**2+Y**2,XI,YI)
Unfortunately it seems as scipys' griddata does not accept matrices as input for x,y,z in contrast to matlab's griddata-function. Does anyone has a hint for me how to solve the problem?
The correct call sequence in your case is
print scipy.interpolate.griddata((X.ravel(),Y.ravel()), (X**2+Y**2).ravel(), (XI, YI))
I.e., you need to cast the input data points to 1-d. (This could be fixed to work without the .ravel()s in the next version of Scipy.)
I think you need to reshape your grids, griddata expects a list of points with coordinates in column form:
points = transpose(reshape((X,Y), (2,12)))
pointsI = transpose(reshape((XI,YI), (2,12)))
Z = reshape(X**2+Y**2, 12)
print scipy.interpolate.griddata(points, Z, pointsI)

Categories