Природа шума и скользящее среднее
Урок про самый простой и самый употребимый фильтр — усреднение по окну.
Скользящее среднее — фильтр, заменяющий каждый отсчёт средним по окну из нескольких соседних отсчётов.
Сигнал датчика дрожит: тепловой шум, наводки, дрожание питания. Если шум случаен и в среднем равен нулю, усреднение нескольких отсчётов его подавляет, оставляя полезный медленный сигнал. Это первый фильтр, который стоит попробовать.
Как усреднение давит шум
Если усреднить $N$ независимых отсчётов с шумом стандартного отклонения $\sigma$, шум среднего падает в $\sqrt{N}$ раз:
$$ \sigma_{avg} = \frac{\sigma}{\sqrt{N}} $$
Чтобы вдвое уменьшить шум, нужно вчетверо больше отсчётов — за чистоту платят инерцией.
import random, statistics
random.seed(1)
true_value = 25.0
# 100 шумных отсчётов
raw = [true_value + random.gauss(0, 2.0) for _ in range(100)]
print("шум одного отсчёта:", round(statistics.pstdev(raw), 3))
# усредняем по окну 9
N = 9
means = [statistics.mean(raw[i:i+N]) for i in range(len(raw)-N+1)]
print("шум среднего по 9:", round(statistics.pstdev(means), 3))
print("ожидаемо (sigma/3):", round(2.0/3, 3))Вывод:
шум одного отсчёта: 1.879 шум среднего по 9: 0.622 ожидаемо (sigma/3): 0.667
Реализация скользящего окна
На потоке данных среднее считают по последним $w$ отсчётам, окно «скользит» вперёд:
def moving_average(xs, w):
out = []
for i in range(len(xs)):
lo = max(0, i - w + 1)
window = xs[lo:i+1]
out.append(round(sum(window) / len(window), 2))
return out
data = [20.1, 22.3, 19.8, 25.0, 21.1, 20.5, 23.2]
print(moving_average(data, 3))Вывод:
[20.1, 21.2, 20.73, 22.37, 21.97, 22.2, 21.6]
Как работает под капотом
Скользящее среднее — это фильтр нижних частот: оно пропускает медленные изменения и срезает быстрые (шум). Чем шире окно, тем сильнее подавление шума, но тем больше задержка отклика на реальное изменение сигнала. Это всегда компромисс «чистота против скорости». Усреднение хорошо работает против белого случайного шума, но бессильно против выбросов: один резко выпавший отсчёт «протащит» среднее за собой — для выбросов нужен медианный фильтр.
Частые ошибки
- Брать слишком широкое окно — фильтр станет инертным и «смажет» реальные изменения.
- Ждать, что усреднение уберёт выброс — оно его лишь размажет, но не удалит.
- Считать, что шум падает линейно с $N$ — он падает как $\sqrt{N}$.
Итог
- Скользящее среднее усредняет окно отсчётов, подавляя случайный шум.
- Шум среднего падает как $\sigma/\sqrt{N}$.
- Широкое окно чище, но медленнее; против выбросов нужен другой фильтр.