【numpy】从时域到频域:详解fft与rfft的实战选择与频率映射 1. 从时域到频域FFT与RFFT的核心差异第一次接触傅里叶变换时我盯着屏幕上那些对称的复数结果发呆了半小时。直到后来才发现原来numpy早就为我们准备了两种翻译官——fft和rfft它们都能把时域信号转换成频域但工作方式却大不相同。fft就像个严谨的语言学家对所有输入一视同仁。无论你给的是实数还是复数信号它都会输出完整的N个频点。比如对一个12点的正弦波做fftimport numpy as np t np.arange(12) signal np.sin(t) spectrum np.fft.fft(signal) print(spectrum.shape) # 输出(12,)而rfft则像个精明的会计发现实数信号的频谱具有共轭对称性后果断只计算前(N//2)1个点。同样的信号用rfft处理compact_spectrum np.fft.rfft(signal) print(compact_spectrum.shape) # 输出(7,)这种差异带来的内存节省在长信号处理中非常可观。我处理过一个采样率48kHz的音频文件一小时录音包含1.7亿个采样点。用fft需要约2.6GB内存而rfft仅需1.3GB——相当于省出了一部高清电影的空间。2. 实战选择什么时候该用哪个去年优化一个嵌入式心率检测算法时我在树莓派上做过对比测试。处理5分钟的心电信号采样率250Hzfft耗时3.2秒rfft仅用1.8秒。这是因为计算复杂度rfft只需计算约一半的频点理论计算量减少近50%内存访问紧凑的内存布局能更好利用CPU缓存后续处理很多特征提取只需正频率部分但要注意几个关键限制当需要相位信息或处理复数信号时必须使用fft某些频域操作如卷积需要完整频谱逆变换时要严格配对ifft配fftirfft配rfft我曾踩过一个坑用rfft处理信号后想用ifft还原结果得到一堆复数。后来才明白应该用irfft# 错误示范 reconstructed np.fft.ifft(compact_spectrum) # 输出复数 # 正确做法 reconstructed np.fft.irfft(compact_spectrum) # 输出实数3. 频率映射让频谱对应真实物理世界第一次用fft分析吉他录音时我发现频谱峰值出现在第247个bin却不知道对应哪个音高。直到学会用fftfreq才解开这个谜题sample_rate 44100 # 音频采样率 freq_bins np.fft.fftfreq(len(signal), d1/sample_rate) peak_freq freq_bins[247] # 现在知道是440Hz的A4音了对于rfft的结果要用rfftfreq获取对应的频率轴。这两个函数的区别就像全屏和半屏模式函数输出长度频率范围适用场景fftfreqN[-Fs/2, Fs/2)完整频谱分析rfftfreqN//21[0, Fs/2]实数信号快速分析在振动监测项目中我发现rfftfreq有个实用特性它把奈奎斯特频率Fs/2算作正频率。这与某些教科书定义不同但更符合工程直觉。4. 完整案例音频频谱分析实战让我们用一段真实的鸟鸣录音来串联所有知识点。假设我们有个WAV文件from scipy.io import wavfile sample_rate, audio wavfile.read(bird.wav) mono_audio audio.mean(axis1) # 转为单声道 # 加窗减少频谱泄漏 window np.hanning(len(mono_audio)) windowed_signal mono_audio * window # 计算功率谱 spectrum np.fft.rfft(windowed_signal) freqs np.fft.rfftfreq(len(windowed_signal), d1/sample_rate) power np.abs(spectrum) ** 2 # 找到主频 peak_index np.argmax(power[1:]) 1 # 跳过直流分量 print(f主频:{freqs[peak_index]:.1f}Hz, 强度:{power[peak_index]:.0f})这个流程我优化过三次第一次发现没加窗导致频谱模糊第二次忘记跳过直流分量第三次才学会用rfft提升效率。现在处理3分钟音频仅需0.8秒比最初版本快6倍。对于需要实时处理的应用可以分段计算频谱。比如每1024个点做一次rfft再用重叠保留法减少边界效应。这种技巧在语音识别中很常见能平衡实时性和频率分辨率。