8000 Merge pull request #8582 from vollbier/magnitude_spectrumScaling · matplotlib/matplotlib@aec1e5b · GitHub
[go: up one dir, main page]

Skip to content

Commit aec1e5b

Browse files
authored
Merge pull request #8582 from vollbier/magnitude_spectrumScaling
API: Changed normalization in _spectral_helper() to obtain consistent scaling
2 parents 3a58953 + ee1c40d commit aec1e5b

File tree

7 files changed

+34
-4
lines changed

7 files changed

+34
-4
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Correct scaling of :func:`magnitude_spectrum()`
2+
```````````````````````````````````````````````
3+
4+
The functions :func:`matplotlib.mlab.magnitude_spectrum()` and :func:`matplotlib.pyplot.magnitude_spectrum()` implicitly assumed the sum
5+
of windowing function values to be one. In Matplotlib and Numpy the
6+
standard windowing functions are scaled to have maximum value of one,
7+
which usually results in a sum of the order of n/2 for a n-point
8+
signal. Thus the amplitude scaling :func:`magnitude_spectrum()` was
9+
off by that amount when using standard windowing functions (`Bug 8417
10+
<https://github.com/matplotlib/matplotlib/issues/8417>`_ ). Now the
11+
behavior is consistent with :func:`matplotlib.pyplot.psd()` and
12+
:func:`scipy.signal.welch()`. The following example demonstrates the
13+
new and old scaling::
14+
15+
import matplotlib.pyplot as plt
16+
import numpy as np
17+
18+
tau, n = 10, 1024 # 10 second signal with 1024 points
19+
T = tau/n # sampling interval
20+
t = np.arange(n)*T
21+
22+
a = 4 # amplitude
23+
x = a*np.sin(40*np.pi*t) # 20 Hz sine with amplitude a
24+
25+
# New correct behavior: Amplitude at 20 Hz is a/2
26+
plt.magnitude_spectrum(x, Fs=1/T, sides='onesided', scale='linear')
27+
28+
# Original behavior: Amplitude at 20 Hz is (a/2)*(n/2) for a Hanning window
29+
w = np.hanning(n) # default window is a Hanning window
30+
plt.magnitude_spectrum(x*np.sum(w), Fs=1/T, sides='onesided', scale='linear')
31+

lib/matplotlib/axes/_axes.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6666,8 +6666,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None,
66666666
66676667
scale : [ 'default' | 'linear' | 'dB' ]
66686668
The scaling of the values in the *spec*. 'linear' is no scaling.
6669-
'dB' returns the values in dB scale. When *mode* is 'density',
6670-
this is dB power (10 * log10). Otherwise this is dB amplitude
6669+
'dB' returns the values in dB scale, i.e., the dB amplitude
66716670
(20 * log10). 'default' is 'linear'.
66726671
66736672
Fc : integer

lib/matplotlib/mlab.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,12 +727,12 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None,
727727
elif mode == 'psd':
728728
result = np.conj(result) * result
729729
elif mode == 'magnitude':
730-
result = np.abs(result)
730+
result = np.abs(result) / np.abs(windowVals).sum()
731731
elif mode == 'angle' or mode == 'phase':
732732
# we unwrap the phase later to handle the onesided vs. twosided case
733733
result = np.angle(result)
734734
elif mode == 'complex':
735-
pass
735+
result /= np.abs(windowVals).sum()
736736

737737
if mode == 'psd':
738738

Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)
0