0

pythonで時系列をsin波で近似できるプログラムを書いています。プログラムは、DFT を使用して正弦波を見つけ、その後、振幅が最大の正弦波を選択します。

これが私のコードです:

__author__ = 'FATVVS'

import math


# Wave - (amplitude,frequency,phase)
# This class was created to sort sin waves:
# - by anplitude( set freq_sort=False)
# - by frequency (set freq_sort=True)
class Wave:
    #flag for choosing sort mode:
    # False-sort by amplitude
    # True-by frequency
    freq_sort = False

    def __init__(self, amp, freq, phase):
        self.freq = freq #frequency
        self.amp = amp #amplitude
        self.phase = phase

    def __lt__(self, other):
        if self.freq_sort:
            return self.freq < other.freq
        else:
            return self.amp < other.amp

    def __gt__(self, other):
        if self.freq_sort:
            return self.freq > other.freq
        else:
            return self.amp > other.amp

    def __le__(self, other):
        if self.freq_sort:
            return self.freq <= other.freq
        else:
            return self.amp <= other.amp

    def __ge__(self, other):
        if self.freq_sort:
            return self.freq >= other.freq
        else:
            return self.amp >= other.amp

    def __str__(self):
        s = "(amp=" + str(self.amp) + ",frq=" + str(self.freq) + ",phase=" + str(self.phase) + ")"
        return s

    def __repr__(self):
        return self.__str__()


#Discrete Fourier Transform
def dft(series: list):
    n = len(series)
    m = int(n / 2)
    real = [0 for _ in range(n)]
    imag = [0 for _ in range(n)]
    amplitude = []
    phase = []
    angle_const = 2 * math.pi / n
    for w in range(m):
        a = w * angle_const
        for t in range(n):
            real[w] += series[t] * math.cos(a * t)
            imag[w] += series[t] * math.sin(a * t)
        amplitude.append(math.sqrt(real[w] * real[w] + imag[w] * imag[w]) / n)
        phase.append(math.atan(imag[w] / real[w]))
    return amplitude, phase


#extract waves from time series
# series - time series
# num - number of waves
def get_waves(series: list, num):
    amp, phase = dft(series)
    m = len(amp)
    waves = []
    for i in range(m):
        waves.append(Wave(amp[i], 2 * math.pi * i / m, phase[i]))
    waves.sort()
    waves.reverse()
    waves = waves[0:num]#extract best waves
    print("the program found the next %s sin waves:"%(num))
    print(waves)#print best waves
    return waves

#approximation by sin waves
#series - time series
#num- number of sin waves
def sin_waves_appr(series: list, num):
    n = len(series)
    freq = get_waves(series, num)
    m = len(freq)
    model = []
    for i in range(n):
        summ = 0
        for j in range(m): #sum by sin waves
            summ += freq[j].amp * math.sin(freq[j].freq * i + freq[j].phase)
        model.append(summ)
    return model


if __name__ == '__main__':
    import matplotlib.pyplot as plt

    N = 500  # length of time series
    num = 2  # number of sin wawes, that we want to find

    #y - generate time series
    y = [2 * math.sin(0.05 * t + 0.5) + 0.5 * math.sin(0.2 * t + 1.5) for t in range(N)]
    model = sin_waves_appr(y, num) #generate approximation model

    ## ------------------plotting-----------------

    plt.figure(1)

    # plotting of time series and his approximation model
    plt.subplot(211)
    h_signal, = plt.plot(y, label='source timeseries')
    h_model, = plt.plot(model, label='model', linestyle='--')
    plt.legend(handles=[h_signal, h_model])
    plt.grid()

    # plotting of spectre
    amp, _ = dft(y)
    xaxis = [2*math.pi*i / N for i in range(len(amp))]
    plt.subplot(212)
    h_freq, = plt.plot(xaxis, amp, label='spectre')
    plt.legend(handles=[h_freq])
    plt.grid()

    plt.show()

しかし、私は奇妙な結果を得ました: ここに画像の説明を入力

プログラムでは、2 つの正弦波から時系列を作成しました。

y = [2 * math.sin(0.05 * t + 0.5) + 0.5 * math.sin(0.2 * t + 1.5) for t in range(N)]

そして、私のプログラムは正弦波の間違ったパラメータを見つけました:

プログラムは次の 2 つの正弦波を見つけました: [(amp=0.9998029885151699,frq=0.10053096491487339,phase=1.1411803525843616), (amp=0.24800925225626422,frq=0.402123859659493524,phase=50)183]4

私の問題は、波のパラメーターのスケーリングが間違っていることだと思いますが、よくわかりません。プログラムがスケーリングを行う場所は 2 つあります。最初の場所は波の作成です:

for i in range(m):
    waves.append(Wave(amp[i], 2 * math.pi * i / m, phase[i]))

2 番目は x 軸のスケーリングです。

xaxis = [2*math.pi*i / N for i in range(len(amp))]

しかし、私の推測は間違っているかもしれません。スケーリングを何度も変更しようとしましたが、問題は解決しませんでした。

コードのどこが間違っている可能性がありますか?

4

1 に答える 1

1

したがって、私が信じているこれらの行は間違っています。

for t in range(n):
    real[w] += series[t] * math.cos(a * t)
    imag[w] += series[t] * math.sin(a * t)
amplitude.append(math.sqrt(real[w] * real[w] + imag[w] * imag[w]) / n)
phase.append(math.atan(imag[w] / real[w]))

ポイントの半分しか計算していないので、nではなくmで割るべきだと思います。これにより、振幅の問題が解決されます。また、 の計算にimag[w]負の符号がありません。atan2 の修正を考慮すると、次のようになります。

for t in range(n):
    real[w] += series[t] * math.cos(a * t)
    imag[w] += -1 * series[t] * math.sin(a * t)
amplitude.append(math.sqrt(real[w] * real[w] + imag[w] * imag[w]) / m)
phase.append(math.atan2(imag[w], real[w]))

次のものはここにあります:

for i in range(m):
    waves.append(Wave(amp[i], 2 * math.pi * i / m, phase[i]))

割り算mは正しくありません。 amp必要なポイントの半分しかないため、アンプの長さを使用するのは適切ではありません。そのはず:

for i in range(m):
    waves.append(Wave(amp[i], 2 * math.pi * i / (m * 2), phase[i]))

最後に、モデルの再構築には問題があります。

    for j in range(m): #sum by sin waves
        summ += freq[j].amp * math.sin(freq[j].freq * i + freq[j].phase)

代わりに余弦を使用する必要があります (正弦は位相シフトを導入します)。

    for j in range(m): #sum by cos waves
        summ += freq[j].amp * math.cos(freq[j].freq * i + freq[j].phase)

そのすべてを修正すると、モデルと DFT の両方が意味を成します。

スペクトル モデルと時間モデルの図

于 2015-11-16T11:01:27.533 に答える