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
FFT of 1D NMR spectra๏
First we open read some time domain data. Here is a NMD free induction decay (FID):
Requires the official spectrochempy-nmr plugin. Install with: pip install spectrochempy[nmr].
[2]:
path = scp.preferences.datadir / "nmrdata" / "bruker" / "tests" / "nmr" / "topspin_1d"
fid = scp.nmr.read_topspin(path)
fid
WARNING | (UserWarning) No module named 'spectrochempy_hypercomplex'
The type of the data is complex:
[3]:
fid.dtype
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[3], line 1
----> 1 fid.dtype
AttributeError: 'NoneType' object has no attribute 'dtype'
We can represent both real and imaginary parts on the same plot using the show_complex parameter.
[4]:
prefs = scp.preferences
prefs.figure.figsize = (6, 3)
_ = fid.plot(show_complex=True, xlim=(0, 15000))
print("td = ", fid.size)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[4], line 3
1 prefs = scp.preferences
2 prefs.figure.figsize = (6, 3)
----> 3 _ = fid.plot(show_complex=True, xlim=(0, 15000))
4 print("td = ", fid.size)
AttributeError: 'NoneType' object has no attribute 'plot'
Now we perform a Fast Fourier Transform (FFT):
[5]:
spec = scp.fft(fid)
_ = spec.plot(xlim=(100, -100))
print("si = ", spec.size)
spec
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[5], line 1
----> 1 spec = scp.fft(fid)
2 _ = spec.plot(xlim=(100, -100))
3 print("si = ", spec.size)
4 spec
File ~/work/spectrochempy/spectrochempy/.venv/lib/python3.13/site-packages/spectrochempy/processing/fft/fft.py:166, in fft(dataset, size, sizeff, inv, **kwargs)
118 def fft(dataset, size=None, sizeff=None, inv=False, **kwargs):
119 """
120 Apply a complex fast fourier transform.
121
(...) 164
165 """
--> 166 is_ir = dataset.meta.interferogram
168 # On which axis do we want to apply transform (get axis from arguments)
169 dim = kwargs.pop("dim", kwargs.pop("axis", -1))
AttributeError: 'NoneType' object has no attribute 'meta'
Alternative notation
[6]:
k = 1024
spec = fid.fft(size=32 * k)
_ = spec.plot(xlim=(100, -100))
print("si = ", spec.size)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[6], line 2
1 k = 1024
----> 2 spec = fid.fft(size=32 * k)
3 _ = spec.plot(xlim=(100, -100))
4 print("si = ", spec.size)
AttributeError: 'NoneType' object has no attribute 'fft'
[7]:
newfid = spec.ifft()
# x coordinate is in second (base units) so lets transform it
_ = newfid.plot(show_complex=True, xlim=(0, 15000))
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[7], line 1
----> 1 newfid = spec.ifft()
2 # x coordinate is in second (base units) so lets transform it
3 _ = newfid.plot(show_complex=True, xlim=(0, 15000))
NameError: name 'spec' is not defined
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()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[8], line 1
----> 1 _ = newfid.real.plot(c="r", label="fft + ifft")
2 ax = fid.real.plot(clear=False, xlim=(0, 5000), ls="--", label="original real part")
3 _ = ax.legend()
NameError: name 'newfid' is not defined
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")
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[9], line 1
----> 1 _ = fid.imag.plot(ls="--", label="original imaginary part")
2 ax = newfid.imag.plot(clear=False, xlim=(0, 5000), c="r", label="fft + ifft")
3 _ = ax.legend(loc="lower right")
AttributeError: 'NoneType' object has no attribute 'imag'
Preprocessing๏
Line broadening๏
Often before applying FFT, some exponential multiplication emor 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.
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[10], line 1
----> 1 fid2 = fid.em(lb="50. Hz")
2 spec2 = fid2.fft()
3 _ = spec2.plot()
4 _ = spec.plot(
AttributeError: 'NoneType' object has no attribute 'em'
Zero-filling๏
[11]:
print("td = ", fid.size)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[11], line 1
----> 1 print("td = ", fid.size)
AttributeError: 'NoneType' object has no attribute 'size'
[12]:
td = 64 * 1024 # size: 64 K
fid3 = fid.zf_size(size=td)
print("new td = ", fid3.x.size)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[12], line 2
1 td = 64 * 1024 # size: 64 K
----> 2 fid3 = fid.zf_size(size=td)
3 print("new td = ", fid3.x.size)
AttributeError: 'NoneType' object has no attribute 'zf_size'
[13]:
spec3 = fid3.fft()
_ = spec3.plot(xlim=(100, -100))
print("si = ", spec3.size)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[13], line 1
----> 1 spec3 = fid3.fft()
2 _ = spec3.plot(xlim=(100, -100))
3 print("si = ", spec3.size)
NameError: name 'fid3' is not defined
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")
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[14], line 1
----> 1 ms = spec.mc()
2 _ = ms.plot(xlim=(10, -10))
3 _ = spec.plot(clear=False, xlim=(10, -10), c="r")
NameError: name 'spec' is not defined
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.
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[15], line 1
----> 1 mp = spec.ps()
2 _ = (mp / mp.max()).plot()
3 _ = (spec / spec.max()).plot(
4 clear=False, xlim=(10, -10), c="r"
NameError: name 'spec' is not defined
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)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[16], line 2
1 lim = (-20, 20)
----> 2 _ = spec3.plot(xlim=lim)
3 _ = spec3.imag.plot(xlim=lim)
NameError: name 'spec3' is not defined
[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)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[17], line 1
----> 1 Re = fid3.real.astype("complex64")
2 fR = Re.fft()
3 _ = fR.plot(xlim=lim, show_complex=True)
4 Im = fid3.imag.astype("complex64")
NameError: name 'fid3' is not defined
Recombination:
[18]:
_ = (fR - fI.imag).plot(xlim=lim)
_ = (fR.imag + fI).plot(xlim=lim)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[18], line 1
----> 1 _ = (fR - fI.imag).plot(xlim=lim)
2 _ = (fR.imag + fI).plot(xlim=lim)
NameError: name 'fR' is not defined