- 技術 💻
フーリエ変換とは|フーリエ級数展開・DFT・FFTの違いをPythonで実測
結論
フーリエ変換とは、時間とともに変わる信号を「どの周波数の波が、どれだけの大きさで含まれているか」に書き換える計算です。周期のある関数を波の足し算で表すのがフーリエ級数展開、コンピューターで扱う有限個のデータに使うのが離散フーリエ変換(DFT)、それを速く計算する方法が FFT です。
この記事でわかること
- フーリエ級数・フーリエ変換・DFT・FFTの違いと式の読み方
- 矩形波の近似誤差とギブズ現象(約9%)を項数ごとに計算した表
- DFTとFFTの速さの差、雑音の中の440Hz検出、窓関数・エイリアシングの実測
フーリエ変換は「時間の世界(いつ、どれだけ)」から「周波数の世界(何Hzが、どれだけ)」へ見方を変える道具です。4つの言葉の関係を先に図にします。
flowchart TD
A[周期のある関数] -->|波の足し算で表す| B[フーリエ級数展開]
B -->|周期を無限に長くする| C[フーリエ変換]
C -->|有限個のサンプルにする| D[離散フーリエ変換 DFT]
D -->|計算を速くする方法| E[FFT 高速フーリエ変換]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| フーリエ変換とは | 信号を周波数ごとの成分(大きさと位相)に分ける計算 | フーリエ変換とは |
| フーリエ級数展開とは | 周期関数を sin・cos の足し算で表すこと | フーリエ級数展開とは |
| フーリエ級数と変換の違い | 級数は周期関数・とびとびの周波数、変換は周期のない関数・連続した周波数 | 違い(一覧表) |
| FFTとは | DFTと同じ結果を、N² ではなく N log N に比例する手間で求める方法 | FFTとは |
| PythonでFFTするには | np.fft.rfft と rfftfreq。振幅は 2/N 倍に直す |
PythonでFFTする手順 |
| 窓関数は要るか | 周波数がビンの間にあるとき要る。手元では漏れが -26dB から -67dB に減った | 窓関数と周波数分解能 |
| サンプリング定理とは | サンプリング周波数の半分より高い成分は、別の周波数に化ける | サンプリング定理とエイリアシング |
| ラプラス変換との違い | ラプラス変換は減衰の項を足して、増えていく関数や過渡応答も扱える | ラプラス変換との違い |
確認環境:Ubuntu 24.04.5 LTS(Intel Xeon 2.10GHz・2コア)の上で、作業用フォルダーに作った仮想環境(venv)に Python 3.13.18、NumPy 2.5.4、SciPy 1.18.1 を入れ、この記事のすべての数値を自分で計算しました。フーリエ係数は scipy.integrate.quad の数値積分、DFT は定義どおりの自作関数と numpy.fft・scipy.fft の比較、速さは timeit の最小値です(時間は計算機によって変わります)。正規化・出力の並び・fftpack と stft の扱いは NumPy 2.5 と SciPy 1.18.0 の公式ドキュメントで確かめました。グラフの画像は使わず、すべて表で示しています。図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「フーリエ変換とは|フーリエ級数展開・DFT・FFTの違いをPythonで実測」
フーリエ変換とは(ひとことで・身近な例)
この節では、フーリエ変換が何をする計算なのかが、式を使わずに分かります。
フーリエ変換は、混ざった音を聞いて「ドとミとソが鳴っている」と言い当てる作業を、計算で行うものです。 マイクで録った音は、時間ごとの空気の圧力の値が並んだだけのデータです。フーリエ変換をすると、それが「440Hz が大きさ1、1000Hz が大きさ0.5」のような周波数ごとの成分の表に変わります。
土台にある考え方は、フランスの数学者ジョゼフ・フーリエが熱の伝わり方の研究で示した「どんな形の波も、いろいろな周波数の sin・cos の足し算で表せる」というものです。正確には条件がありますが、工学で扱うほとんどの信号はこの考え方で分解できます。
音
録音から音の高さ(周波数)を当てる。チューナー・音声認識・イコライザー。
この記事で440Hzを検出
画像
縞模様の細かさ(空間周波数)を調べる。JPEG の圧縮は親戚の DCT を使う。
2次元FFTの例あり
振動・機械
モーターや建物の揺れに含まれる周波数を見て、異常の兆しを探す。
振動解析
通信・計算
無線の変調(OFDM)や、大きな畳み込み・多倍長の掛け算を速く計算する。
畳み込みで約70倍速
時間の世界と周波数の世界
同じ信号を2つの見方で書くと、次のようになります。手元で作った「440Hz(大きさ1)+1000Hz(大きさ0.5)+雑音」の信号です。
時間の世界(もとのデータ)
0.4525, -0.259, 1.8655, 2.0346, -1.1165 …
8,000個の数が並ぶだけで、何Hzが入っているかは見えない
雑音のほうが信号より大きい(SN比 -2.06dB)
周波数の世界(FFTのあと)
440Hz:0.9912、1000Hz:0.5137
ほかの周波数はどれも 0.07 以下
入れた2つの音が、ひと目で分かる
フーリエ級数・フーリエ変換・DFT・FFTの違い(一覧表)
この節では、名前が似た4つの言葉が、それぞれ何を相手にして何を出すのかが一覧で分かります。
入力が「周期のある関数」「周期のない関数」「有限個のサンプル」のどれかで、使う道具が決まります。 FFT は新しい変換ではなく、DFT の計算方法の名前です。
| 名前 | 入力 | 出力 | 式の形(要点) |
|---|---|---|---|
| フーリエ級数展開 | 周期 T の関数(連続) | とびとびの周波数 n/T ごとの係数 | 係数を積分で求め、sin・cos を足す |
| フーリエ変換 | 周期のない関数(連続) | 連続した周波数の関数 F(f) | −∞ から ∞ までの積分 |
| 離散フーリエ変換(DFT) | N 個のサンプル | N 個の周波数成分 | N 個の足し算を N 回 |
| 高速フーリエ変換(FFT) | N 個のサンプル | DFT と同じ N 個 | 分割して使い回し、手間は N log N 程度 |
| 逆変換(IDFT・IFFT など) | 周波数成分 | もとの信号 | 指数の符号が逆、1/N などで割る |
- 級数は「足し算(Σ)」、変換は「積分(∫)」です。周期を無限に長くすると、とびとびだった周波数の間隔が0に近づき、足し算が積分に変わります。これがフーリエ級数からフーリエ変換への道筋です。
- コンピューターは無限に長いデータも、連続した値も持てないため、実際の計算はすべて DFT(を FFT で計算したもの)です。
- FFT の考え方はガウスが1805年ごろに知っていたもので、今の形は1965年のクーリー(Cooley)とテューキー(Tukey)の論文で広まりました(NumPy の公式ドキュメントの説明)。
フーリエ級数展開とは(係数の求め方を一歩ずつ)
この節では、フーリエ級数展開の式の意味と、係数を自分で求める手順が分かります。
フーリエ級数展開は、周期 2π の関数 f(x) を、次の形で表すことです。
f(x) = a0/2 + Σ(n=1〜∞) { an·cos(nx) + bn·sin(nx) }
an と bn を「フーリエ係数」と呼びます。求め方は次の積分です。
an = (1/π) ∫[−π→π] f(x)·cos(nx) dx(n = 0, 1, 2, …)bn = (1/π) ∫[−π→π] f(x)·sin(nx) dx(n = 1, 2, 3, …)
この積分は「f(x) と cos(nx) がどれだけ似ているか」を測る計算です。違う n どうしの sin・cos を掛けて1周期積分すると0になる(直交する)ため、ほかの成分が混ざらずに、その周波数の成分だけが取り出せます。ベクトルの内積で成分を取り出すのと同じ考え方で、関連記事「内積・外積とは|公式と計算例・使い道をNumPyで実演」で扱っています。
例:矩形波(−1 と 1 を行き来する波)の係数
f(x) を「−π〜0 で −1、0〜π で 1」の矩形波として、手で計算します。
an を見る
矩形波は原点について点対称(奇関数)で、cos(nx) は偶関数です。奇関数×偶関数は奇関数なので、−π〜π で積分すると0。すべての an が0になります。
bn の積分を分ける
bn = (1/π){ ∫[−π→0] (−1)·sin(nx)dx + ∫[0→π] sin(nx)dx }。左右は同じ値になるので bn = (2/π)∫[0→π] sin(nx)dx。
積分する
∫[0→π] sin(nx)dx = (1 − cos(nπ))/n。cos(nπ) は n が奇数なら −1、偶数なら 1。
まとめる
n が奇数なら bn = 4/(nπ)、偶数なら 0。よって f(x) = (4/π){ sin x + sin(3x)/3 + sin(5x)/5 + … }。
手計算の答えを、SciPy の数値積分で確かめました。
import numpy as np
from scipy import integrate
sq = lambda x: np.sign(np.sin(x)) # 矩形波
for n in range(1, 8):
a, _ = integrate.quad(lambda x: sq(x) * np.cos(n * x), -np.pi, np.pi, points=[0])
b, _ = integrate.quad(lambda x: sq(x) * np.sin(n * x), -np.pi, np.pi, points=[0])
print(n, round(a / np.pi, 6), round(b / np.pi, 6))
| n | an(数値積分) | bn(数値積分) | 手計算 4/(nπ) |
|---|---|---|---|
| 1 | 0.000000 | 1.273240 | 1.273240 |
| 2 | 0.000000 | 0.000000 | 0 |
| 3 | 0.000000 | 0.424413 | 0.424413 |
| 4 | 0.000000 | 0.000000 | 0 |
| 5 | 0.000000 | 0.254648 | 0.254648 |
| 6 | 0.000000 | 0.000000 | 0 |
| 7 | 0.000000 | 0.181891 | 0.181891 |
6桁まで一致しました。もう1つ、よく例題に出る f(x) = x²(−π〜π)も確かめると、a0 = 6.579736(= 2π²/3)、a1 = −4、a2 = 1、a3 = −0.444444、a4 = 0.25 となり、公式 an = 4(−1)^n / n² と一致しました。
複素数の形(複素フーリエ級数)
オイラーの公式 e^(iθ) = cosθ + i·sinθ を使うと、sin と cos を1つにまとめられます。
f(x) = Σ(n=−∞〜∞) cn·e^(inx)、cn = (1/2π) ∫[−π→π] f(x)·e^(−inx) dx
cn の大きさ |cn| がその周波数の強さ、偏角が位相(波の始まりのずれ)です。DFT やフーリエ変換は、この複素数の形を土台にしています。
矩形波のフーリエ級数展開とギブズ現象(項数ごとの誤差表)
この節では、項を増やすと近似がどう良くなるのか、そして角の近くに残る「ギブズ現象」の大きさが数値で分かります。
項を増やすほど全体の誤差は小さくなりますが、跳びのすぐ横の飛び出し(オーバーシュート)は約9%から減りません。 これがギブズ現象です。−π〜π を40万等分した点で、部分和(奇数の項を K 個足したもの)を計算しました。
x = np.linspace(-np.pi, np.pi, 400001)
f = np.sign(np.sin(x))
def S(K, x): # 奇数の項を K 個足した部分和
s = np.zeros_like(x)
for k in range(1, K + 1):
n = 2 * k - 1
s += 4 / np.pi * np.sin(n * x) / n
return s
for K in [1, 2, 3, 5, 10, 50, 100, 1000]:
s = S(K, x)
print(K, s.max(), np.sqrt(np.mean((s - f) ** 2)))
| 足した項(最大の n) | 最大値 | 飛び出し(跳び2に対する%) | RMS誤差 | x=π/2 の値(正解1) |
|---|---|---|---|---|
| 1項(n=1) | 1.27324 | 13.662% | 0.43524 | 1.27324 |
| 2項(n=3) | 1.20042 | 10.021% | 0.31523 | 0.84883 |
| 3項(n=5) | 1.18836 | 9.418% | 0.25874 | 1.10347 |
| 5項(n=9) | 1.18233 | 9.116% | 0.20099 | 1.06305 |
| 10項(n=19) | 1.17981 | 8.991% | 0.14230 | 0.96825 |
| 50項(n=99) | 1.17901 | 8.951% | 0.06368 | 0.99363 |
| 100項(n=199) | 1.17899 | 8.949% | 0.04504 | 0.99682 |
| 1000項(n=1999) | 1.17898 | 8.949% | 0.01432 | 0.99968 |
- 山の高さは1.179付近で止まり、山の位置だけが跳び(x=0)に近づきます。 山の位置は 10項で 0.15708(=π/20)、100項で 0.01571(=π/200)でした。項を増やすと「飛び出す幅」は細くなりますが、高さは変わりません。
- 理論の極限値は
(2/π)·Si(π)(Si は sin(t)/t の積分)で、SciPy で計算すると 1.1789797 でした。表の 1.17898 と一致します。
なめらかな波は早く収束する(三角波との比較)
跳びの無い三角波(係数が 1/n² で小さくなる)では、最大誤差そのものが減っていきました。
| 足した項 | 三角波の最大誤差 | 矩形波のRMS誤差 |
|---|---|---|
| 1項 | 0.18943 | 0.43524 |
| 3項 | 0.06694 | 0.25874 |
| 10項 | 0.02025 | 0.14230 |
| 100項 | 0.00203 | 0.04504 |
波に跳び(不連続)があると係数は 1/n でしか小さくならず、ギブズ現象が出ます。 跳びが無く角だけある三角波は 1/n² で小さくなり、最大誤差は項を10倍にすると約10分の1になりました。音声や画像の処理で、急な変化の横に波打つような模様(リンギング)が出るのは、同じ理由です。
フーリエ変換の定義と代表的な変換(矩形→sinc・ガウス)
この節では、フーリエ変換の式の読み方と、2πの付け方の流儀の違い、代表的な関数の変換結果が分かります。
周期の無い関数 x(t) のフーリエ変換は、周波数 f を使うと次の式です。
- フーリエ変換:
X(f) = ∫[−∞→∞] x(t)·e^(−i2πft) dt - 逆変換:
x(t) = ∫[−∞→∞] X(f)·e^(i2πft) df
e^(−i2πft) は「周波数 f で回る矢印」です。x(t) に掛けて全部の時間で足すと、x(t) の中にその周波数の成分がどれだけあるかが残ります。
2πの付け方は本によって違う
| 流儀 | 変換 | 逆変換 | よく使う分野 |
|---|---|---|---|
| 周波数 f(Hz) | ∫x(t)e^(−i2πft)dt |
∫X(f)e^(i2πft)df |
信号処理・NumPy の考え方 |
| 角周波数 ω | ∫x(t)e^(−iωt)dt |
(1/2π)∫X(ω)e^(iωt)dω |
電気・制御の教科書 |
| 角周波数 ω(対称) | (1/√2π)∫… |
(1/√2π)∫… |
数学・物理 |
どれも正しく、係数の置き場所が違うだけです。教科書や論文の式を写すときは、どの流儀かを先に確かめます。 試験の答えが係数1つだけ合わないときは、たいていこの違いです。
矩形パルスは sinc に、ガウスはガウスになる
幅1の矩形パルス(|t|<0.5 で 1)と、ガウス関数 e^(−πt²) を、数値積分でフーリエ変換しました。
| f | 矩形(数値積分) | sinc(f) の式 | ガウス(数値積分) | e^(−πf²) の式 |
|---|---|---|---|---|
| 0 | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
| 0.25 | 0.900316 | 0.900316 | 0.821725 | 0.821725 |
| 0.5 | 0.636620 | 0.636620 | 0.455938 | 0.455938 |
| 1 | 0.000000 | 0.000000 | 0.043214 | 0.043214 |
| 1.5 | −0.212207 | −0.212207 | 0.000851 | 0.000851 |
| 2.5 | 0.127324 | 0.127324 | 0.000000 | 0.000000 |
- 矩形パルスの変換は
sinc(f) = sin(πf)/(πf)で、f = 1, 2, … で0になり、符号を変えながらゆっくり小さくなります。角のある信号ほど高い周波数まで成分が広がる、という前の節の話と同じです。 - ガウス関数
e^(−πt²)は、変換しても同じ形e^(−πf²)になりました。なめらかな関数は、高い周波数の成分がすぐに0に近づきます。 - 虚部(sin の積分)はどちらも0でした。左右対称(偶関数)の実数の関数は、変換も実数になります。
DFT はフーリエ変換の近似になる
ガウス関数を 0.01 刻みで −10〜10 のサンプルにし、FFT の結果に刻み幅 0.01 を掛けると、式 e^(−πf²) との差は最大 3.24×10⁻¹³ でした。サンプルの刻みを掛ければ、FFT で連続のフーリエ変換を計算できます。 このとき、時間0が配列の先頭に来るよう np.fft.ifftshift で並べ替えてから FFT しています。
離散フーリエ変換(DFT)を定義どおり計算する
この節では、DFT の式を一歩ずつ手で計算する方法と、自作の DFT が NumPy の結果と一致することが分かります。
N 個のサンプル x[0]〜x[N−1] の DFT は次の式です(NumPy の公式ドキュメントと同じ定義)。
X[k] = Σ(n=0〜N−1) x[n]·e^(−i2πkn/N) (k = 0, 1, …, N−1)
逆変換は x[n] = (1/N) Σ(k=0〜N−1) X[k]·e^(i2πkn/N) です。NumPy の説明どおり、順方向と比べて指数の符号が逆で、1/N で割ります。
N=4 を手で計算する(x = [1, 2, 3, 4])
回転の単位を決める
N=4 なので W = e^(−i2π/4) = −i。W の累乗は 1, −i, −1, i を繰り返します。
X[0](周波数0=直流)
すべて1を掛けて足す:1+2+3+4 = 10。X[0] は合計で、N で割ると平均(2.5)です。
X[1]
1·1 + 2·(−i) + 3·(−1) + 4·(i) = (1−3) + (−2+4)i = −2+2i。
X[2]
1·1 + 2·(−1) + 3·1 + 4·(−1) = −2。
X[3]
1·1 + 2·(i) + 3·(−1) + 4·(−i) = −2−2i。X[1] の複素共役です。
自作の関数と NumPy で同じ計算をしました。
import numpy as np
def dft(x):
N = len(x)
X = []
for k in range(N):
s = 0j
for n in range(N):
s += x[n] * np.exp(-2j * np.pi * k * n / N)
X.append(s)
return np.array(X)
x = np.array([1., 2., 3., 4.])
print(dft(x))
print(np.fft.fft(x))
print(np.fft.ifft(np.fft.fft(x)))
[10.+0.j -2.+2.j -2.-0.j -2.-2.j]
[10.+0.j -2.+2.j -2.+0.j -2.-2.j]
[1.+0.j 2.+0.j 3.+0.j 4.+0.j]
手計算と同じ [10, −2+2i, −2, −2−2i] になり、逆変換で [1, 2, 3, 4] に戻りました。−0.j は、ごく小さな負の誤差が付いたという表示で、値は0です。乱数のデータでも比べました。
| N | 自作(二重ループ)と np.fft の最大の差 | 自作(行列の掛け算)と np.fft の最大の差 |
|---|---|---|
| 8 | 3.71×10⁻¹⁵ | 3.78×10⁻¹⁵ |
| 64 | 2.76×10⁻¹³ | 1.93×10⁻¹³ |
| 1000 | 3.13×10⁻¹¹ | 1.72×10⁻¹¹ |
差は浮動小数点の丸め誤差だけで、FFT は DFT と同じ値を出す計算方法だと確かめられます。N が大きいほど差が増えるのは、自作の DFT が足し算の回数が多く、丸め誤差がたまるためです。
実数の入力では後ろ半分が前半の鏡になる
x = [1, 2, …, 8] の FFT は [36, −4+9.6569i, −4+4i, −4+1.6569i, −4, −4−1.6569i, −4−4i, −4−9.6569i] でした。X[k] と X[8−k] が複素共役になっています。np.fft.rfft はこの性質を使い、前半の N/2+1 個(ここでは5個)だけを返します。
パーセバルの等式(エネルギーは変わらない)
乱数1000個で、Σ|x[n]|² と (1/N)Σ|X[k]|² を計算すると、どちらも 1041.9423934282659 で一致しました。時間の世界で測っても、周波数の世界で測っても、信号の強さ(エネルギー)は同じです。
FFTとは(高速フーリエ変換の仕組みと速さの実測)
この節では、FFT がなぜ速いのかと、DFT との速さの差を N ごとに測った結果が分かります。
FFT(高速フーリエ変換)は、N 点の DFT を「偶数番目」と「奇数番目」の2つの N/2 点の DFT に分け、それをさらに分けていくことで、同じ掛け算を使い回す方法です。 もっとも有名なのがクーリー–テューキー型で、N が2の累乗のとき、手間は N² から N log₂N 程度に減ります。
flowchart TD
A[8点のDFT] --> B[偶数番目 4点]
A --> C[奇数番目 4点]
B --> D[2点]
B --> E[2点]
C --> F[2点]
C --> G[2点]
D & E & F & G --> H[バタフライ演算で<br>結果を組み立てる]
分けたあと、奇数番目の結果に回転(W の累乗)を掛けて偶数番目の結果と足し引きします。この足し引きの形が蝶に似ているので「バタフライ演算」と呼ばれます。
| N | DFT の掛け算 N² | FFT の目安 N log₂N | 手間の比 |
|---|---|---|---|
| 8 | 64 | 24 | 約2.7倍 |
| 1,024 | 1,048,576 | 10,240 | 約102倍 |
| 65,536 | 約43億 | 1,048,576 | 約4,096倍 |
| 1,048,576 | 約1.1兆 | 20,971,520 | 約52,429倍 |
実測:自作のDFTとFFTの速さ
同じ乱数データを、4つの方法で変換した時間です(2コアの Xeon 2.10GHz、timeit の最小値)。
| N | Pythonだけの二重ループ | NumPyで1行ずつ | np.fft.fft | scipy.fft.fft |
|---|---|---|---|---|
| 256 | 167.5 ms | 7.4 ms | 7.0 µs | 5.1 µs |
| 1,024 | 2,492.2 ms | 30.1 ms | 15.4 µs | 11.9 µs |
| 4,096 | 48,369.4 ms | 1,017.3 ms | 59.5 µs | 28.2 µs |
| 16,384 | (測らず) | 10,923.5 ms | 269.5 µs | 140.5 µs |
- N が4倍になると、二重ループは約15〜19倍(N² なら16倍)に増え、FFT は2〜5倍ほどしか増えませんでした。
- この環境では、2の累乗などの長さで
scipy.fftがnumpy.fftより1.3〜2.6倍ほど速くなりました。どちらも結果は同じです。
長さが素数だと遅くなる
| N | N の特徴 | np.fft.fft | scipy.fft.fft | next_fast_len |
|---|---|---|---|---|
| 65,536 | 2¹⁶ | 1.42 ms | 0.68 ms | 65,536 |
| 65,521 | 素数 | 7.59 ms | 6.84 ms | 65,536 |
| 65,537 | 素数 | 7.08 ms | 8.15 ms | 65,610 |
| 100,000 | 2⁵×5⁵ | 1.92 ms | 0.92 ms | 100,000 |
| 99,991 | 素数 | 12.19 ms | 12.42 ms | 100,000 |
2の累乗でなくても、小さな素数(2・3・5 など)の積なら速く、素数の長さだけが5〜14倍遅くなりました。 SciPy の公式ドキュメントは、scipy.fft.next_fast_len で「小さな素因数だけを持つ、目的以上の長さ」を求めてゼロで埋めると速くなる、と説明しています。2の累乗にこだわる必要は無く、100,000 点もそのまま速く計算できました。
PythonでFFTする手順(NumPy・SciPy)と周波数の当て方
この節では、NumPy で FFT をして「何Hzが、どれだけの大きさか」を正しく読み取る手順と、雑音の中から音の周波数を当てた結果が分かります。
FFT の結果は複素数の配列で、そのままでは横軸(周波数)も縦軸(振幅)も分かりません。 次の手順で直します。
サンプリング周波数 fs と点数 N を決める
1秒に何回測るか(fs)と、何個のデータを使うか(N)。観測時間は N/fs 秒です。
実数のデータは rfft を使う
X = np.fft.rfft(x)。0Hz からナイキスト周波数(fs/2)までの N/2+1 個だけを返します。
周波数の軸を作る
f = np.fft.rfftfreq(N, d=1/fs)。d を省くと 0〜0.5 の「サンプルあたりの周波数」になります。
振幅に直す
amp = np.abs(X) * 2 / N。ただし 0Hz(直流)と、N が偶数のときのナイキストの点は 2 を掛けずに N で割ります。
大きい順に並べる
np.argsort(amp)[::-1] や scipy.signal.find_peaks でピークを探します。
雑音の中の440Hzと1000Hzを当てる
大きさ1の440Hz、大きさ0.5の1000Hz(位相0.3)に、標準偏差1の雑音を足した1秒の信号です。雑音のほうが強く(SN比 −2.06dB)、波形の数字を見ても何も分かりません。
import numpy as np
rng = np.random.default_rng(42)
fs = 8000 # サンプリング周波数 [Hz]
N = fs * 1 # 1秒分
t = np.arange(N) / fs
clean = 1.0 * np.sin(2*np.pi*440*t) + 0.5 * np.sin(2*np.pi*1000*t + 0.3)
x = clean + rng.normal(0, 1.0, N)
X = np.fft.rfft(x)
f = np.fft.rfftfreq(N, d=1/fs)
amp = np.abs(X) * 2 / N
amp[0] /= 2 # 直流は2倍しない
for i in np.argsort(amp)[::-1][:4]:
print(f"{f[i]:.1f}Hz amp={amp[i]:.4f} phase={np.angle(X[i]):+.4f}")
440.0Hz amp=0.9912 phase=-1.5651
1000.0Hz amp=0.5137 phase=-1.2816
1017.0Hz amp=0.0677 phase=+0.3765
111.0Hz amp=0.0630 phase=+2.2336
| 項目 | 入れた値 | FFTで読んだ値 |
|---|---|---|
| 1つ目の周波数・振幅 | 440Hz・1.0 | 440.0Hz・0.9912 |
| 2つ目の周波数・振幅 | 1000Hz・0.5 | 1000.0Hz・0.5137 |
| 1000Hz の位相(sin を基準) | 0.3 → 角度は 0.3−π/2 = −1.2708 | −1.2816 |
| 雑音の中でいちばん大きい点 | ― | 1017Hz・0.0677 |
| 雑音の床(中央値) | ― | 0.0189 |
雑音は約4,000個の周波数の点に散らばり、1点あたりは小さくなります。一方、音は1点に集まります。 そのため、波形では埋もれていた音が、周波数の世界では雑音の約15倍の高さで見えました。sin の位相は、np.angle では「cos を基準にした角度」になるので、π/2 だけずれて読めます。
観測時間を短くすると見分けられなくなる
| 使った長さ | 周波数の刻み Δf | 大きい順の3点 |
|---|---|---|
| 1秒(8000点) | 1 Hz | 440Hz・0.991/1000Hz・0.514/1017Hz・0.068 |
| 0.1秒(800点) | 10 Hz | 440Hz・0.947/1000Hz・0.609/1370Hz・0.169 |
| 0.01秒(80点) | 100 Hz | 400Hz・0.765/1000Hz・0.600/500Hz・0.513 |
0.01秒では刻みが100Hzになり、440Hz という点がそもそもありません。周波数の刻みは Δf = fs/N = 1/観測時間 で決まります。 細かく見たいなら、長く測ります。
逆変換で雑音を取る
振幅0.2より大きい2点(440Hz・1000Hz)だけを残し、ほかを0にして np.fft.irfft(Y, n=N) で戻すと、もとのきれいな信号との RMS 誤差は 1.0024 から 0.0128 に下がりました。ただしこれは、音が FFT の点にぴったり乗る周波数で、しかも2つだけだと分かっている例です。実際の音声では、次の節の窓関数や、帯域で切るフィルターを組み合わせます。数値計算の配列操作は関連記事「NumPyの使い方 基本一覧|配列の作成・演算・形状変更」で扱っています。
窓関数と周波数分解能(漏れ・ゼロ埋めの実測)
この節では、FFT の結果が「にじむ」理由と、窓関数で何が変わるのか、ゼロ埋めで分解能が上がらない理由が数値で分かります。
FFT は、切り取ったデータが「ずっと繰り返す」とみなして計算します。 周波数が FFT の点(ビン)にぴったり乗らないと、切り取りの端で波がつながらず、ほかの周波数に成分が漏れます。これを漏れ(リーケージ)と呼びます。窓関数は、データの両端をなめらかに0に近づけて、つなぎ目の段差を消すための掛け算です。
100Hz(ビンの上)と100.5Hz(ビンの間)
fs=1000、N=1000(Δf=1Hz)で、大きさ1の正弦波を5種類の窓で変換しました。振幅は窓の合計で割って直しています(2·|X| / Σw)。
| 窓 | 100.5Hz の読める振幅 | 10Hz 離れた点の漏れ | 50Hz 離れた点の漏れ |
|---|---|---|---|
| なし(矩形窓) | 0.6380 | −25.9 dB | −41.5 dB |
| ハン(hann) | 0.8488 | −67.1 dB | −110.3 dB |
| ハミング(hamming) | 0.8176 | −45.3 dB | −60.2 dB |
| ブラックマン(blackman) | 0.8812 | −75.3 dB | −118.0 dB |
| フラットトップ(flattop) | 0.9989 | −108.3 dB | −100.1 dB |
- 100Hz(ビンの上)では、どの窓でも振幅は 1.0000 で、漏れは −280dB 以下(計算上の0)でした。漏れは「ビンの間の周波数」で起きます。
- 窓なしでは、100.5Hz の振幅が 0.638(約 −3.9dB)に下がって見えました。フラットトップ窓は振幅をほぼ正しく(0.9989)読めます。
大きな音の横の小さな音(−60dB)
100.5Hz(大きさ1)の横に、130.5Hz(大きさ0.001=−60dB)を足しました。
| 窓 | 125〜135Hz のいちばん大きい点 | 120Hz の床 | 小さな音が見えたか |
|---|---|---|---|
| なし | 125Hz・−38.6 dB | −36.4 dB | 見えない(漏れに埋もれる) |
| ハミング | 131Hz・−53.2 dB | −53.2 dB | 床と同じ高さで区別できない |
| ハン | 130Hz・−61.3 dB | −87.4 dB | 見える |
| ブラックマン | 130Hz・−61.0 dB | −94.9 dB | 見える |
振幅を読むとき
2|X|/N のまま読む。ハン窓なら振幅が約半分(0.5倍)に見える2|X|/Σw(窓の合計で割る)。ハン窓の平均は 0.5、ハミングは 0.54、ブラックマンは 0.42 だった窓は振幅を小さくする。直す係数は窓ごとに違う
| 窓 | 窓の平均(振幅の縮み) | 等価雑音帯域幅 | 向いている用途 |
|---|---|---|---|
| 矩形(なし) | 1.0000 | 1.000 ビン | ビンに乗る周期信号・過渡の短い信号 |
| ハン | 0.5000 | 1.500 ビン | 迷ったらこれ。一般の分析 |
| ハミング | 0.5400 | 1.363 ビン | 近くの同じ大きさの音を分ける |
| ブラックマン | 0.4200 | 1.727 ビン | 大きな音の横の小さな音を探す |
| フラットトップ | 0.2156 | 3.770 ビン | 振幅を正確に測る(校正) |
scipy.signal.get_window は、既定で FFT 向けの「周期的な窓」を返します(SciPy の公式ドキュメント)。np.hanning は左右対称の窓で、フィルター設計向けです。長いデータではほとんど差がありません。
周波数分解能:2つの近い音(100Hz と 101.5Hz)
1.5Hz しか離れていない2つの音を、観測時間を変えて見分けられるかを調べました。ハン窓を掛け、そのままの FFT と、16,000点までゼロで埋めた FFT を比べています。
| 観測時間(点数) | Δf | そのままのピーク | ゼロ埋めのピーク |
|---|---|---|---|
| 0.25秒(250) | 4 Hz | 100Hz の1つ | 100.75Hz の1つ(まん中) |
| 0.5秒(500) | 2 Hz | 100Hz の1つ | 99.875Hz と 101.625Hz(ずれる) |
| 1秒(1000) | 1 Hz | 100Hz の1つ | 100.125Hz と 101.375Hz |
| 2秒(2000) | 0.5 Hz | 100Hz と 101.5Hz | 100.0Hz と 101.5Hz |
- ゼロ埋めは、点と点の間を細かく描く(補間する)だけで、情報は増えません。 0.25秒のデータは、ゼロ埋めしても2つの山に分かれず、まん中の 100.75Hz に1つの山が出ました。
- 山が2つに分かれても、0.5秒・1秒では山の位置が本当の周波数からずれました。正しい位置で分かれたのは2秒(周波数の差 1.5Hz の逆数 0.67秒の約3倍)からです。
- ゼロ埋めは、1つの音の周波数を細かく読むのには役立ちます。100.3Hz の音(0.5秒)は、そのままでは 100Hz、ゼロ埋めでは 100.25Hz と読めました。
1分解能は観測時間で決まる
Δf = 1/観測時間。近い2つの音を分けるには、差の逆数より長く測る。
2窓は漏れを減らすが山を太らせる
ハン窓の等価雑音帯域幅は矩形窓の1.5倍。山が太り、近い音を分ける力は少し落ちる。
3ゼロ埋めは見た目を細かくする
速さのため(next_fast_len)や、山の位置を細かく読むために使う。
サンプリング定理とエイリアシング(周波数が化ける実例)
この節では、サンプリング周波数の半分を超える成分が別の周波数に化ける様子と、それを防ぐ方法が分かります。
サンプリング定理(標本化定理)は、「もとの信号に含まれる最も高い周波数の2倍より高い周波数でサンプリングすれば、もとの信号を復元できる」というものです。 サンプリング周波数の半分 fs/2 をナイキスト周波数と呼びます。これを超える成分は、FFT では低い周波数に化けて(エイリアシング)見えます。
fs=1000Hz(ナイキスト周波数 500Hz)で、いろいろな周波数の cos 波をサンプリングして FFT しました。
| 入れた周波数 | FFTで見えた周波数 | 読み方 |
|---|---|---|
| 100 Hz | 100 Hz | 正しく見える |
| 499 Hz | 499 Hz | ナイキストより下なので正しい |
| 500 Hz | 500 Hz(振幅2.0と表示) | ナイキストの点は2倍しない。直すと1.0 |
| 600 Hz | 400 Hz | 1000−600 |
| 700 Hz | 300 Hz | 1000−700 |
| 900 Hz | 100 Hz | 1000−900 |
| 1100 Hz | 100 Hz | 1100−1000 |
| 1300 Hz | 300 Hz | 1300−1000 |
見える周波数は「入れた周波数から fs の整数倍を引いて、0〜fs/2 に折り返した値」です。700Hz と 300Hz の cos 波のサンプルを並べると、先頭5個はどちらも 1, −0.309, −0.809, 0.809, 0.309 で、1000個の最大の差は 1.28×10⁻¹²(丸め誤差)でした。サンプルにした時点で区別がつかなくなるので、FFT のあとでは直せません。sin 波の場合は、700Hz が 300Hz の符号を反転した形になりました(最大の差 1.17×10⁻¹²)。
間引く前に高い周波数を落とす
8000Hz で測った「100Hz+700Hz(大きさ0.5)」を、1000Hz に間引きました。
| 間引き方 | 100Hz の振幅 | 300Hz に現れた偽の成分 |
|---|---|---|
8個に1個取るだけ(y[::8]) | 1.0 | 0.5(700Hz がそのまま化けた) |
scipy.signal.decimate(y, 8, ftype='fir') | 0.9999 | 0.0007 |
decimate は、間引く前にナイキスト周波数より上を落とすフィルター(アンチエイリアシングフィルター)を掛けます。録音機器や ADC の前にも同じ役目のフィルターが入っています。
フーリエ変換の使い道(畳み込み・画像・スペクトログラム)
この節では、周波数を調べる以外の FFT の使い道を、実際に動かした結果で分かります。
畳み込みを FFT で速く計算する(畳み込み定理)
「時間の世界での畳み込み」は「周波数の世界での掛け算」になります(畳み込み定理)。 移動平均やフィルター、画像のぼかしは畳み込みなので、長いデータでは FFT で計算したほうが速くなります。
a = rng.standard_normal(1000)
b = rng.standard_normal(300)
c1 = np.convolve(a, b) # そのまま計算
L = len(a) + len(b) - 1 # 1299 点
c2 = np.fft.irfft(np.fft.rfft(a, L) * np.fft.rfft(b, L), L)
print(np.abs(c1 - c2).max()) # 6.39e-14
結果の差は 6.39×10⁻¹⁴ でした。ゼロ埋めを忘れて長さ1000のまま掛けると、後ろがはみ出して前に回り込み(巡回畳み込み)、差は最大 44.5 になりました。 速さは次のとおりです。
| データの長さ × フィルターの長さ | np.convolve | scipy.signal.fftconvolve |
|---|---|---|
| 10,000 × 1,000 | 0.9 ms | 0.21 ms |
| 100,000 × 10,000 | 200.9 ms | 2.89 ms |
| 1,000,000 × 10,000 | 2,878.0 ms | 40.78 ms |
画像の縞模様(2次元FFT)
64×64 の画像に、横方向に8回繰り返す縦縞(大きさ1)と、縦方向に3回繰り返す横縞(大きさ0.5)を重ねて np.fft.fft2 すると、成分が出たのは (行0, 列8)・(行0, 列56) が 0.5、(行3, 列0)・(行61, 列0) が 0.25 の4点だけでした(N²で割った値)。56 = 64−8、61 = 64−3 は負の周波数です。縞の向きと細かさが、周波数の世界では点の位置になります。 画像の圧縮やノイズ除去は、この性質を使います。
時間とともに変わる音(スペクトログラム)
200Hz から2000Hz へ2秒かけて上がる音(チャープ)を FFT 1回で見ると、200〜2000Hz の全体に平らに広がり(平均振幅 0.0166)、「いつ何Hzだったか」は分かりません。短い区間ごとに FFT する短時間フーリエ変換(STFT)を使うと、時刻ごとの周波数が読めました。
from scipy import signal
from scipy.signal import ShortTimeFFT
from scipy.signal.windows import hann
fs = 8000
t = np.arange(fs * 2) / fs
x = signal.chirp(t, f0=200, t1=2.0, f1=2000, method='linear')
SFT = ShortTimeFFT(hann(512, sym=False), hop=256, fs=fs, scale_to='magnitude')
S = np.abs(SFT.stft(x)) # 行が周波数、列が時刻
| 時刻 | STFT のピーク | 本当の周波数 |
|---|---|---|
| 0.256 秒 | 437.5 Hz | 430.4 Hz |
| 0.512 秒 | 656.2 Hz | 660.8 Hz |
| 0.992 秒 | 1093.8 Hz | 1092.8 Hz |
| 1.504 秒 | 1546.9 Hz | 1553.6 Hz |
| 1.760 秒 | 1781.2 Hz | 1784.0 Hz |
周波数の刻みは 15.625Hz(8000/512)、時間の刻みは 0.032 秒でした。区間を長くすると周波数は細かく、時間は粗くなります。両方を同時に細かくはできません。 なお SciPy の公式ドキュメントは、以前からある scipy.signal.stft を「legacy(今後は更新しない)」とし、新しいコードには ShortTimeFFT を勧めています。
ラプラス変換との違い
この節では、フーリエ変換とラプラス変換が何を共有し、どこで使い分けるのかが分かります。
ラプラス変換は、フーリエ変換の e^(−iωt) に減衰の項 e^(−σt) を足したもの(s = σ + iω)で、0から∞まで積分します。 減衰を足すことで、時間とともに増えていく関数も扱え、初期値のある微分方程式(回路のスイッチを入れた直後など)を解きやすくなります。
| フーリエ変換 | ラプラス変換 | |
|---|---|---|
| 式(角周波数) | ∫[−∞→∞] f(t)e^(−iωt)dt | ∫[0→∞] f(t)e^(−st)dt |
| 変数 | 実数の ω(周波数) | 複素数の s = σ + iω |
| e^t のように増える関数 | 積分が発散して扱えない | σ を大きくすれば扱える |
| 主な使い道 | 周波数を調べる・信号処理・画像 | 微分方程式・制御・過渡応答 |
| 計算機での形 | DFT・FFT | 離散時間の信号では z 変換が対応する |
手元で f(t) = e^(−t)(t≧0)を数値積分でフーリエ変換すると、ω=1 で 0.5−0.5i、ω=2 で 0.2−0.4i、ω=5 で 0.03846−0.19231i となり、ラプラス変換 1/(s+1) に s = iω を入れた値と一致しました。収束する関数なら、ラプラス変換の s を iω に置くとフーリエ変換になります。 一方 e^t は、0〜T の積分が T=10 で 2.2025×10⁴、T=20 で 4.8517×10⁸、T=40 で 2.3539×10¹⁷ と増え続け、フーリエ変換は存在しません。ラプラス変換なら s=2 で 1/(s−1) = 1 になり、数値積分でも 1.0000000000000002 でした。
ラプラス変換の変換表・逆変換・微分方程式の解き方は、同じ時期に公開する関連記事「ラプラス変換とは|変換表・逆変換・微分方程式の解き方をSymPyで検算」で扱います。データに直線や曲線を当てはめる話は関連記事「最小二乗法とは|公式・手計算の例とPythonでの求め方を実例で解説」で、データを直交する成分に分ける別の方法は関連記事「主成分分析(PCA)とは|手順・寄与率・負荷量の読み方をPythonで実演」で扱っています。
よくあるエラー・つまずきと判断表
この節では、FFT のコードで出るエラーの原文と直し方、そして目的ごとにどの道具を使うかが分かります。
エラー・警告の原文
| したこと | 出た文言(原文) | 直し方 |
|---|---|---|
空の配列を np.fft.fft | ValueError: Invalid number of FFT data points (0) specified. | 読み込みや切り出しで0個になっていないか確かめる |
| 長さの違うスペクトルを掛けた | ValueError: operands could not be broadcast together with shapes (501,) (513,) | 両方を同じ n で rfft する(rfft(a, L)) |
| FFTの結果を実数の配列に入れた | ComplexWarning: Casting complex values to real discards the imaginary part | np.abs(X)(大きさ)か X.real を明示する |
窓の名前を 'hanning' | ValueError: Invalid window name 'hanning' in parameter window='hanning'! | SciPy では 'hann'。NumPy の関数名は np.hanning |
get_window('kaiser', 8) | ValueError: 'kaiser' must have parameters, but window='kaiser'! | get_window(('kaiser', 8.6), 8) のようにタプルで渡す |
np.fft.fftfreq(8.0, 0.1) | ValueError: n should be an integer | int(N) を渡す |
エラーにならないのに間違うもの
- 1振幅が N/2 倍のまま大きさ1の440Hzが |X| = 4000 と出た(N=8000)。2/N を掛けて 1.0。norm='forward' なら 0.5、'ortho' なら 44.72
- 2rfftfreq に d を渡し忘れる横軸が 0〜0.5 になり、Hz ではない。d=1/fs を渡す
- 3奇数の長さを irfft で戻す7点の rfft を n なしで irfft すると6点になり、値も [0.58, 1.18, 2.79, …] と崩れた。n=7 を渡すと [0, 1, 2, …, 6] に戻った
- 4fft の後ろ半分を高い周波数だと思う後ろ半分は負の周波数。fftfreq(8, d=0.001) は [0, 125, 250, 375, −500, −375, −250, −125]。並べ替えは fftshift
- 5畳み込みでゼロ埋めを忘れる巡回畳み込みになり、差は最大 44.5。長さを len(a)+len(b)−1 以上にする
- 6新しいコードで scipy.fftpack を使う公式ドキュメントは legacy(更新しない)とし、scipy.fft を勧めている
どれを使うかの判断表
| したいこと | 使うもの | 手元の結果 |
|---|---|---|
| 周期関数を式で sin・cos に分ける | フーリエ級数展開(係数を積分) | 矩形波 bn = 4/(nπ) |
| 理論で関数の周波数成分を求める | フーリエ変換(積分) | 矩形→sinc、ガウス→ガウス |
| 測ったデータの周波数を調べる | np.fft.rfft/scipy.fft.rfft | 雑音の中の440Hzを0.9912で検出 |
| 周波数がビンの間にありそう | ハン窓(振幅を正確に読むならフラットトップ) | 漏れ −26dB → −67dB |
| 時間とともに周波数が変わる | STFT(ShortTimeFFT) | チャープを時刻ごとに追えた |
| 長いデータの畳み込み・フィルター | scipy.signal.fftconvolve | 2,878 ms → 40.78 ms |
| 長さが素数で遅い | scipy.fft.next_fast_len でゼロ埋め | 素数長は5〜14倍遅かった |
| 過渡応答・微分方程式・制御 | ラプラス変換 | e^t もs=2で扱えた |
学ぶ順番
- 1三角関数と複素数・オイラーの公式e^(iθ) = cosθ + i·sinθ が読めれば式が追える
- 2フーリエ級数(矩形波を手で計算)係数=内積という見方をつかむ
- 3DFT を N=4 で手計算し、自作コードで np.fft と比べる式と結果の並びが結びつく
- 4rfft・rfftfreq・振幅の直し方440Hz を当てる例を自分で動かす
- 5窓関数・分解能・エイリアシング「見えたものが本物か」を判断できる
- 6フーリエ変換の性質・ラプラス変換・STFT専門分野(音声・制御・画像)へ進む
よくある質問
Q. フーリエ変換とは、ひとことで言うと何ですか? A. 時間とともに変わる信号を、周波数ごとの成分(どの周波数が、どれだけの大きさと位相で含まれるか)に書き換える計算です。逆変換で、もとの信号に戻せます。
Q. フーリエ級数展開とフーリエ変換の違いは何ですか? A. フーリエ級数展開は周期のある関数を、とびとびの周波数の sin・cos の足し算で表します。フーリエ変換は周期の無い関数を、連続した周波数の積分で表します。周期を無限に長くした極限がフーリエ変換です。
Q. FFT とは何ですか?DFT と結果は違いますか?
A. FFT(高速フーリエ変換)は DFT を速く計算する方法で、結果は同じです。手元では N=1000 で差が 3.13×10⁻¹¹(丸め誤差)でした。N=4,096 では、定義どおりの Python のループが48.4秒、scipy.fft.fft が28.2µsでした。
Q. FFT のデータ数は2の累乗でないといけませんか?
A. NumPy・SciPy では、どの長さでも計算できます。速いのは小さな素数の積の長さで、100,000点は0.92msでした。遅いのは素数の長さ(99,991点で12.42ms)で、next_fast_len でゼロ埋めすると速くなります。
Q. FFT の結果の振幅が、入れた値と合いません。
A. NumPy の既定(norm='backward')では順方向に割り算が無いため、大きさ1の正弦波が N/2 として出ます。np.abs(X)*2/N で直します。窓を掛けたら 2*np.abs(X)/np.sum(w) にし、周波数がビンの間にあるときは窓で振幅が変わる点にも注意します。
Q. 窓関数はいつ使いますか? A. 測る周波数が FFT の点にぴったり乗らないときです(実際の測定のほとんど)。手元では窓なしの漏れが10Hz先で −25.9dB、ハン窓で −67.1dB でした。迷ったらハン窓、振幅を正確に読みたいならフラットトップ窓を使います。
Q. サンプリング周波数はどう決めますか? A. 調べたい最も高い周波数の2倍より高くします。それを超える成分は折り返して別の周波数に見えるため、測る前にアンチエイリアシングフィルターで落とします。fs=1000Hz では700Hzが300Hzに見えました。
Q. ゼロ埋めをすると周波数分解能は上がりますか? A. 上がりません。点の間を補間して細かく描くだけです。100Hz と 101.5Hz は、0.25秒のデータではゼロ埋めしても分かれず、2秒のデータで正しく分かれました。分解能は観測時間で決まります。
Q. ラプラス変換とどう使い分けますか? A. 周波数を調べる・信号を処理するならフーリエ変換(FFT)、初期値のある微分方程式や制御の過渡応答を扱うならラプラス変換です。収束する関数では、ラプラス変換の s を iω に置くとフーリエ変換になります。
Q. numpy.fft と scipy.fft はどちらを使えばよいですか?
A. 結果は同じです。この環境では scipy.fft が多くの長さで1.3〜2.6倍ほど速く、next_fast_len や workers(並列)も使えます。scipy.fftpack は公式ドキュメントで legacy とされているので、新しいコードでは scipy.fft を使います。
まとめ
- フーリエ変換は、信号を周波数ごとの成分に書き換える計算。周期関数はフーリエ級数、周期の無い関数はフーリエ変換、有限個のデータは DFT で、DFT を速く計算する方法が FFT。
- 矩形波のフーリエ係数は 4/(nπ)(奇数のみ)で、数値積分と6桁まで一致した。項を1000まで増やしても跳びの横の飛び出しは8.949%(理論 1.178980)から減らない(ギブズ現象)。
- 定義どおりの DFT と np.fft は丸め誤差の範囲で一致し、N=4,096 では Python のループ48.4秒に対し scipy.fft は28.2µs。素数の長さは5〜14倍遅く、next_fast_len で避けられる。
- 振幅は 2/N(窓を掛けたら 2/Σw)で直し、横軸は rfftfreq(N, d=1/fs)。雑音の中の440Hzは0.9912として読めた。分解能は観測時間で決まり、ゼロ埋めでは上がらない。
- 周波数がビンの間にあるときは窓関数(迷ったらハン窓)を使う。fs/2 を超える成分は折り返して化けるので、間引く前・測る前に高い周波数を落とす。
次に読むとよい記事です。係数を内積で取り出す考え方は関連記事「内積・外積とは|公式と計算例・使い道をNumPyで実演」で、結果をグラフにする方法は関連記事「matplotlibの使い方と日本語文字化け対策|OS別・Colab・3.12対応」で扱っています。
参考・出典
確認日はいずれも 2026年10月29日です。
- NumPy v2.5 Manual「Discrete Fourier Transform (numpy.fft)」(定義・正規化・出力の並び・実数入力):https://numpy.org/doc/stable/reference/routines.fft.html
- SciPy v1.18.0「Fourier Transforms (scipy.fft)」:https://docs.scipy.org/doc/scipy/reference/fft.html
- SciPy v1.18.0「scipy.fft.next_fast_len」:https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.next_fast_len.html
- SciPy v1.18.0「Legacy discrete Fourier transforms (scipy.fftpack)」:https://docs.scipy.org/doc/scipy/reference/fftpack.html
- SciPy v1.18.0「scipy.signal.get_window」:https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.get_window.html
- SciPy v1.18.0「scipy.signal.stft」(legacy と ShortTimeFFT の案内):https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.stft.html
- SciPy v1.18.0「scipy.signal.ShortTimeFFT」:https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.ShortTimeFFT.html
- J. W. Cooley, J. W. Tukey「An Algorithm for the Machine Calculation of Complex Fourier Series」Mathematics of Computation, 19 (1965), 297–301
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「フーリエ変換とは|フーリエ級数展開・DFT・FFTの違いをPythonで実測」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。