実験やデータ解析では、測定値をそのまま使うだけでなく、測定値から新しい量を計算することがよくあります。
たとえば、
- 長さと幅から面積を求める
- 距離と時間から速度を求める
- 2つの測定値の比を求める
- 複数の値を足し合わせる
といった計算です。
ここで重要なのは、元の測定値に誤差や不確かさがあるなら、計算後の値にも不確かさが生じるということです。
この記事では、ダミーデータを使って、
測定値の不確かさは、計算後にどう広がるのか?
をPythonで可視化します。
Google Colabでそのまま実行できるコードも載せます。
この記事で分かること
この記事では、以下を扱います。
- 測定値、誤差、不確かさの基本的な考え方
- 誤差伝播の基本式
- 面積 $(A=x\times y)$ の不確かさの計算
- Monte Carloシミュレーションによる確認
- 誤差伝播式がうまくいく場合とうまくいかない場合
まず用語を整理する
測定値
測定値とは、実験や観測によって得られた値です。
たとえば、ある長方形の辺の長さを測って、
$$
x = 10.0\ \mathrm{cm}
$$
と得られたとします。
しかし、現実の測定では完全に正確な値を得ることはできません。
定規の目盛り、測定者の読み取り、装置の分解能、ノイズなどによって、測定値にはばらつきが生じます。
誤差と不確かさ
厳密には、「誤差」と「不確かさ」は少し違う概念です。
- 誤差:真の値からどれだけずれているか
- 不確かさ:その測定値がどれくらいばらつきそうか
ただし、真の値は普通は分かりません。
そのため、実際のデータ解析では、
この測定値は、だいたいこのくらいの幅でばらつく
という意味で、不確かさを考えることが多いです。
この記事では、分かりやすさのために、不確かさを標準偏差として表します。
たとえば、
$$
x = 10.0 \pm 0.3\ \mathrm{cm}
$$
と書いたとき、この記事では
- 代表値は $(10.0\ \mathrm{cm})$
- 標準偏差は $(0.3\ \mathrm{cm})$
という意味で使います。
誤差伝播とは何か
測定値 (x), (y) から、何らかの計算によって
$$
z = f(x, y)
$$
という値を求めるとします。
このとき、$(x) $と $(y) $に不確かさがあるなら、$(z) $にも不確かさが生じます。
この不確かさを見積もる考え方が 誤差伝播 です。
$(x) $と $(y) $が独立にばらつくと仮定すると、基本的な誤差伝播式は次のように書けます。
$$
\sigma_z^2
\approx
\left(\frac{\partial f}{\partial x}\right)^2 \sigma_x^2
+
\left(\frac{\partial f}{\partial y}\right)^2 \sigma_y^2
$$
ここで、
- $(\sigma_x)$:$(x) $の標準偏差
- $(\sigma_y)$:$(y) $の標準偏差
- $(\sigma_z)$:計算後の値 $(z) $の標準偏差
- $(\frac{\partial f}{\partial x})$:$(x)$ が少し変わったときに $(z) $がどれくらい変わるか
- $(\frac{\partial f}{\partial y})$:$(y) $が少し変わったときに $(z)$ がどれくらい変わるか
です。
少し難しく見えますが、直感的には、
計算結果が、各測定値の変化にどれくらい敏感かを使って、不確かさの広がりを見積もる
という式です。
例:長さと幅から面積を求める
ここでは、長方形の面積を例にします。
長さを (x)、幅を (y) とすると、面積 (A) は
$$
A = xy
$$
です。
測定値を次のように仮定します。
$$
x = 10.0 \pm 0.3\ \mathrm{cm}
$$
$$
y = 5.0 \pm 0.2\ \mathrm{cm}
$$
このとき、面積の代表値は
$$
A = 10.0 \times 5.0 = 50.0\ \mathrm{cm^2}
$$
です。
では、面積の不確かさはどうなるでしょうか。
$(A = xy) $なので、
$$
\frac{\partial A}{\partial x} = y
$$
$$
\frac{\partial A}{\partial y} = x
$$
です。
したがって、
$$
\sigma_A
\approx
\sqrt{
(y\sigma_x)^2
+
(x\sigma_y)^2
}
$$
となります。
今回の値を代入すると、
$$
\sigma_A
\approx
\sqrt{
(5.0 \times 0.3)^2
+
(10.0 \times 0.2)^2
}
=2.5\ \mathrm{cm^2}
$$
です。
つまり、面積はおおよそ
$$
A = 50.0 \pm 2.5\ \mathrm{cm^2}
$$
と見積もれます。
Monte Carloシミュレーションで確かめる
誤差伝播式は便利ですが、式だけだと少し実感しにくいです。
そこで、Pythonでダミーデータを作って確認します。
考え方は単純です。
- $(x)$ と $(y) $がばらつく測定値だと考える
- たくさんの $(x), (y)$ の組をランダムに作る
- それぞれについて $(A=xy) $を計算する
- 面積 $(A) $がどれくらいばらつくかを見る
このように、乱数を使ってたくさんの可能な測定値を作る方法を Monte Carloシミュレーション と呼びます。
Google Colabで実行するコード
以下のコードは、Google Colabでそのまま実行できます。
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
import math
# 乱数の再現性を固定します
rng = np.random.default_rng(42)
# 図の保存先フォルダ
out_dir = Path("fig_error_propagation")
out_dir.mkdir(exist_ok=True)
# ------------------------------------------------------------
# 基本設定:2つの測定値 x, y
# ------------------------------------------------------------
# x と y は長方形の2辺の長さ [cm] とします。
# それぞれの不確かさを標準偏差として表します。
x0 = 10.0 # x の代表値 [cm]
y0 = 5.0 # y の代表値 [cm]
sx = 0.30 # x の標準不確かさ [cm]
sy = 0.20 # y の標準不確かさ [cm]
# Monte Carloシミュレーションのサンプル数
n = 100_000
# x と y が正規分布に従ってばらつくと仮定して、ダミーデータを作ります
x = rng.normal(loc=x0, scale=sx, size=n)
y = rng.normal(loc=y0, scale=sy, size=n)
def normal_pdf(values, mean, sd):
"""正規分布の確率密度関数"""
return np.exp(-0.5 * ((values - mean) / sd)**2) / (sd * np.sqrt(2 * np.pi))
# ------------------------------------------------------------
# 図1:測定値 x, y のばらつき
# ------------------------------------------------------------
plt.figure(figsize=(6, 5))
# 点が多すぎると見づらいので、最初の5000点だけ描画します
plt.scatter(x[:5000], y[:5000], s=8, alpha=0.15)
# 代表値と ±1標準偏差の位置を線で示します
plt.axvline(x0, linestyle="--", label="nominal x")
plt.axvline(x0 - sx, linestyle=":", label="x ± 1 SD")
plt.axvline(x0 + sx, linestyle=":")
plt.axhline(y0, linestyle="--", label="nominal y")
plt.axhline(y0 - sy, linestyle=":", label="y ± 1 SD")
plt.axhline(y0 + sy, linestyle=":")
plt.xlabel("x [cm]")
plt.ylabel("y [cm]")
plt.title("Measured values with uncertainty")
plt.legend()
plt.tight_layout()
plt.savefig(out_dir / "fig1_measured_values_cloud.png", dpi=160)
plt.show()
# ------------------------------------------------------------
# 面積 A = x * y の不確かさ
# ------------------------------------------------------------
area = x * y
# 代表値から計算した面積
area_nominal = x0 * y0
# 誤差伝播式による面積の標準不確かさ
area_sd_formula = math.sqrt((y0 * sx)**2 + (x0 * sy)**2)
# ------------------------------------------------------------
# 図2:計算後の面積 A の分布
# ------------------------------------------------------------
plt.figure(figsize=(7, 4.5))
# Monte Carloで得られた面積の分布
plt.hist(area, bins=80, density=True, alpha=0.6, label="Monte Carlo")
# 誤差伝播式で見積もった正規分布を重ねます
grid = np.linspace(
area.mean() - 4 * area.std(ddof=1),
area.mean() + 4 * area.std(ddof=1),
500
)
plt.plot(
grid,
normal_pdf(grid, area_nominal, area_sd_formula),
linewidth=2,
label="error propagation formula"
)
plt.axvline(area_nominal, linestyle="--", label="nominal area")
plt.axvline(area_nominal - area_sd_formula, linestyle=":", label="nominal area ± 1 SD")
plt.axvline(area_nominal + area_sd_formula, linestyle=":")
plt.xlabel("area A = x × y [cm$^2$]")
plt.ylabel("probability density")
plt.title("Uncertainty of a calculated value")
plt.legend()
plt.tight_layout()
plt.savefig(out_dir / "fig2_area_uncertainty.png", dpi=160)
plt.show()
# ------------------------------------------------------------
# いくつかの計算で、誤差伝播式とMonte Carloを比較します
# ------------------------------------------------------------
calculations = []
# z = x + y
z_sum = x + y
z_sum_nominal = x0 + y0
z_sum_sd_formula = math.sqrt(sx**2 + sy**2)
calculations.append(("x + y", z_sum, z_sum_nominal, z_sum_sd_formula))
# z = x * y
z_product = x * y
z_product_nominal = x0 * y0
z_product_sd_formula = math.sqrt((y0 * sx)**2 + (x0 * sy)**2)
calculations.append(("x × y", z_product, z_product_nominal, z_product_sd_formula))
# z = x / y
z_ratio = x / y
z_ratio_nominal = x0 / y0
z_ratio_sd_formula = math.sqrt((sx / y0)**2 + (x0 * sy / y0**2)**2)
calculations.append(("x / y", z_ratio, z_ratio_nominal, z_ratio_sd_formula))
labels = [c[0] for c in calculations]
# Monte Carloから求めた相対不確かさ [%]
mc_rel = [
np.std(c[1], ddof=1) / np.mean(c[1]) * 100
for c in calculations
]
# 誤差伝播式から求めた相対不確かさ [%]
formula_rel = [
c[3] / c[2] * 100
for c in calculations
]
# ------------------------------------------------------------
# 図3:誤差伝播式とMonte Carloの比較
# ------------------------------------------------------------
positions = np.arange(len(labels))
width = 0.36
plt.figure(figsize=(7, 4.5))
plt.bar(positions - width/2, formula_rel, width, label="formula")
plt.bar(positions + width/2, mc_rel, width, label="Monte Carlo")
plt.xticks(positions, labels)
plt.ylabel("relative uncertainty [%]")
plt.title("Formula vs Monte Carlo")
plt.legend()
plt.tight_layout()
plt.savefig(out_dir / "fig3_formula_vs_montecarlo.png", dpi=160)
plt.show()
# ------------------------------------------------------------
# 誤差伝播式の注意点:非線形が強い場合
# ------------------------------------------------------------
# 誤差伝播式は、関数を測定値のまわりで一次近似する方法です。
# そのため、関数の曲がりが強い場合や、不確かさが大きい場合には外れることがあります。
#
# ここでは z = u^2 を例にします。
cases = [
{"label": "u=2.0±0.2", "u0": 2.0, "su": 0.2},
{"label": "u=0.0±0.5", "u0": 0.0, "su": 0.5},
]
formula_sd_square = []
mc_sd_square = []
for case in cases:
u0 = case["u0"]
su = case["su"]
u = rng.normal(u0, su, size=n)
z = u**2
# z = u^2 の微分は dz/du = 2u
# 一次の誤差伝播式では sigma_z ≈ |2u0| sigma_u
formula_sd_square.append(abs(2 * u0) * su)
# Monte Carloで得られる z の標準偏差
mc_sd_square.append(np.std(z, ddof=1))
# ------------------------------------------------------------
# 図4:一次近似がうまくいく場合と外れる場合
# ------------------------------------------------------------
positions = np.arange(len(cases))
plt.figure(figsize=(7, 4.5))
plt.bar(positions - width/2, formula_sd_square, width, label="formula")
plt.bar(positions + width/2, mc_sd_square, width, label="Monte Carlo")
plt.xticks(positions, [c["label"] for c in cases])
plt.ylabel("standard deviation of z = u$^2$")
plt.title("When first-order error propagation can fail")
plt.legend()
plt.tight_layout()
plt.savefig(out_dir / "fig4_limitation_nonlinear.png", dpi=160)
plt.show()
# ------------------------------------------------------------
# 数値結果の表示
# ------------------------------------------------------------
print("===== Basic measurements =====")
print(f"x = {x0:.2f} ± {sx:.2f} cm")
print(f"y = {y0:.2f} ± {sy:.2f} cm")
print()
print("===== Area A = x * y =====")
print(f"Nominal A: {area_nominal:.2f} cm^2")
print(f"Formula SD: {area_sd_formula:.3f} cm^2")
print(f"Monte Carlo mean: {np.mean(area):.3f} cm^2")
print(f"Monte Carlo SD: {np.std(area, ddof=1):.3f} cm^2")
print()
print("===== Formula vs Monte Carlo: relative uncertainty =====")
for label, f_rel, m_rel in zip(labels, formula_rel, mc_rel):
print(f"{label:5s}: formula = {f_rel:.2f} %, Monte Carlo = {m_rel:.2f} %")
print()
print("===== Nonlinear example z = u^2 =====")
for case, f_sd, m_sd in zip(cases, formula_sd_square, mc_sd_square):
print(f"{case['label']:10s}: formula SD = {f_sd:.3f}, Monte Carlo SD = {m_sd:.3f}")
print()
print(f"Figures were saved to: {out_dir}")
実行結果の例
上のコードを実行すると、次の数値が得られます。
===== Basic measurements =====
x = 10.00 ± 0.30 cm
y = 5.00 ± 0.20 cm
===== Area A = x * y =====
Nominal A: 50.00 cm^2
Formula SD: 2.500 cm^2
Monte Carlo mean: 49.999 cm^2
Monte Carlo SD: 2.506 cm^2
===== Formula vs Monte Carlo: relative uncertainty =====
x + y: formula = 2.40 %, Monte Carlo = 2.41 %
x × y: formula = 5.00 %, Monte Carlo = 5.01 %
x / y: formula = 5.00 %, Monte Carlo = 5.03 %
===== Nonlinear example z = u^2 =====
u=2.0±0.2 : formula SD = 0.800, Monte Carlo SD = 0.800
u=0.0±0.5 : formula SD = 0.000, Monte Carlo SD = 0.353
図1:測定値は1点ではなく、ばらつきを持つ
図1では、測定値 (x) と (y) のばらつきを点の集まりとして表示しています。
中心付近に点が多く、中心から離れるほど点が少なくなっています。
ここでは、
$$
x = 10.0 \pm 0.3\ \mathrm{cm}
$$
$$
y = 5.0 \pm 0.2\ \mathrm{cm}
$$
という設定なので、点の集まりは $((10.0, 5.0)) $のまわりに広がります。
この図から分かる大事なことは、
測定値は、厳密には1点ではなく、ある幅を持った値として考えられる
ということです。
図2:計算後の面積にもばらつきが生じる
図2では、ランダムに作った (x) と (y) から面積
$$
A = xy
$$
を計算し、その分布を表示しています。
青いヒストグラムは、Monte Carloシミュレーションで得られた面積の分布です。
オレンジの曲線は、誤差伝播式から見積もった正規分布です。
今回の例では、
$$
A = 50.0\ \mathrm{cm^2}
$$
であり、誤差伝播式から
$$
\sigma_A \approx 2.5\ \mathrm{cm^2}
$$
と見積もられます。
Monte Carloシミュレーションでも、面積の標準偏差は約
$$
2.506\ \mathrm{cm^2}
$$
となりました。
つまり、今回の条件では、誤差伝播式とMonte Carloシミュレーションがよく一致しています。
図3:足し算・掛け算・割り算で不確かさを比較する
図3では、次の3つの計算について、相対不確かさを比較しています。
$$
z = x + y
$$
$$
z = xy
$$
$$
z = \frac{x}{y}
$$
相対不確かさとは、
$$
\frac{\sigma_z}{z}
$$
のように、値そのものに対して不確かさがどれくらい大きいかを表す量です。
この記事では、見やすいようにパーセントで表示しています。
今回の例では、
- $(x+y)$:約 2.4%
- $(x\times y)$:約 5.0%
- $(x/y)$:約 5.0%
となりました。
面白いのは、元の測定値 $(x), (y) $の不確かさが同じでも、どんな計算をするかによって、計算後の不確かさが変わることです。
つまり、
測定値の不確かさは、計算によってそのまま移るのではなく、計算式の形に応じて広がり方が変わる
ということです。
少しだけ式で確認する
足し算の場合
$$
z = x + y
$$
なら、
$$
\sigma_z
\approx
\sqrt{\sigma_x^2 + \sigma_y^2}
$$
です。
足し算では、不確かさそのものが二乗和として合成されます。
掛け算の場合
$$
z = xy
$$
なら、
$$
\sigma_z
\approx
\sqrt{
(y\sigma_x)^2
+
(x\sigma_y)^2
}
$$
です。
掛け算の場合、$(x) $の不確かさは $(y) $倍され、$(y) $の不確かさは $(x) $倍されて、計算後の値に効いてきます。
相対不確かさで見ると、近似的に
$$
\frac{\sigma_z}{z}
\approx
\sqrt{
\left(\frac{\sigma_x}{x}\right)^2
+
\left(\frac{\sigma_y}{y}\right)^2
}
$$
となります。
割り算の場合
$$
z = \frac{x}{y}
$$
なら、
$$
\sigma_z
\approx
\sqrt{
\left(\frac{\sigma_x}{y}\right)^2
+
\left(\frac{x\sigma_y}{y^2}\right)^2
}
$$
です。
割り算の場合は、分母 $(y)$ の不確かさが重要です。
特に、分母が小さい場合や、分母の不確かさが大きい場合には、計算結果が大きくばらつくことがあります。
図4:誤差伝播式がうまくいかない場合もある
最後に、誤差伝播式の注意点を見ます。
誤差伝播式は、関数を測定値のまわりで一次近似する方法です。
ざっくり言えば、
測定値の近くでは、曲線を直線のように見なす
という近似です。
そのため、関数が十分なめらかで、不確かさが小さい場合にはよく働きます。
しかし、関数の曲がりが強い場合や、不確かさが大きい場合には、うまくいかないことがあります。
ここでは、
$$
z = u^2
$$
を例にしました。
$(z=u^2) $の微分は
$$
\frac{dz}{du} = 2u
$$
なので、一次の誤差伝播式では
$$
\sigma_z \approx |2u_0|\sigma_u
$$
となります。
条件1
$(u=2.0\pm0.2) $の場合
この場合は、$(u) $のばらつきが代表値 $(2.0) $に比べて小さいため、一次近似がよく働きます。
実際に、
formula SD = 0.800
Monte Carlo SD = 0.800
となり、よく一致しました。
条件2
$(u=0.0\pm0.5) $の場合
一方で、代表値が $(u_0=0) $の場合、誤差伝播式では
$$
\sigma_z \approx |2\times0|\sigma_u = 0
$$
となってしまいます。
しかし、実際には $(u)$ は$ (0) $のまわりにばらついており、$(u^2) $は正の値としてばらつきます。
Monte Carloでは、
formula SD = 0.000
Monte Carlo SD = 0.353
となりました。
つまり、この場合は一次近似による誤差伝播式が明らかに外れています。
誤差伝播式を使うときの注意点
誤差伝播式は非常に便利ですが、万能ではありません。
特に、次のような場合には注意が必要です。
- 不確かさが大きい
- 計算式が強く非線形である
- 分母に不確かな値が入っている
- 測定値どうしが独立でない
- 外れ値や歪んだ分布がある
- 正規分布で近似しにくい
今回の記事では、$(x) $と $(y) $が独立に正規分布でばらつくと仮定しました。
しかし、実データでは測定値どうしに相関があることもあります。
その場合、より一般には共分散の項も必要になります。
$$
\sigma_z^2
\approx
\left(\frac{\partial f}{\partial x}\right)^2 \sigma_x^2
+
\left(\frac{\partial f}{\partial y}\right)^2 \sigma_y^2
+
2
\frac{\partial f}{\partial x}
\frac{\partial f}{\partial y}
\mathrm{Cov}(x,y)
$$
ここで、$(\mathrm{Cov}(x,y)) $は $(x) $と $(y) $の共分散です。
ただし、初心者のうちはまず、
独立な測定値なら、不確かさは二乗和として合成される
という感覚を持つとよいと思います。
誤差伝播式とMonte Carloの使い分け
個人的には、次のように使い分けると分かりやすいと思います。
誤差伝播式が向いている場合
- 計算式が比較的単純
- 不確かさが小さい
- 結果を式で見通したい
- どの測定値の不確かさが効いているか知りたい
誤差伝播式の良いところは、式を見ることで、
どの変数の不確かさが結果に効いているのか
を理解しやすい点です。
Monte Carloが向いている場合
- 計算式が複雑
- 非線形性が強い
- 分布が正規分布とは限らない
- 途中に条件分岐や複雑な処理がある
- 誤差伝播式を手で導出するのが大変
Monte Carloの良いところは、計算式が複雑でも、とりあえずシミュレーションで確認できる点です。
ただし、Monte Carloは仮定した分布に依存します。
今回であれば、
x = rng.normal(loc=x0, scale=sx, size=n)
y = rng.normal(loc=y0, scale=sy, size=n)
としているので、
(x) と (y) は独立な正規分布に従う
という仮定を置いています。
この仮定が現実に合っていなければ、Monte Carloの結果も現実とはずれる可能性があります。
まとめ
この記事では、測定値の不確かさが計算後にどう広がるかを、誤差伝播式とMonte Carloシミュレーションで確認しました。
ポイントは以下です。
- 測定値には不確かさがある
- 測定値から計算した値にも不確かさが生じる
- 誤差伝播式は、計算後の不確かさを見積もる基本的な方法である
- $(A=xy) $のような単純な計算では、誤差伝播式とMonte Carloはよく一致する
- 誤差伝播式は一次近似なので、強い非線形や大きな不確かさでは外れることがある
- 複雑な計算では、Monte Carloシミュレーションが理解と確認に役立つ
測定値を扱うときは、代表値だけを見るのではなく、
その値はどれくらい不確かなのか
その不確かさは計算後にどう広がるのか
を考えることが大切です。
自分で試すと理解が深まる変更例
以下を変更して、結果がどう変わるか試してみてください。
sx = 0.30
sy = 0.20
を大きくすると、計算後の分布はどう広がるでしょうか。
また、
z_ratio = x / y
のような割り算では、分母 (y) の不確かさを大きくすると結果がどう不安定になるでしょうか。
さらに、
u0 = 0.0
su = 0.5
を変えると、$(z=u^2)$ に対する誤差伝播式とMonte Carloのずれがどう変わるかも観察できます。
式を眺めるだけでなく、パラメータを変えて図を動かすと、誤差伝播の感覚がかなりつかみやすくなります。



