You can interpolate your data using scipy's 1-D Splines functions. The computed spline has a convenient derivative method for computing derivatives.

For the data of your example, using UnivariateSpline gives the following fit

import matplotlib.pyplot as plt
from scipy.interpolate import UnivariateSpline

y_spl = UnivariateSpline(x,y,s=0,k=4)

plt.semilogy(x,y,'ro',label = 'data')
x_range = np.linspace(x[0],x[-1],1000)
plt.semilogy(x_range,y_spl(x_range))

The fit seems reasonably good, at least visually. You might want to experiment with the parameters used by UnivariateSpline.

The second derivate of the spline fit can be simply obtained as

y_spl_2d = y_spl.derivative(n=2)

plt.plot(x_range,y_spl_2d(x_range))

The outcome appears somewhat unnatural (in case your data corresponds to some physical process). You may either want to change the spline fit parameters, improve your data (e.g., provide more samples, perform less noisy measurements), or decide on an analytic function to model your data and perform a curve fit (e.g., using sicpy's curve_fit)

Answer from Stelios on Stack Overflow
🌐
SciPy
docs.scipy.org › doc › scipy › reference › generated › scipy.differentiate.derivative.html
derivative — SciPy v1.17.0 Manual
The implementation was inspired by jacobi [1], numdifftools [2], and DERIVEST [3], but the implementation follows the theory of Taylor series more straightforwardly (and arguably naively so). In the first iteration, the derivative is estimated using a finite difference formula of order order with maximum step size initial_step.
Discussions

How To Take Derivatives In Python: 3 Different Types of Scenarios
How To Take Derivatives In Python: 3 Different Types of Scenarios In this video I show how to properly take derivatives in python in 3 different types of scenarios. The first scenario is when you have an explicit form for your function, such as f(x)=x2 or f(x)=ex sin(x). In such a scenario, the sympy library can be used to take first, second, up to nth derivatives of a function. This comes in handy for complicated functions, but can later on be EXTREMELY useful for computing Lagrange's equations of motion given strange trajectories. The second scenario is when you collect data and want to compute a derivative. In such a scenario, the data is often noisey, and taking a simple derivative will fail since it will amplify the high-frequency component of the data. In such a case, one needs to smooth data before taking a derivative. The ideal library for managing this is numpy. The third scenario involves functions of an irregular form. By this, I mean that your function can't be written down as simply as "sin(x)" or "ex". For example. f(x) = "solve an ode using some complex odesolver with parameter abserr=x and compute the integral of the answer". In this case, derivatives can't be computed symbolically, but one can use scipy's derivative method to get a good estimate of df/dx at certain values of x. More on reddit.com
🌐 r/Physics
4
408
August 9, 2021
Higher order central differences using NumPy.gradient()
Hello everyone, I am new to Python and am still learning it. So my apologies if this is a basic question. I am given two arrays: X and Y. Where Y=2*(x^2)+x/2. I need to calculate the first and the fifth order central differences of Y with respect to X using the numpy.gradient function. More on discuss.python.org
🌐 discuss.python.org
0
0
September 5, 2022
python - Computing numeric derivative via FFT - SciPy - Computational Science Stack Exchange
I wrote the following code to compute the approximate derivative of a function using FFT: from scipy.fftpack import fft, ifft, dct, idct, dst, idst, fftshift, fftfreq from numpy import linspace, z... More on scicomp.stackexchange.com
🌐 scicomp.stackexchange.com
May 30, 2020
scipy - Implementation of the second derivative of a normal probability distribution function in python - Stack Overflow
def second_derivative(x, mu, sigma): g = (mu - x)**2/sigma**2; return scipy.stats.norm.pdf(x, mu, sigma)*(g**2 - 1/sigma**2) More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 2
4

Ndimage generates a Gaussian kernel by sampling a Gaussian and normalizing it to 1. The derivative of this kernel is generated by modifying that normalized kernel according to the chain rule to compute the derivative, and this modification is applied repeatedly to obtain higher order derivatives. Here is the relevant source code.

This indeed leads to a kernel that produces imprecise second order derivatives, as described by the OP in the question and the very nice answer.

Indeed, one can not just sample a derivative of Gaussian to obtain a convolution kernel, because the Gaussian function is not band-limited, and so sampling causes aliasing. The Gaussian function is nearly bandlimited, sampling with a sample spacing of leads to less than 1% of the energy being aliased. But as the order of the derivative increases, so does the bandlimit, meaning that the higher the derivative order, the more sampling error we get. [Note that if we had no sampling error, convolution with the sampled kernel would yield the same result as a convolution in the continuous domain.]

So we need some tricks to make the Gaussian derivatives more precise. In DIPlib (disclosure: I'm an author) the second order derivative is computed as follows (see the relevant bit of source code):

  1. Sample the second order derivative of the Gaussian function.
  2. Subtract the mean, to ensure that the response to a contant function is 0.
  3. Normalize such that the kernel, multiplied by a unit parabola (), sums to 2, to ensure that the response to a parabolic function has the right magnitude.

As we can see, the error in this case is within numerical precision of the double-precision floating-point values:

import diplib as dip
import numpy as np
import matplotlib.pyplot as plt

A = np.zeros((30,30))
A[:,15] = 1

B = dip.Gauss(A, [1], [0,2])  # note dimension order is always [x,y] in DIPlib

plt.imshow(B)
plt.colorbar()
plt.show()

The DIPlib code to generate a 1D second order derivative of the Gaussian is equivalent to the following Python code:

import numpy as np

sigma = 2.0
radius = np.ceil(4.0 * sigma)

x = np.arange(-radius, radius+1)
g = np.exp(-(x**2) / (2.0 * sigma**2)) / (np.sqrt(2.0 * np.pi) * sigma)
g2 = g * (x**2 / sigma**2 - 1) / sigma**2
g2 -= np.mean(g2)
g2 /= np.sum(g2 * x**2) / 2.0
2 of 2
3

I think I have figured out the answer. The results have to do with the difference between the integral of a continuous Gaussian function, versus the sum of a discretized Gaussian.

In the ideal case, continuous and discrete sums of Gaussians already differ

As one illustrative example, consider the integral of a Gaussian versus the discrete summation of a Gaussian with unit steps is $$ \int_{-\infty}^\infty e^{-x^2}\!\text{d}x=\sqrt{\pi}\approx 1.77245,\qquad \sum_{x\in\mathbb{Z}} e^{-x^2} = \theta_3(0,e^{-1})\approx 1.77264.$$ Apparently, the two expressions differ in the details (after 4 decimals). Such things matter when we compare a continuous convolution () against a discrete convolution () using Gaussian kernels.

Symmetric versus asymmetric functions

A Gaussian is a symmetric function, i.e., . The first derivative of a Gaussian is antisymmetric, i.e., . The second derivative of a Gaussian is symmetric again, and so on.

Implication for convolutions

We consider truncated (to terms, although we include in this description) convolutions with Gaussians or their -th derivatives () with the following computation: Knowing that Gaussian functions are either anti-symmetric or symmetric, that means we can 'simplify' the summation to only consider non-negative integer values for ,

We now note that for an odd (i.e., first-order, third-order, ...) derivative of a Gaussian, the antisymmetry also implies that . This is not true for an even derivative of a Gaussian, where .

Specific example for constant functions,

We can thus symbolically write down what happens when convolving a Gaussian derivative with a constant function . For a first derivative, with , we compute that is, it evaluates to 0, which we expect from differentiating a constant function.

Conversely, for a second derivative, , it results in This expression only equals the expected result of 0, if the truncated and discrete kernel sums to 0, which is alternatively expressed as the expression above. This strict requirement is not met for discrete sums of a Gaussian derivative, even without truncation (i.e., when letting the sum is still not 0)!

Explaining the observed behavior

So, I can now answer the question posed above (which was 'why do two successive 1st-order derivatives give the correct result, but a single 2nd-order derivative not?'): after applying the first derivative operation, only zeroes remain (per the result for described above), which leads to field of zeroes for the second convolution operation. Conversely, an immediate application of simply returns the sum of the kernel multiplied with the local value. That second definition should be 0 in the ideal and continuous case -- but because we're working with a discrete application of convolutions it ceases to hold. Even if we use a very large truncation (e.g., specify truncate=1000 when calling the Gaussian filter in Python), it will not converge to 0. Instead, it will converge to (the term comes from the Gaussian which is applied in the horizontal direction), which we can confirm with our code:

import numpy as np
import matplotlib.pyplot as plt
A = np.zeros((30,30))
A[:,15] = 1
from scipy.ndimage import gaussian_filter
B = gaussian_filter(A, sigma=1, order=[2, 0], mode='reflect', truncate=10000)
plt.imshow(B)
plt.colorbar()
plt.show()
print(B[0,15]) # >> -8.426948379372423e-08

So, to conclude, the scipy implementation is correct, and it does what it is supposed to do, from a discrete point of view. However, from a continuous point of view, the results are slightly unexpected and seem wrong. I'll ponder what the most appropriate fix is. Perhaps, we must simply ensure that the summation convention holds, by slightly modifying the kernel , after which the discrete kernel will mimic the continuous kernel, at least for constant functions!

🌐
Svitla Systems
svitla.com › home › articles › numerical differentiation methods in python
Python for Numerical Differentiation: Methods & Tools
January 14, 2021 - To get more information about scipy.misc.derivative, please refer to this manual. It allows you to calculate the first order derivative, second order derivative, and so on. It accepts functions as input and this function can be represented as a Python function.
Price   $$$
Address   100 Meadowcreek Drive, Suite 102, 94925, Corte Madera
🌐
Python Guides
pythonguides.com › python-scipy-derivative-of-array
Python SciPy Derivative Of Array: Calculate With Precision
June 23, 2025 - The second derivative helps identify where sales growth is accelerating or decelerating, which is valuable for inventory planning and marketing strategy. When working with large arrays, performance becomes critical. Here’s a comparison of different methods: import numpy as np import time ...
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.gradient.html
numpy.gradient — NumPy v2.4 Manual
The gradient is computed using second order accurate central differences in the interior points and either first or second order accurate one-sides (forward or backwards) differences at the boundaries.
🌐
YouTube
youtube.com › tsinfo technologies
How to find the derivative of the given function using scipy | Python Scipy derivative - YouTube
In this Python SciPy video tutorial, I will tell you How to find the derivative of the given function using Scipy. Then I will explain how to compute the der...
Published   September 25, 2022
Views   946
Find elsewhere
🌐
Medium
medium.com › @whyamit404 › understanding-derivatives-with-numpy-e54d65fcbc52
Understanding Derivatives with NumPy | by whyamit404 | Medium
February 8, 2025 - When dealing with a higher-order derivative, you simply apply np.gradient() multiple times. For example, to compute the second derivative of a function with respect to x, you’d calculate the derivative of the first derivative.
🌐
YouTube
youtube.com › watch
23 Calculus: Example Second Derivative Test with Python
Enjoy the videos and music you love, upload original content, and share it all with friends, family, and the world on YouTube.
🌐
CSDN
devpress.csdn.net › python › 62fdae137e66823466192d3c.html
Second Derivative in Python - scipy/numpy/pandas_python_Mangs-Python
August 18, 2022 - import matplotlib.pyplot as plt from scipy.interpolate import UnivariateSpline y_spl = UnivariateSpline(x,y,s=0,k=4) plt.semilogy(x,y,'ro',label = 'data') x_range = np.linspace(x[0],x[-1],1000) plt.semilogy(x_range,y_spl(x_range)) The fit seems reasonably good, at least visually. You might want to experiment with the parameters used by UnivariateSpline. The second derivate of the spline fit can be simply obtained as
🌐
Reddit
reddit.com › r/physics › how to take derivatives in python: 3 different types of scenarios
r/Physics on Reddit: How To Take Derivatives In Python: 3 Different Types of Scenarios
August 9, 2021 - The first scenario is when you have an explicit form for your function, such as f(x)=x2 or f(x)=ex sin(x). In such a scenario, the sympy library can be used to take first, second, up to nth derivatives of a function.
🌐
Towards Data Science
towardsdatascience.com › home › latest › taking derivatives in python
Taking Derivatives in Python | Towards Data Science
January 28, 2025 - The product rule states that if f(x) and g(x) are two differentiable functions, then the derivative is calculated as the first function times the derivative of second plus the second times the derivative of first.
🌐
Saturn Cloud
saturncloud.io › blog › second-derivative-in-python-scipynumpypandas
Second Derivative in Python - scipy/numpy/pandas | Saturn Cloud Blog
September 9, 2023 - In this blog post, we explored how to calculate the second derivative in Python using popular libraries such as scipy, numpy, and pandas. We learned that scipy provides a direct method to calculate the second derivative using the scipy.misc.derivative function.
🌐
YouTube
youtube.com › watch
How to: Numerical Derivative in Python - YouTube
Learn how to take a simple numerical derivative of data using a difference formula in Python.Script and resources to download can be found at: https://www.ha...
Published   September 26, 2020
🌐
Python.org
discuss.python.org › python help
Higher order central differences using NumPy.gradient() - Python Help - Discussions on Python.org
September 5, 2022 - Hello everyone, I am new to Python and am still learning it. So my apologies if this is a basic question. I am given two arrays: X and Y. Where Y=2*(x^2)+x/2. I need to calculate the first and the fifth order central differences of Y with respect to X using the numpy.gradient function.
Top answer
1 of 3
10

FFT returns a complex array that has the same dimensions as the input array. The output array is ordered as follows:

  1. Element 0 contains the zero frequency component, F0.

  2. 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.

  3. F2 corresponds to a frequency of 2/(Ni Ti).

  4. Negative frequencies are stored in the reverse order of positive frequencies, ranging from the highest to lowest negative frequencies.

  5. 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.

  6. 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()

2 of 3
9

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:

  1. x = linspace(0,2*pi,N): By constructing your spatial domain like this, your x values will range from $0$ to $2\pi$, inclusive! This is a problem because your function y = 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 using x = linspace(0,2*pi,N, endpoint=False) (or x = 2*pi*arange(0,N,1)/N, as Maxim Umansky did; this is what he is referring to with “open-periodic domain”).
  2. k = fftshift(k): As Maxim Umansky explained, your k values need to be in a specific order to match the FFT convention. fftshift sorts the values (from small/negative to large/positive), which can be useful e. g. for plotting, but is incorrect for computations.
  3. dydx1 = ifft(-k*1j*fft(y)).real: scipy defines the FFT as y(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, for scipy’s FFT convention, the k values 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()
🌐
SciPy
docs.scipy.org › doc › scipy-0.14.0 › reference › generated › scipy.interpolate.UnivariateSpline.derivative.html
scipy.interpolate.UnivariateSpline.derivative — SciPy v0.14.0 Reference Guide
Construct a new spline representing the derivative of this spline. New in version 0.13.0. ... >>> from scipy.interpolate import UnivariateSpline >>> x = np.linspace(0, 10, 70) >>> y = np.sin(x) >>> spl = UnivariateSpline(x, y, k=4, s=0)
🌐
SciPy
docs.scipy.org › doc › scipy-1.15.2 › reference › generated › scipy.differentiate.derivative.html
derivative — SciPy v1.15.2 Manual
The implementation was inspired by jacobi [1], numdifftools [2], and DERIVEST [3], but the implementation follows the theory of Taylor series more straightforwardly (and arguably naively so). In the first iteration, the derivative is estimated using a finite difference formula of order order with maximum step size initial_step.