2010-11-23 117 views
13

我有周期T的周期函數,並想知道如何獲得傅里葉係數的列表。我嘗試使用numpy的fft模塊,但它似乎比系列更專注於傅立葉變換。 也許它缺乏數學知識,但我看不出如何從fft計算傅立葉係數。如何計算Numpy中的傅立葉級數?

幫助和/或示例讚賞。

回答

15

最後,最簡單的事情(計算與黎曼和係數)是最便攜/高效/健壯解決我的問題的方式:

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

+0

感謝您發佈此解決方案。它節省了我一些時間:) – zega 2013-06-28 15:44:59

3

你有你的函數的離散樣本列表,或者你的函數本身是離散的嗎?如果是這樣,則使用FFT算法計算的離散傅立葉變換直接提供傅立葉係數(see here)。另一方面,如果你有一個函數的解析表達式,你可能需要某種符號數學解算器。

10

Numpy不是真正計算fourier系列組件的正確工具,因爲您的數據必須進行離散採樣。你真的想使用像Mathematica或應該使用傅立葉變換。爲了粗略地做到這一點,讓我們看一下簡單的週期爲2pi的三角波,在那裏我們可以很容易地計算n的傅里葉係數(c_n = -i((-1)^(n + 1))/ n > 0;例如,當n = 1,2,3,4,5時,c_n = {-i,i/2,-i/3,i/4,-i/5,i/6,...} 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的時間段之前產生的)也應該被認爲是零,因爲函數是週期性的)。因此,如果我們忽略了這兩個因素,我們得到:

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或能夠集成和處理連續函數的計算機代數系統。

+1

優秀的,優秀的點,不得不付出努力瞭解結果。 +1。 – mtrw 2010-11-23 17:29:42

4

正如其他答案已經提到,看起來你正在尋找的是一個符號計算包,所以numpy不適合。如果您希望使用免費的基於python的解決方案,那麼sympysage應該可以滿足您的需求。

+2

這裏是使用sympy的fourier系列的參考:http://docs.sympy.org/dev/modules/mpmath/calculus/approximation.html?highlight=fourier。它需要mpmath甚至不在我的sympy發行版中。雖然是一個很好的提示,但爲了代碼的可移植性,我不會選擇這個解決方案。 – Mermoz 2010-11-24 10:35:10

4

這是一個老問題,但由於我必須對此進行編碼,因此我在此處發佈使用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)