You are on page 1of 6

Discrete Fourier Transform

A DFT is a reversible, composable transform for sampled data. Sampled data is a sequence of numbers -- complex numbers, in
general.

Number of Samples
Start with a global variable denoting the number of samples in all examples in the rest of this note. Pick any number large enough to
be interesting but small enough to be understandable.

In[1]:= n = 60;

Example Data
A quick way to get n points of real-world data is to grab, say, temperature in Seattle.

st = ðP2T & ž
Take@WeatherData@"Seattle", "Temperature", 82010, 3<D, nD;
In[2]:=

In[3]:= ListPlot@st, Joined ® TrueD

16

14

12

Out[3]=
10

10 20 30 40 50 60

Transform-Kernel Function
The kernel of a transform is a function independent of the data: you give me a wave number 0 £ k < n and a point index 0 £ p < n
and I give you back a complex number.

-2 Π ä
kp
ã n
In[4]:= kerAk_, p_E :=
n
Examine. Fix k and let p vary (just for future generality, let the particular kernel function vary, too, though it won't change in this note):

In[5]:= eksAker_, k_E := Table@ker@k, pD, 8p, 0, n - 1<D


2 DFT002.nb

In[6]:= Take@eks@ker, 1D, 5D


äΠ äΠ äΠ 2äΠ
- - - -

: >
1 ã 30 ã 15 ã 10 ã 15

Out[6]= , , , ,
2 15 2 15 2 15 2 15 2 15

Complex numbers. There are lots of interesting ways to plot them. Plot the Real part on the horizontal axis and the Imaginary part
on the vertical axis (the Argand diagram).

In[7]:= ReImAc_E := 8Režc, Imžc<

plotComplexesAcomplexes_, showLines_: FalseE := Module@


8points = ReIm ž complexes, lp<,
In[8]:=

lp = ListPlot@List ž points, PlotMarkers ® Table@i, 8i, 0, n - 1<D,


H* NOTE:
Mathematica Bug: cannot use "Range" under PlotMarkers *L
Axes ® False, Frame ® True, AspectRatio ® 1D;
If@showLines, Show@lp, ListLinePlot@pointsDD, lpDD
For wave number 1, the (complex-number) values of the kernel distribute around the unit circle as closely and finely as possible,
and they go clockwise because of the minus sign in the exponent.

In[9]:= plotComplexes@eks@ker, 1DD

44 45 46 47
42 43 48
41 49
40 50
39 51
0.10 38 52
37 53
36 54
35 55
0.05 34 56
33 57
32 58
31 59
0.00 30 0
Out[9]=
29 1
28 2
27 3
-0.05 26 4
25 5
24 6
23 7
-0.10 22 8
21 9
20 10
19 11
18 17 12
16 15 14 13
-0.10 -0.05 0.00 0.05 0.10

With larger wave numbers k, values for adjacent point indices p skip k-1 places
DFT002.nb 3

In[10]:= plotComplexes@eks@ker, 7DD

32 15 58 41
6 49 24
23 7
40 50
57 33
0.10 14 16
31 59
48 42
5 25
0.05 22 8
39 51
56 34
13 17
0.00 30 0
Out[10]=
47 43
4 26
21 9
-0.05 38 52
55 35
12 18
29 1
-0.10 46 44
3 27
20 10
37 53
54 11 36
28 45 2 19
-0.10 -0.05 0.00 0.05 0.10

k is 7, above; k-1 is 6, and in between the value for point-index 0 and the value for point-index 1 are the 6 values for point indices
43, 26, 9, 52, 35, and 18, clockwise.
If the wave number k divides n, then some values land on top of others; in fact, there will be n/k clumps of values:

In[11]:= plotComplexes@eks@ker, 6DD

17
27
37
47
57
7 18
28
38
48
58
8

0.10

16
26
36
46
56
6 19
29
39
49
59
9

0.05

0.00 55
15
25
35
45
5 10
20
30
40
50
0
Out[11]=

-0.05

14
24
34
44
54
4 11
21
31
41
51
1

-0.10

13
23
33
43
53
3 12
22
32
42
52
2
-0.10 -0.05 0.00 0.05 0.10
4 DFT002.nb

Inner Product
Since the number of point indices p in a fixed-wave-number run of the kernel function is n, the same as the number of samples in
the data, imagine the kernel-run and the data each as an n-dimensional vector in an n-dimensional complex vector space. If you
have vectors, you have inner products, one for each wave number.

In[12]:= ipAdata_, kernel_, k_E := Plus žž Hdata * eks@kernel, kDL

In[13]:= ip@st, ker, 1D  Simplify

-1 - 4 H-1L215 - 6 H-1L730 - 6 H-1L415 -


1 1 9ä 15 10
Out[13]= - + -5 -1 - 4
2 15 2 2
5 H-1L310 + 2 H-1L1330 + 6 H-1L715 + H5 + 5 äL H-1L815 + 7 H-1L1730 + 7 H-1L35 + 6 H-1L1930 +

H-1L1115 + H-1L2330 + H-1L45 - H-1L910 - 2 H-1L1415 - 2 H-1L2930 -


3 ä 30
- 3 - 2 -1 3
2 2

Mathematica does exact math, and the answer is heady. All that arithmetic would have taken a lot of time on paper, and the
chances of getting it right are small.
An inner product is just one complex number. Collect inner products for every k from 0 through n-1, save them all in a variable dft,
and just look at the top 3 (because the arithmetic is complicated enough to be impressive, but too complicated to be useful):

In[14]:= Take@dft = Table@ip@st, ker, kD, 8k, 0, n - 1<D  Simplify, 3D

: -1 - 4 H-1L215 - 6 H-1L730 -
643 1 1 9ä 15 10
Out[14]= , - + -5 -1 - 4
2 15 2 15 2 2
6 H-1L415 - 5 H-1L310 + 2 H-1L1330 + 6 H-1L715 + H5 + 5 äL H-1L815 + 7 H-1L1730 + 7 H-1L35 + 6 H-1L1930 +

H-1L1115 + H-1L2330 + H-1L45 - H-1L910 - 2 H-1L1415 - 2 H-1L2930 -


3 ä 30
- 3 - 2 -1 3 ,
2 2

K3 + 2 H-1L215 + 3 -1 + 2 H-1L415 + 7 H-1L715 + 9 H-1L815 + 10 H-1L35 +


1 5

2 15

3 H-1L45 + 2 H-1L1315 - 2 H-1L1415 + 5 ä 3 + 4 H-1L1730 3 O>

Now plot these results in the Argand plane:


DFT002.nb 5

In[15]:= plotComplexes@dftD

3
56

2
53 54
5
48
42
51
1 49
52 45 46
31 43 38 44
10 27 23 32
36 21 4034
35
13
Out[15]=
0 24 41
30 19
39 20 25
26
50 33 28 47
29 17 37
22 16
15 14
8
-1 11
9
18
12
55
7 6
-2

4
-3

-0.5 0.0 0.5 1.0 1.5

There is a bit of a pattern, here, and probably some very interesting info, but it's better to look at the Re and Im parts as functions of
the index. First, the real part

In[16]:= ListPlot@Re ž dft, Joined ® TrueD

1.5

1.0

Out[16]= 0.5

10 20 30 40 50 60

-0.5

Oh, look. Except for the weirdness at the ends, there are clear peaks at wave-numbers 12 and 48. Remember the source is
temperature data in Seattle? We would expect it to change every 12 hours! And combe back every 48 too. Not sure why we don't
have something obvious at 24 hours. Look at the Imaginary part:
6 DFT002.nb

In[17]:= ListPlot@Im ž dft, Joined ® TrueD

Out[17]=
10 20 30 40 50 60
-1

-2

-3

Nothing too instructive here.

Big Magic: Inverse Transform


The big magic is that we can get back the original data, by just repeating the transform process with negative wave numbers,
varying from 0 to -Hn - 1L. Use "chop" to get rid of floating-point flotsam, separately for the Real and Imaginary parts, notice that the
Imaginary parts all go to virtual zero. It's fabulous!

Module@8inv, invRes, invIms<,


inv = Table@Nžip@dft, ker, kD, 8k, 0, -Hn - 1L, -1<D;
In[18]:=

invRes = Chop@Re ž invD;


invIms = Chop@Im ž invD;
invRes + ä invImsD

Out[18]= 814., 13., 12., 11., 11., 10., 8., 8., 8., 8., 7., 7., 6., 6., 6., 7., 8., 11., 12., 13., 14., 16., 16., 15., 14., 13., 13., 13., 13., 11.,
11., 11., 10., 10., 10., 9., 9., 9., 9., 10., 11., 13., 13., 13., 14., 14., 14., 13., 12., 11., 11., 11., 10., 9., 9., 9., 9., 9., 8., 8.<

In[19]:= % Š Nžst

Out[19]= True

You might also like