Fourier Transform, Applied (3): Magnitude and phase encoding in complex data

Reimplementing np.abs and np.angle to better understand the FFT

Peter Barrett Bryan
Towards Data Science

--

We’ve built a common sense understanding of what magnitude and phase values from the Fourier transform can tell us about the composite frequencies of a signal. Now, we’ll take a pit stop to appreciate how the np.abs and np.angle functions work under the hood. To get there, we’ll also introduce a working definition for complex numbers!

Check out the previous articles in the series!

Complex number basics

Later in this series, we’ll get a touch more technical in our treatment of complex numbers. For now, though, we only need to recognize complex numbers as composites of two parts: a real component and an imaginary component.

We’ll come back to some of the magical properties of complex numbers (and introduce a special guest named Euler) soon, but to get to magnitude and phase we can consider complex numbers as simple two-dimensional values. Each complex number is a pair of a real value and an imaginary value. Like any two-dimensional, we can plot these complex pairs. By convention, we usually put our real component on the x-axis and our imaginary on the y-axis.

Figure 1: Visualizing magnitude and phase. Image by author.

The imaginary part of our complex numbers are a multiple of i (or j, depending on who you ask): (-1). To the left, I’ve visualized the complex number 1+1j. A complex number with a real value of 1 and an imaginary value of 1. Again, the real component is on the x-axis and the imaginary component is on the y-axis. As expected, this puts us squarely in the first quadrant.

For now, we don’t need to worry too much about why the y-axis is expressed as a coefficient of j. The geometric intuitions work as long as you accept that we can treat this imaginary value as a second dimension. We’ll come back to it, I promise!

The values returned by the Fourier transform are complex. Calling np.abs on these complex values gives us the magnitude for each. Calling np.angle on these complex values gives the phase angle for each. In this way, magnitude and phase are encoded in the complex values of the Fourier transform.

Reimplementing np.abs

The magnitude of a complex number is just the Euclidean distance to the origin (0+0j): the square root of the sum of the squares. As shown in the plot above, for 1+1j, this is (1² + 1²)≈1.41 (think Pythagorean theorem!). Below, I’ve included the logic for an arbitrary complex number:

Reimplementing np.angle

The phase of a complex number is the standard angle (clockwise from right). As shown in the plot above, for 1+1j, this is arctan2(1, 1)≈0.79 radians. Checking against degrees (0.79 radians * 180° / π radians ≈ 45°), the value correctly reflects the angle from the plot above. If the arctan made your blood pressure spike, take a minute to review the inverse trigonometric identities that allow us to recover this angle!

Below, I’ve included the logic for an arbitrary complex number:

Great! Taking stock, we now have a good intuitive grasp for the meaning of the magnitude and phase of the Fourier transform. We have a basic handle on complex numbers and the way that magnitude and phase information can be recovered from them.

Up next, we’ll look at the inverse Fourier transform (IFFT) and demonstrate what we can accomplish with the toolkit we’ve developed so far!

Give the article a clap if the code or text were valuable to you! Thanks for reading!

--

--

Software engineer with specializations in remote sensing, machine learning applied to computer vision, and project management.