As pointed out by @SevC_10 in his answer, you are missing dx parameter.
I like to show case the use of sympy for derivation operations, I find it much easier in many cases.
Copyimport sympy
import numpy as np
x = sympy.Symbol('x')
f = sympy.exp(x) # my function e^x
df = f.diff() # y' of the function = e^x
f_lambda = sympy.lambdify(x, f, 'numpy')
df_lambda = sympy.lambdify(x, yprime, 'numpy') # use lambdify
print(f_lambda(np.ones(5)))
# array([2.71828183, 2.71828183, 2.71828183, 2.71828183, 2.71828183])
print(df_lambda(np.ones(5)))
# array([2.71828183, 2.71828183, 2.71828183, 2.71828183, 2.71828183])
print(f_lambda(np.zeros(5)))
# array([1., 1., 1., 1., 1.])
print(df_lambda(np.zeros(5)))
# array([1., 1., 1., 1., 1.])
print(f_lambda(np.array([0, 1, 2, 3, 4])))
# array([ 1. , 2.71828183, 7.3890561 , 20.08553692, 54.59815003])
print(df_lambda(np.array([0, 1, 2, 3, 4])))
# array([ 1. , 2.71828183, 7.3890561 , 20.08553692, 54.59815003])
Answer from Sreeram TP on Stack OverflowAs pointed out by @SevC_10 in his answer, you are missing dx parameter.
I like to show case the use of sympy for derivation operations, I find it much easier in many cases.
Copyimport sympy
import numpy as np
x = sympy.Symbol('x')
f = sympy.exp(x) # my function e^x
df = f.diff() # y' of the function = e^x
f_lambda = sympy.lambdify(x, f, 'numpy')
df_lambda = sympy.lambdify(x, yprime, 'numpy') # use lambdify
print(f_lambda(np.ones(5)))
# array([2.71828183, 2.71828183, 2.71828183, 2.71828183, 2.71828183])
print(df_lambda(np.ones(5)))
# array([2.71828183, 2.71828183, 2.71828183, 2.71828183, 2.71828183])
print(f_lambda(np.zeros(5)))
# array([1., 1., 1., 1., 1.])
print(df_lambda(np.zeros(5)))
# array([1., 1., 1., 1., 1.])
print(f_lambda(np.array([0, 1, 2, 3, 4])))
# array([ 1. , 2.71828183, 7.3890561 , 20.08553692, 54.59815003])
print(df_lambda(np.array([0, 1, 2, 3, 4])))
# array([ 1. , 2.71828183, 7.3890561 , 20.08553692, 54.59815003])
The derivative function has other arguments. From the help(derivative):
CopyParameters
----------
func : function
Input function.
x0 : float
The point at which the nth derivative is found.
dx : float, optional
Spacing.
n : int, optional
Order of the derivative. Default is 1.
args : tuple, optional
Arguments
order : int, optional
Number of points to use, must be odd.
As you can see, you didn't specify the dx parameter, so this can cause rounding error because the approximate derivative is computed on a larger interval. From the documentation, the default value is 1 (https://docs.scipy.org/doc/scipy/reference/generated/scipy.misc.derivative.html).
Simply try to reduce the spacing interval: for example, using 1e-3 I get:
Copy2.718281828459045
2.718282281505724
Help with scipy derivatives
python - Scipy Derivative - Stack Overflow
scipy - Numerical derivative in python - Computational Science Stack Exchange
python - scipy.misc.derivative for multiple argument function - Stack Overflow
Videos
from scipy.misc import derivative as der
import sympy as sy
def f(x):
......return sy.sin(x)
a = der(f, 1)
print(a)
I really don't understand why this isn't working properly. I'm trying to derive sin(x) (as a simple test) at a certain point, but it gives me the wrong result (0.454648713412841 instead of 0.5403023....). In the line "a = der(f, 1)", it means that I'm looking for the derivative of the function f, at point 1.
why is this not working?
I would write a simple wrapper, something along the lines of
def partial_derivative(func, var=0, point=[]):
args = point[:]
def wraps(x):
args[var] = x
return func(*args)
return derivative(wraps, point[var], dx = 1e-6)
Demo:
>>> partial_derivative(foo, 0, [3,1])
6.0000000008386678
>>> partial_derivative(foo, 1, [3,1])
2.9999999995311555
Yes, it is implemented in sympy. Demo:
>>> from sympy import symbols, diff
>>> x, y = symbols('x y', real=True)
>>> diff( x**2 + y**3, y)
3*y**2
>>> diff( x**2 + y**3, y).subs({x:3, y:1})
3
You have four options
- Finite Differences
- Automatic Derivatives
- Symbolic Differentiation
- Compute derivatives by hand.
Finite differences require no external tools but are prone to numerical error and, if you're in a multivariate situation, can take a while.
Symbolic differentiation is ideal if your problem is simple enough. Symbolic methods are getting quite robust these days. SymPy is an excellent project for this that integrates well with NumPy. Look at the autowrap or lambdify functions or check out Jensen's blogpost about a similar question.
Automatic derivatives are very cool, aren't prone to numeric errors, but do require some additional libraries (google for this, there are a few good options). This is the most robust but also the most sophisticated/difficult to set up choice. If you're fine restricting yourself to numpy syntax then Theano might be a good choice.
Here is an example using SymPy
In [1]: from sympy import *
In [2]: import numpy as np
In [3]: x = Symbol('x')
In [4]: y = x**2 + 1
In [5]: yprime = y.diff(x)
In [6]: yprime
Out[6]: 2⋅x
In [7]: f = lambdify(x, yprime, 'numpy')
In [8]: f(np.ones(5))
Out[8]: [ 2. 2. 2. 2. 2.]
The most straight-forward way I can think of is using numpy's gradient function:
x = numpy.linspace(0,10,1000)
dx = x[1]-x[0]
y = x**2 + 1
dydx = numpy.gradient(y, dx)
This way, dydx will be computed using central differences and will have the same length as y, unlike numpy.diff, which uses forward differences and will return (n-1) size vector.
FFT returns a complex array that has the same dimensions as the input array. The output array is ordered as follows:
Element 0 contains the zero frequency component, F0.
The array element F1 contains the smallest, nonzero positive frequency, which is equal to 1/(Ni Ti), where Ni is the number of elements and Ti is the sampling interval.
F2 corresponds to a frequency of 2/(Ni Ti).
Negative frequencies are stored in the reverse order of positive frequencies, ranging from the highest to lowest negative frequencies.
For an even number of points, the frequencies corresponding to the returned complex values are: 0, 1/(NiTi), 2/(NiTi), ..., (Ni/2–1)/(NiTi), 1/(2Ti), –(Ni/2–1)/(NiTi), ..., –1/(NiTi) where 1/(2Ti) is the Nyquist critical frequency.
For an odd number of points, the frequencies corresponding to the returned complex values are: 0, 1/(NiTi), 2/(NiTi), ..., (Ni–1)/2)/(NiTi), –(Ni–1)/2)/(NiTi), ..., –1/(NiTi)
Using this information we can construct the proper vector of frequencies that should be used for calculating the derivative. Below is a piece of self-explanatory Python code that does it all correctly. Note that the factor 2$\pi$N cancels out due to normalization of FFT.
from scipy.fftpack import fft, ifft, dct, idct, dst, idst, fftshift, fftfreq
from numpy import linspace, zeros, array, pi, sin, cos, exp, arange
import matplotlib.pyplot as plt
N = 100
x = 2*pi*arange(0,N,1)/N #-open-periodic domain
dx = x[1]-x[0]
y = sin(2*x)+cos(5*x)
dydx = 2*cos(2*x)-5*sin(5*x)
k2=zeros(N)
if ((N%2)==0):
#-even number
for i in range(1,N//2):
k2[i]=i
k2[N-i]=-i
else:
#-odd number
for i in range(1,(N-1)//2):
k2[i]=i
k2[N-i]=-i
dydx1 = ifft(1j*k2*fft(y))
plt.plot(x,dydx,'b',label='Exact value')
plt.plot(x,dydx1, color='r', linestyle='--', label='Derivative by FFT')
plt.legend()
plt.show()

Maxim Umansky’s answer describes the storage convention of the FFT frequency components in detail, but doesn’t necessarily explain why the original code didn’t work. There are three main problems in the code:
x = linspace(0,2*pi,N): By constructing your spatial domain like this, yourxvalues will range from $0$ to $2\pi$, inclusive! This is a problem because your functiony = sin(2*x)+cos(5*x)is not exactly periodic on this domain ($0$ and $2\pi$ correspond to the same point, but they’re included twice). This causes spectral leakage and thus a small deviation in the result. You can avoid this by usingx = linspace(0,2*pi,N, endpoint=False)(orx = 2*pi*arange(0,N,1)/N, as Maxim Umansky did; this is what he is referring to with “open-periodic domain”).k = fftshift(k): As Maxim Umansky explained, yourkvalues need to be in a specific order to match the FFT convention.fftshiftsorts the values (from small/negative to large/positive), which can be useful e. g. for plotting, but is incorrect for computations.dydx1 = ifft(-k*1j*fft(y)).real:scipydefines the FFT asy(j) = (x * exp(-2*pi*sqrt(-1)*j*np.arange(n)/n)).sum(), i. e. with a factor of $2\pi$ in the exponential, so you need to include this factor when deriving the formula for the derivative. Also, forscipy’s FFT convention, thekvalues shouldn’t get a minus sign.
So, with these three changes, the original code can be corrected as follows:
from scipy.fftpack import fft, ifft, dct, idct, dst, idst, fftshift, fftfreq
from numpy import linspace, zeros, array, pi, sin, cos, exp
import matplotlib.pyplot as plt
N = 100
x = linspace(0,2*pi,N, endpoint=False) # (1.)
dx = x[1]-x[0]
y = sin(2*x)+cos(5*x)
dydx = 2*cos(2*x)-5*sin(5*x)
k = fftfreq(N,dx)
# (2.)
dydx1 = ifft(2*pi*k*1j*fft(y)).real # (3.)
plt.plot(x,dydx,'b',label='Exact value')
plt.plot(x,dydx1,'r',label='Derivative by FFT')
plt.legend()
plt.show()