短时傅里叶矩阵(STFT matrix)的形状为(n_frames, n_fft),即时间帧数×频率点数;手动实现需分帧后沿第二维做FFT,scipy.signal.stft返回的Zxx形状为(n_freqs, n_times),需转置为(n_times, n_freqs)才与常规一致。

什么是短时傅里叶矩阵(STFT matrix)的形状?
它不是直接用 reshape 能搞定的——一维信号切片后要重叠拼成二维,每列(或每行)代表一个时间帧,每行(或每列)代表一个频率 bin。NumPy 本身不提供 STFT 实现,numpy.fft 只做单次 FFT;你需要手动分帧 + FFT,或借助 scipy.signal.stft 得到标准输出,再提取复数谱矩阵。
手动分帧 + 沿轴 FFT 的关键三步
适合想控制窗长、步长、是否补零等细节的场景。核心是:用 numpy.lib.stride_tricks.sliding_window_view(>=1.20)或手写索引切片生成帧,再对每帧做 numpy.fft.fft:
- 先确定帧长
n_fft和步长hop_length(如n_fft=256,hop_length=128) - 用
sliding_window_view(x, window_shape=n_fft)[::hop_length]得到 shape 为(n_frames, n_fft)的帧数组 - 对第二维做 FFT:
np.fft.fft(frames, axis=1)→ 输出 shape(n_frames, n_fft),即你要的 STFT 矩阵 - 注意:默认返回全频谱(含负频率),如只需单边谱,取
stft_matrix[:, :n_fft//2+1]
用 scipy.signal.stft 更稳妥但要注意返回结构
它返回 f(频率点)、t(时间点)、Zxx(复数 STFT 矩阵),其中 Zxx.shape 是 (n_freqs, n_times) —— 频率在前,时间在后,和手动实现的常见习惯(时间在前)相反:
-
Zxx是你要的矩阵,但行列顺序需按需转置:Zxx.T得到(n_times, n_freqs) - 默认窗是汉宁窗,可传
window='boxcar'或自定义数组;return_onesided=False才返回全谱 - 若输入是实信号且只关心幅度谱,直接用
np.abs(Zxx)即可,无需手动fft - 常见坑:
scipy>=1.9.0才默认启用多线程加速,旧版本可能慢;n_fft必须 ≥ 信号长度才不会报错
性能与内存陷阱:别让 sliding_window_view 复制数据
该函数返回的是视图(view),不复制原始内存——但前提是步长整除、无跨边界访问。一旦触发条件不满足(比如 hop_length=100 而 n_fft=256),它会静默退化为复制(copy),大幅增加内存占用:
立即学习“Python免费学习笔记(深入)”;
- 检查是否为视图:
frames.base is not None为True表示是视图 - 更安全的手动切片写法(兼容老 NumPy):
frames = np.array([x[i:i+n_fft] for i in range(0, len(x)-n_fft+1, hop_length)]),但明确是 copy - 如果信号很长(如 >100 万点),优先用
scipy.signal.stft,它内部做了内存优化,且支持流式分块处理
真正麻烦的不是 FFT 本身,而是帧边界如何对齐、窗函数怎么加、归一化要不要做——这些细节不显式处理,矩阵看着像,相位和幅度就全偏了。


















