当前位置:首页>python>Python|比较不同的带通滤波方法

Python|比较不同的带通滤波方法

  • 2026-09-02 20:33:00
Python|比较不同的带通滤波方法

写在前面

空闲一周,突发奇想想测试一下cdo和python的带通滤波函数。我自己常用的滤波方法是cdo和python的butternworth,研究波动的这是跨不去的技术手段。最近看到一篇Bsiso的文章,发现信号提取的第一步基本上也离不开带通滤波。

当然,以上指的主要是时间滤波。空间滤波用到的人太少了这里不管他。

方法比较

这里,比较一下几个内容:

  • 1、滤波速度
  • 2、不同滤波方法的滤波结果
    • signal
    • geocat
    • cdo
  • 3、butterworth滤波参数的选择

cdo

获取输入文件中每个网格点的时间序列,并对其进行快速傅里叶变换,转换到频域。根据特定算子及其参数,频域中的某些频率会被滤波(置零),然后频谱通过逆快速傅里叶变换转换回时域。频率的确定基于输入文件的时间轴。(由于转换需要假设时间增量恒定,因此数据应具有恒定的时间增量。但是,时间增量必须不为零。)所有作为参数给出的频率均按年解释。这是基于365天日历的假设。因此,如果要精确地进行多年滤波,则必须删除2月29日的数据。

如果输入文件采用360年日历,则频率参数fmin和fmax应乘以360/365的系数,才能获得精确的结果。设置频率滤波器时,必须将频率参数调整为数据中的某个频率。此处fmin向下取整,fmax始终向上取整。

使用cdo进行带通滤波注意点:

  • 1、 为了获得可靠的结果,必须对时间序列进行去趋势处理(cdo detrend)

  • 2、 不支持缺失值

    cdo bandpass,x,y -del29feb infile outfile

with x<=0.5 and y >=0.5.

相对90%的人懒得去看说明文档,这里直接给出运行命令。

x,y分布表示滤波的时间,以20-80天为例,且数据分辨率为daily,计算方式为:

[365/80, 365/20] = [4.5625,18.25]

需要传入两个浮点参数,举个例子,同时确保删除了数据中02.29这一天:

signal.butterworth

defbutterworth_filter(data, low_period=80, high_period=30, order=5):
"""
    Butterworth filter with higher order for sharper cutoff.

    Parameters:
    -----------
    data : array-like
        Input time series data
    low_period : int
        Lower period bound in days
    high_period : int
        Upper period bound in days
    order : int
        Filter order (higher = sharper, default=5)

    Returns:
    --------
    filtered_data : array-like
        Filtered time series
    """

from scipy import signal

    low_freq = 2.0 / low_period
    high_freq = 2.0 / high_period

    b, a = signal.butter(order, [low_freq, high_freq], 'bandpass')
    filtered = signal.filtfilt(b, a, data, axis=0, padtype='even')
return filtered

geocat

defgeocat_fourier_bandpass(data, low_period=80, high_period=20):
"""
    Apply band-pass filter using geocat.comp.fourier_band_pass (frequency-domain).
    >>> olr_filtered_geocat = geocat_fourier_bandpass(
    ...     olr_anom_clean, low_period=80, high_period=20)
    """

from geocat.comp.fourier_filters import fourier_band_pass
import time as _time

    fs               = 1.0# daily sampling = 1 cycle/day
    cutoff_freq_low  = 1.0 / low_period   # e.g. 1/80 cycles/day
    cutoff_freq_high = 1.0 / high_period  # e.g. 1/20 cycles/day

    print(f"Applying geocat.comp.fourier_band_pass ({high_period}–{low_period} day)...")
    print(f"  cutoff_frequency_low  = 1/{low_period}  = {cutoff_freq_low:.5f} cycle/day")
    print(f"  cutoff_frequency_high = 1/{high_period} = {cutoff_freq_high:.5f} cycle/day")

    filtered = fourier_band_pass(
        data,
        frequency=fs,
        cutoff_frequency_low=cutoff_freq_low,
        cutoff_frequency_high=cutoff_freq_high,
        time_axis=0,
    )

# Restore coordinates and attributes
    filtered = filtered.assign_coords(data.coords)
    filtered.attrs = data.attrs.copy()
    filtered.attrs['long_name'] = (
f'{high_period}-{low_period} day fourier_band_pass (geocat)'
    )
    filtered.name = 'olr_anomaly_geocat_fourier'

return filtered

比较结果

cdo与signal

比较了一下signal不同滤波方法与cdo的结果,对去除气候态平均和季节日循环的olr进行20-80天的带通滤波

看起来还凑合,如果以cdo为真值的话。但是意外的是,cdo的速度运行非常之慢,这让我非常不理解。 anyway,结果我还是会相信cdo的更准确的/

所有比较结果

下面,对比了各种滤波函数的滤波结果,以及滤波速度,并与cdo的滤波结果进行比较。

总体来说,butterworth的参数order=5和3的时候效果都还不错,比lanczos更接近cdo的结果。其中,geocat的滤波结果最接近cdo的结果,相关系数最高,约为0.99797.当然,这里后面想了一下,也不能完全用cdo当作真值,考虑到cdo的频率参数是四舍五入的。

从计算速度上,butterworth-3最快,geocat也还不赖吧。2s的速度,后续再考虑进行dask加速,应该可以接受。

所以,后续进行带通滤波,还是优先考虑geocat和butterworth-3吧。

★

https://code.mpimet.mpg.de/projects/cdo/embedded/index.html

https://geocat-comp.readthedocs.io/en/v2025.03.0/user_api/generated/geocat.comp.fourier_filters.fourier_band_pass.html

最新文章

随机文章