One-dimensional (1D) Fourier transformation
In this notebook, we are going to transform time-domain data into 1D or 2D spectra using SpectroChemPy processing tools
[1]:
import spectrochempy as scp
|
SpectroChemPy's API - v.0.7.0 © Copyright 2014-2025 - A.Travert & C.Fernandez @ LCS |
FFT of 1D NMR spectra
First we open read some time domain data. Here is a NMD free induction decay (FID):
[2]:
path = scp.preferences.datadir / "nmrdata" / "bruker" / "tests" / "nmr" / "topspin_1d"
fid = scp.read_topspin(path)
fid
[2]:
name | topspin_1d expno:1 procno:1 (FID) |
author | runner@fv-az1670-365 |
created | 2025-02-18 09:42:42+00:00 |
DATA | |
title | intensity |
values | R[ 1078 2284 ... 0.2342 -0.1008] pp I[ -1037 -2200 ... 0.06203 -0.05273] pp |
size | 12411 (complex) |
DIMENSION `x` | |
size | 12411 |
title | F1 acquisition time |
coordinates | [ 0 4 ... 4.964e+04 4.964e+04] µs |
The type of the data is complex:
[3]:
fid.dtype
[3]:
dtype('complex128')
We can represent both real and imaginary parts on the same plot using the show_complex
parameter.
[4]:
prefs = fid.preferences
prefs.figure.figsize = (6, 3)
_ = fid.plot(show_complex=True, xlim=(0, 15000))
print("td = ", fid.size)
td = 12411

Now we perform a Fast Fourier Transform (FFT):
[5]:
spec = scp.fft(fid)
_ = spec.plot(xlim=(100, -100))
print("si = ", spec.size)
spec
si = 16384
[5]:
name | topspin_1d expno:1 procno:1 (FID) |
author | runner@fv-az1670-365 |
created | 2025-02-18 09:42:42+00:00 |
history | 2025-02-18 09:42:43+00:00> `zf_size` shift performed on dimension `x` with parameters: {'size': 16384} 2025-02-18 09:42:43+00:00> Fft applied on dimension x 2025-02-18 09:42:43+00:00> Inplace binary op: imul with `[ 0.61+0.793j 0.61+0.793j ... 0.813+0.582j 0.813+0.582j]` 2025-02-18 09:42:43+00:00> `pk` applied to dimension `x` with parameters: {'phc0': 52.43836, 'phc1': -16.8366, 'pivot': 0.0, 'exptc': 0.0} |
DATA | |
title | intensity |
values | R[ -1093 -1014 ... -1031 -1079] pp I[ -229.7 -126.8 ... 187.2 216.1] pp |
size | 16384 (complex) |
DIMENSION `x` | |
size | 16384 |
title | $\delta\ ^{1}H$ |
coordinates | [ 253.1 253.1 ... -246.8 -246.8] ppm |

Alternative notation
[6]:
k = 1024
spec = fid.fft(size=32 * k)
_ = spec.plot(xlim=(100, -100))
print("si = ", spec.size)
si = 32768

[7]:
newfid = spec.ifft()
# x coordinate is in second (base units) so lets transform it
_ = newfid.plot(show_complex=True, xlim=(0, 15000))

Let’s compare fid and newfid. There differs as a rephasing has been automatically applied after the first FFT (with the parameters found in the original fid metadata: PHC0 and PHC1).
First point in the time domain of the real part is at the maximum.
[8]:
_ = newfid.real.plot(c="r", label="fft + ifft")
ax = fid.real.plot(clear=False, xlim=(0, 5000), ls="--", label="original real part")
_ = ax.legend()

First point in the time domain of the imaginary part is at the minimum.
[9]:
_ = fid.imag.plot(ls="--", label="original imaginary part")
ax = newfid.imag.plot(clear=False, xlim=(0, 5000), c="r", label="fft + ifft")
_ = ax.legend(loc="lower right")

Preprocessing
Line broadening
Often before applying FFT, some exponential multiplication em
or other broadening filters such as gm
or sp
are applied. See the dedicated apodization tutorial.
[10]:
fid2 = fid.em(lb="50. Hz")
spec2 = fid2.fft()
_ = spec2.plot()
_ = spec.plot(
clear=False, xlim=(10, -5), c="r"
) # superpose the non broadened spectrum in red and show expansion.

Zero-filling
[11]:
print("td = ", fid.size)
td = 12411
[12]:
td = 64 * 1024 # size: 64 K
fid3 = fid.zf_size(size=td)
print("new td = ", fid3.x.size)
new td = 65536
[13]:
spec3 = fid3.fft()
_ = spec3.plot(xlim=(100, -100))
print("si = ", spec3.size)
si = 65536

Time domain baseline correction
See the dedicated Time domain baseline correction tutorial.
Magnitude calculation
[14]:
ms = spec.mc()
_ = ms.plot(xlim=(10, -10))
_ = spec.plot(clear=False, xlim=(10, -10), c="r")

Power spectrum
[15]:
mp = spec.ps()
_ = (mp / mp.max()).plot()
_ = (spec / spec.max()).plot(
clear=False, xlim=(10, -10), c="r"
) # Here we have normalized the spectra at their max value.

Real Fourier transform
In some case, it might be interesting to perform real Fourier transform . For instance, as a demonstration, we will independently transform real and imaginary part of the previous fid, and recombine them to obtain the same result as when performing complex fourier transform on the complex dataset.
[16]:
lim = (-20, 20)
_ = spec3.plot(xlim=lim)
_ = spec3.imag.plot(xlim=lim)


[17]:
Re = fid3.real.astype("complex64")
fR = Re.fft()
_ = fR.plot(xlim=lim, show_complex=True)
Im = fid3.imag.astype("complex64")
fI = Im.fft()
_ = fI.plot(xlim=lim, show_complex=True)


Recombination:
[18]:
_ = (fR - fI.imag).plot(xlim=lim)
_ = (fR.imag + fI).plot(xlim=lim)

