filtfilt
é a filtragem de fase zero, que não muda o sinal ao filtrar. Como a fase é zero em todas as frequências, também é linear. A filtragem para trás no tempo exige que você preveja o futuro, para que não possa ser usado em aplicativos da vida real "online", apenas para processamento offline de gravações de sinais.
lfilter
é apenas uma filtragem causal de encaminhamento no tempo, semelhante a um filtro eletrônico da vida real. Não pode ser de fase zero. Pode ser de fase linear (FIR simétrico), mas geralmente não é. Geralmente, ele adiciona diferentes quantidades de atraso em diferentes frequências.
Um exemplo e imagem devem torná-lo óbvio. Embora a magnitude da resposta de frequência dos filtros seja idêntica (canto superior esquerdo e canto superior direito), o passa-baixo da fase zero se alinha com o sinal original, apenas sem conteúdo de alta frequência, enquanto a filtragem mínima de fase atrasa o sinal de maneira causal :
from __future__ import division, print_function
import numpy as np
from numpy.random import randn
from numpy.fft import rfft
from scipy import signal
import matplotlib.pyplot as plt
b, a = signal.butter(4, 0.03, analog=False)
# Show that frequency response is the same
impulse = np.zeros(1000)
impulse[500] = 1
# Applies filter forward and backward in time
imp_ff = signal.filtfilt(b, a, impulse)
# Applies filter forward in time twice (for same frequency response)
imp_lf = signal.lfilter(b, a, signal.lfilter(b, a, impulse))
plt.subplot(2, 2, 1)
plt.semilogx(20*np.log10(np.abs(rfft(imp_lf))))
plt.ylim(-100, 20)
plt.grid(True, which='both')
plt.title('lfilter')
plt.subplot(2, 2, 2)
plt.semilogx(20*np.log10(np.abs(rfft(imp_ff))))
plt.ylim(-100, 20)
plt.grid(True, which='both')
plt.title('filtfilt')
sig = np.cumsum(randn(800)) # Brownian noise
sig_ff = signal.filtfilt(b, a, sig)
sig_lf = signal.lfilter(b, a, signal.lfilter(b, a, sig))
plt.subplot(2, 1, 2)
plt.plot(sig, color='silver', label='Original')
plt.plot(sig_ff, color='#3465a4', label='filtfilt')
plt.plot(sig_lf, color='#cc0000', label='lfilter')
plt.grid(True, which='both')
plt.legend(loc="best")