Using brentq with two lists of data - python

I am trying to find the root(s) of a line which is defined by data like:
x = [1,2,3,4,5]
y = [-2,4,6,8,4]
I have started by using interpolation but I have been told I can then use the brentq function. How can I use brentq from two lists? I thought continuous functions are needed for it.

As the documentation of brentq says, the first argument must be a continuous function. Therefore, you must first generate, from your data, a function that will return a value for each parameter passed to it. You can do that with interp1d:
import numpy as np
from scipy.interpolate import interp1d
from scipy.optimize import brentq
x, y = np.array([1,2,3,4,5]), np.array([-2,4,6,8,4])
f = interp1d(x,y, kind='linear') # change kind to something different if you want e.g. smoother interpolation
brentq(f, x.min(), x.max()) # returns: 1.33333
You could also use splines to generate the continuous function needed for brentq.

Related

How to get piecewise linear function in Python

I would like to get piecewise linear function from set of points. Here is visual example:
import matplotlib.pyplot as plt
x = [1,2,7,9,11]
y = [2,5,9,1,11]
plt.plot(x, y)
plt.show()
So I need a function that would take two lists and would return piecewise linear function back. I do not need regression or any kind of least square fit.
I can try to write it myself, but wonder if there is something already written. So far, I only found code returning regression
try np.interp. It interpolates the values.
Here is a small example.
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> x = [1,2,7,9,11]
>>> y = [2,5,9,1,11]
>>> np.interp([1.5, 3], x, y)
array([ 3.5, 5.8])
A caution note is to make sure for the sample points, make sure the x increases.

Plotting function for specified intervals in python matplotlib

I am trying to replicate the mathematical operations from the link using Python.
I managed to integrate the function but I am unable to plot it for the specified intervals. I've tried to use arrange to get values for arguments and plot it against the function but it doesn't work.
Does anyone know how to get it work?
My snippet code:
from scipy.integrate import quad
def f(x):
if x >= 0 and x <= 2:
return x ** 2
elif x > 2 and x <= 4:
return 4
else:
return 0
res = quad(f, 0, 5)
print(res)
I assume you want to plot the function, f(x), rather than the results of integrating it. To do that you will want to create a list of x values (sounds like you did this), evaluate f for each of those values, and then use matplotlib's plot function to display the result.
The documentation for arange says that "When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use linspace for these cases." You probably want to plot with a non-integer step in x, otherwise you will use most of the details of your plot. So I would suggest switching to linspace.
import numpy as np
from matplotlib.pyplot import plot
xvals = np.linspace(0,6,100) #100 points from 0 to 6 in ndarray
yvals = list(map(f, xvals)) #evaluate f for each point in xvals
plot(xvals, yvals)
Most likely where you ran into a problem was directly applying your function f to an ndarray. The way it is written, f expects a single value as an input rather than an array. Map solves this problem by applying f to each value in your ndarray individually.
Edit: To use a sympy symbolic function:
You can also define a piecewise function in sympy. For the things you are trying to accomplish in your question, this won't be any different from using the original method described. However, if you want to do further symbolic manipulations with your function this could be useful.
import sympy
x = sympy.symbols('x')
f = sympy.Piecewise((0, x>4),(4, x>2) ,(x**2, x>=0)) #defines f as a symbolic function
sympy.plot(f, (x, 0,6)) #Plots f on the interval 0 to 6
Note in the definition of a piecewise function, the conditions are evaluated in order, so the order you define them in does matter. If, for example you swapper the first two conditions, the x>4 condition would never be reached because x>2 would always be satisfied first. This is why the conditions are defined in the reverse order from your original function.

Matlab's spline equivalent in Python, three inputs.

I'm converting a matlab script to python and I have it a roadblock.
In order to use cubic spline interpolation on a signal. The script uses the command spline with three inputs. f_o, c_signal and freq. so it looks like the following.
cav_sig_freq = spline(f_o, c_signal, freq)
f_o = 1x264, c_signal = 1x264 and freq = 1x264
From the documentation in matlab it reads that "s = spline(x,y,xq) returns a vector of interpolated values s corresponding to the query points in xq. The values of s are determined by cubic spline interpolation of x and y."
In python i'm struggling to find the correct python equivalent. Non of different interpolation functions I have found in the numpy and Scipy documentation let's use the third input like in Matlab.
Thanks for taking the time to read this. If there are any suggestion to how I can make it more clear, I'll be happy to do so.
Basically you will first need to generate something like an interpolant function, then give it your points. Using your variable names like this:
from scipy import interpolate
tck = interpolate.splrep(f_o, c_signal, s=0)
and then apply this tck to your points:
c_interp = interpolate.splev(freq, tck, der=0)
For more on this your can read this post.
Have you tried the InterpolatedUnivariateSpline within scipy.interpolate? If I understand the MatLab part correctly, then I think this will work.
import numpy as np
from scipy.interpolate import InterpolatedUnivariateSpline as ius
a = [1,2,3,4,5,6]
b = [r * 2 for r in a]
c = ius(a, b, k=1)
# what values do you want to query?
targets = [3.4, 2.789]
interpolated_values = c(targets)
It seems this may add one more step to your code than what MatLab provides, but I think it is what you want.

