5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

実験やデータ解析では、測定値をそのまま使うだけでなく、測定値から新しい量を計算することがよくあります。

たとえば、

  • 長さと幅から面積を求める
  • 距離と時間から速度を求める
  • 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でダミーデータを作って確認します。

考え方は単純です。

  1. $(x)$ と $(y) $がばらつく測定値だと考える
  2. たくさんの $(x), (y)$ の組をランダムに作る
  3. それぞれについて $(A=xy) $を計算する
  4. 面積 $(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点ではなく、ばらつきを持つ

fig1_measured_values_cloud.png

図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:計算後の面積にもばらつきが生じる

fig2_area_uncertainty.png

図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:足し算・掛け算・割り算で不確かさを比較する

fig3_formula_vs_montecarlo.png

図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:誤差伝播式がうまくいかない場合もある

fig4_limitation_nonlinear.png

最後に、誤差伝播式の注意点を見ます。

誤差伝播式は、関数を測定値のまわりで一次近似する方法です。

ざっくり言えば、

測定値の近くでは、曲線を直線のように見なす

という近似です。

そのため、関数が十分なめらかで、不確かさが小さい場合にはよく働きます。

しかし、関数の曲がりが強い場合や、不確かさが大きい場合には、うまくいかないことがあります。

ここでは、

$$
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のずれがどう変わるかも観察できます。

式を眺めるだけでなく、パラメータを変えて図を動かすと、誤差伝播の感覚がかなりつかみやすくなります。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?