web-dev-qa-db-ja.com

Numpyでフーリエ級数を計算する方法は?

周期Tの周期関数があり、フーリエ係数のリストを取得する方法を知りたいです。 numpyの fft モジュールを使用してみましたが、シリーズよりもフーリエ変換に特化しているようです。多分それは数学的知識の欠如ですが、私はfftからフーリエ係数を計算する方法を見ることができません。

ヘルプおよび/または例をいただければ幸いです。

21
Mermoz

結局、最も簡単なこと(リーマン和で係数を計算すること)は、私の問題を解決するための最も移植性があり、効率的で、堅牢な方法でした:

def cn(n):
   c = y*np.exp(-1j*2*n*np.pi*time/period)
   return c.sum()/c.size

def f(x, Nh):
   f = np.array([2*cn(i)*np.exp(1j*2*i*np.pi*x/period) for i in range(1,Nh+1)])
   return f.sum()

y2 = np.array([f(t,50).real for t in time])

plot(time, y)
plot(time, y2)

私に与える: alt text

20
Mermoz

これは古い質問ですが、これをコーディングする必要があったため、numpy.fftモジュールを使用するソリューションをここに投稿します。これは、他の手作りのソリューションよりも高速である可能性があります。

[〜#〜] dft [〜#〜] は、関数のフーリエ級数の係数を数値精度まで計算するための適切なツールであり、引数の分析式として定義されますまたは、いくつかの離散点に対する数値補間関数として。

これは、適切なreturn_complexを渡すことにより、フーリエ級数の実数値係数または複素数値係数を計算できる実装です。

def fourier_series_coeff_numpy(f, T, N, return_complex=False):
    """Calculates the first 2*N+1 Fourier series coeff. of a periodic function.

    Given a periodic, function f(t) with period T, this function returns the
    coefficients a0, {a1,a2,...},{b1,b2,...} such that:

    f(t) ~= a0/2+ sum_{k=1}^{N} ( a_k*cos(2*pi*k*t/T) + b_k*sin(2*pi*k*t/T) )

    If return_complex is set to True, it returns instead the coefficients
    {c0,c1,c2,...}
    such that:

    f(t) ~= sum_{k=-N}^{N} c_k * exp(i*2*pi*k*t/T)

    where we define c_{-n} = complex_conjugate(c_{n})

    Refer to wikipedia for the relation between the real-valued and complex
    valued coeffs at http://en.wikipedia.org/wiki/Fourier_series.

    Parameters
    ----------
    f : the periodic function, a callable like f(t)
    T : the period of the function f, so that f(0)==f(T)
    N_max : the function will return the first N_max + 1 Fourier coeff.

    Returns
    -------
    if return_complex == False, the function returns:

    a0 : float
    a,b : numpy float arrays describing respectively the cosine and sine coeff.

    if return_complex == True, the function returns:

    c : numpy 1-dimensional complex-valued array of size N+1

    """
    # From Shanon theoreom we must use a sampling freq. larger than the maximum
    # frequency you want to catch in the signal.
    f_sample = 2 * N
    # we also need to use an integer sampling frequency, or the
    # points will not be equispaced between 0 and 1. We then add +2 to f_sample
    t, dt = np.linspace(0, T, f_sample + 2, endpoint=False, retstep=True)

    y = np.fft.rfft(f(t)) / t.size

    if return_complex:
        return y
    else:
        y *= 2
        return y[0].real, y[1:-1].real, -y[1:-1].imag

これは使用例です:

from numpy import ones_like, cos, pi, sin, allclose
T = 1.5  # any real number

def f(t):
    """example of periodic function in [0,T]"""
    n1, n2, n3 = 1., 4., 7.  # in Hz, or nondimensional for the matter.
    a0, a1, b4, a7 = 4., 2., -1., -3
    return a0 / 2 * ones_like(t) + a1 * cos(2 * pi * n1 * t / T) + b4 * sin(
        2 * pi * n2 * t / T) + a7 * cos(2 * pi * n3 * t / T)


N_chosen = 10
a0, a, b = fourier_series_coeff_numpy(f, T, N_chosen)

# we have as expected that
assert allclose(a0, 4)
assert allclose(a, [2, 0, 0, 0, 0, 0, -3, 0, 0, 0])
assert allclose(b, [0, 0, 0, -1, 0, 0, 0, 0, 0, 0])

そして、結果のa0,a1,...,a10,b1,b2,...,b10係数のプロット: enter image description here

これは、両方の動作モードの関数のオプションのテストです。コードを実行する前に、例の後にこれを実行するか、定期的な関数fとピリオドTを定義する必要があります。

# #### test that it works with real coefficients:
from numpy import linspace, allclose, cos, sin, ones_like, exp, pi, \
    complex64, zeros


def series_real_coeff(a0, a, b, t, T):
    """calculates the Fourier series with period T at times t,
       from the real coeff. a0,a,b"""
    tmp = ones_like(t) * a0 / 2.
    for k, (ak, bk) in enumerate(Zip(a, b)):
        tmp += ak * cos(2 * pi * (k + 1) * t / T) + bk * sin(
            2 * pi * (k + 1) * t / T)
    return tmp


t = linspace(0, T, 100)
f_values = f(t)
a0, a, b = fourier_series_coeff_numpy(f, T, 52)
# construct the series:
f_series_values = series_real_coeff(a0, a, b, t, T)
# check that the series and the original function match to numerical precision:
assert allclose(f_series_values, f_values, atol=1e-6)

# #### test similarly that it works with complex coefficients:

def series_complex_coeff(c, t, T):
    """calculates the Fourier series with period T at times t,
       from the complex coeff. c"""
    tmp = zeros((t.size), dtype=complex64)
    for k, ck in enumerate(c):
        # sum from 0 to +N
        tmp += ck * exp(2j * pi * k * t / T)
        # sum from -N to -1
        if k != 0:
            tmp += ck.conjugate() * exp(-2j * pi * k * t / T)
    return tmp.real

f_values = f(t)
c = fourier_series_coeff_numpy(f, T, 7, return_complex=True)
f_series_values = series_complex_coeff(c, t, T)
assert allclose(f_series_values, f_values, atol=1e-6)
13
gg349

データは離散的にサンプリングされる必要があるため、Numpyは実際にはフーリエ級数成分を計算するための適切なツールではありません。あなたは本当にMathematicaのようなものを使いたい、またはフーリエ変換を使うべきです。

おおまかにそれを行うには、2piの単純な三角波を見てみましょう。ここでは、フーリエ係数(c_n = -i((-1)^(n + 1))/ n for n> 0を簡単に計算できます。たとえば、 、c_n = {-i、i/2、-i/3、i/4、-i/5、i/6、...}の場合n = 1、2、3、4、5、6(合計を使用) (c_n exp(i 2 pi nx))フーリエ級数として)。

import numpy
x = numpy.arange(0,2*numpy.pi, numpy.pi/1000)
y = (x+numpy.pi/2) % numpy.pi - numpy.pi/2
fourier_trans = numpy.fft.rfft(y)/1000

最初のいくつかのフーリエ成分を見ると:

array([ -3.14159265e-03 +0.00000000e+00j,
         2.54994550e-16 -1.49956612e-16j,
         3.14159265e-03 -9.99996710e-01j,
         1.28143395e-16 +2.05163971e-16j,
        -3.14159265e-03 +4.99993420e-01j,
         5.28320925e-17 -2.74568926e-17j,
         3.14159265e-03 -3.33323464e-01j,
         7.73558750e-17 -3.41761974e-16j,
        -3.14159265e-03 +2.49986840e-01j,
         1.73758496e-16 +1.55882418e-17j,
         3.14159265e-03 -1.99983550e-01j,
        -1.74044469e-16 -1.22437710e-17j,
        -3.14159265e-03 +1.66646927e-01j,
        -1.02291982e-16 -2.05092972e-16j,
         3.14159265e-03 -1.42834113e-01j,
         1.96729377e-17 +5.35550532e-17j,
        -3.14159265e-03 +1.24973680e-01j,
        -7.50516717e-17 +3.33475329e-17j,
         3.14159265e-03 -1.11081501e-01j,
        -1.27900121e-16 -3.32193126e-17j,
        -3.14159265e-03 +9.99670992e-02j,

最初に、浮動小数点の精度のために0に近いコンポーネント(〜1e-16、ゼロである)を無視します。さらに難しいのは、関数が周期的であるため、3.14159の数値(1000の周期で除算する前に発生した)もゼロとして認識されることを確認することです。したがって、これらの2つの要素を無視すると、次のようになります。

fourier_trans = [0,0,-i,0,i/2,0,-i/3,0,i/4,0,-i/5,0,-i/6, ...

そして、フーリエ級数が他のすべての数字と同じように表示されることを確認できます(調査していません。ただし、コンポーネントは[c0、c-1、c1、c-2、c2、...]に対応していると思います)。私はwikiに従って規約を使用しています: http://en.wikipedia.org/wiki/Fourier_series

繰り返しますが、mathematicaまたは連続関数を統合して処理できるコンピュータ代数システムを使用することをお勧めします。

11
dr jimbob

他の回答が述べているように、あなたが探しているのはシンボリックコンピューティングパッケージであるようですので、numpyは適切ではありません。無料のpythonベースのソリューションを使用したい場合は、 sympy または sage のいずれかでニーズを満たします。

5
DaveP

関数の離散サンプルのリストがありますか、それとも関数自体が離散ですか?その場合、FFTアルゴリズムを使用して計算された離散フーリエ変換は、フーリエ係数を直接提供します( ここを参照 )。

一方、関数の分析式がある場合、おそらく何らかのシンボリック数学ソルバーが必要です。

4
mtrw