Scipy: efficiently generate a series of integration (integral function)

I have a function, I want to get its integral function, something like this:
That is, instead of getting a single integration value at point x, I need to get values at multiple points.
For example:
Let's say I want the range at (-20,20)
def f(x):
return x**2
x_vals = np.arange(-20, 21, 1)
y_vals =[integrate.nquad(f, [[0, x_val]]) for x_val in x_vals ]
plt.plot(x_vals, y_vals,'-', color = 'r')
The problem
In the example code I give above, for each point, the integration is done from scratch. In my real code, the f(x) is pretty complex, and it's a multiple integration, so the running time is simply too slow(Scipy: speed up integration when doing it for the whole surface?).
I'm wondering if there is any way of efficient generating the Phi(x), at a giving range.
My thoughs:
The integration value at point Phi(20) is calucation from Phi(19), and Phi(19) is from Phi(18) and so on. So when we get Phi(20), in reality we also get the series of (-20,-19,-18,-17 ... 18,19,20). Except that we didn't save the value.
So I'm thinking, is it possible to create save points for a integrate function, so when it passes a save point, the value would get saved and continues to the next point. Therefore, by a single process toward 20, we could also get the value at (-20,-19,-18,-17 ... 18,19,20)
One could implement the strategy you outlined by integrating only over the short intervals (between consecutive x-values) and then taking the cumulative sum of the results. Like this:
import numpy as np
import scipy.integrate as si
def f(x):
return x**2
x_vals = np.arange(-20, 21, 1)
pieces = [si.quad(f, x_vals[i], x_vals[i+1])[0] for i in range(len(x_vals)-1)]
y_vals = np.cumsum([0] + pieces)
Here pieces are the integrals over short intervals, which get summed to produce y-values. As written, this code outputs a function that is 0 at the beginning of the range of integration which is -20. One can, of course, subtract the y-value that corresponds to x=0 in order to have the same normalization as on your plot.
That said, the split-and-sum process is unnecessary. When you find an indefinite integral of f, you are really solving the differential equation F' = f. And SciPy has a built-in method for that, odeint. Just use it:
import numpy as np
import scipy.integrate as si
def f(x):
return x**2
x_vals = np.arange(-20, 21, 1)
y_vals = si.odeint(lambda y,x: f(x), 0, x_vals)
The output is essential identical to the first version (within tiny computational errors), with less code. The reason for using lambda y,x: f(x) is that the first argument of odeint must be a function taking two arguments, the right-hand side of the equation y' = f(y, x).
For the equivalent version of user3717023's answer using scipy's solve_ivp you need to keep in mind the different ordering of x and y in the function f (different from the odeint version).
Further, keep in mind that you can only compute the solution up to a constant. So you might want to shift the result according to some given condition. In the example here (with the function f(x)=x^2 as given by the OP), I shifted the numeric solution such that it goes through the origin, matching the simplest analytic solution F(x)=x^3/3.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
def f(x):
return x**2
xs = np.linspace(-20, 20, 1001)
# This is the integration step:
sol = solve_ivp(lambda x, y: f(x), t_span=(xs[0], xs[-1]), y0=[0], t_eval=xs)
plt.plot(sol.t, sol.t**3/3, ls='-', c='C0', label="analytic: $F(x)=x^3/3$")
plt.plot(sol.t, sol.y[0], ls='--', c='C1', label="numeric solution")
plt.plot(sol.t, sol.y[0] - sol.y[0][sol.t.size//2], ls='-.', c='C3', label="shifted solution going through origin")
plt.legend()
In case you don't have an analytical version of the function f, but only xs and ys as data points, then you can use scipy's interp1d function to interpolate between the data points and pass on that interpolating function the same way as before:
from scipy.interpolate import interp1d
f = interp1d(xs, ys)

Python: Integration of Interpolation

I've got some question I cant solve:
#! /usr/bin/env python
import numpy as np
from scipy.interpolate import UnivariateSpline
from scipy.integrate import quad
import pylab as pl
x = ([0,10,20,30,40,50,60,70,...,4550,4560])
y = ([0,0,0,0,0,0,0,3,2,3,2,1,2,1,2,...,8,6,5,7,11,6,7,10,6,5,8,13,6,8,8,3])
s = UnivariateSpline(x, y, k=5, s=5)
xs = np.linspace(0, 4560, 4560)
ys = s(xs)
This is my code for making some Interpolation over some data.
In addition, I plotted this function.
But now I want to integrate it (from zero to infinity).
I tried
results = integrate.quad(ys, 0, 99999)
but it didnt work.
Can you give me some hints (or solutions) please? thanks
As Pierre GM said, you have to give a function for quad (I think also you can use np.inf for the upper bound, though here it doesn't matter as the splines go to 0 quickly anyways). However, what you want is:
s.integral(0, np.inf)
Since this is a spline, the UnivariateSpline object already implements an integral that should be better and faster.
According to the documentation of quad, you need to give a function as first argument, followed by the lower and upper bounds of the integration range, and some extra arguments for your function (type help(quad) in your shell for more info.
You passed an array as first argument (ys), which is why it doesn't work. You may want to try something like:
results = quad(s, xs[0], xs[-1])
or
results = quad(s, 0, 9999)

Categories

Resources