濃度が分かっている試料を測定し、濃度と信号の関係を調べる。その関係を表したものが検量線です。
今回は、説明用の6点のデータに直線を当てはめます。Pythonで検量線を描き、傾き・切片・R²を求めるところまで進めましょう。
使うのはNumPy、Matplotlib、SciPyです。Google Colabの新しいノートブックを開き、コードを上から順に実行してください。最後には、1つのセルで試せる完成コードも載せています。
Colabの開き方やコードの実行操作から確認したい場合は、Pythonで初めてのグラフ作成を参照してください。今回はCSVファイルを使わず、数値をコードに直接書きます。
今回使うデータ
横軸の濃度(Concentration)は「試料の濃さ」、縦軸の信号(Signal)は「測定器から得られる数値」です。ここでは、試料がどれくらい濃いと、測定器の値がどれくらいになるかをグラフにします。
以下は記事の説明用に用意したデータで、実際の実験で取得したものではありません。特定の物質・測定法・単位は想定していません。
| 濃度 | 信号 |
|---|---|
| 0 | 0.12 |
| 1 | 1.05 |
| 2 | 2.08 |
| 3 | 2.91 |
| 4 | 4.02 |
| 5 | 5.10 |
import numpy as np
concentration = np.array([
0, 1, 2, 3, 4, 5
])
signal = np.array([
0.12, 1.05, 2.08, 2.91, 4.02, 5.10
])
np.array()で数値の並びをNumPyの配列にしています。後で、この並び全体に掛け算や足し算をするためです。同じ位置の濃度と信号が1組のデータになります。
まず散布図を描いてみる
計算する前に、まず元データをグラフで確認します。plt.scatter()は、濃度と信号の組を点で表示する命令です。
import matplotlib.pyplot as plt
plt.scatter(
concentration,
signal
)
plt.xlabel("Concentration")
plt.ylabel("Signal")
plt.show()

濃度が大きくなるにつれて、信号もほぼ直線的に増えています。この6点に、y = ax + bという形の直線を当てはめてみます。ここでxは濃度、yは直線から計算する信号です。
linregress()で直線を当てはめる
SciPyのlinregress()を使うと、データに合う直線の傾きと切片を求められます。
from scipy.stats import linregress
result = linregress(
concentration,
signal
)
結果はresultに入ります。今回は次の3つだけ使います。
result.slope:傾きresult.intercept:切片result.rvalue:相関係数(後でR²を計算するために使います)
傾きと切片を確認する
print("slope =", result.slope)
print("intercept =", result.intercept)
Google Colabでの実行結果は次のとおりです。
slope = 0.9897142857142854
intercept = 0.07238095238095266
y = ax + bのaが傾き、bが切片です。aにはresult.slope、bにはresult.interceptを入れるので、今回の式はおよそ次のようになります。
y = 0.9897x + 0.0724
濃度xが1増えると、直線上の信号yは約0.9897増えます。また、x = 0のときの直線上のyは約0.0724です。直線は6点全体に当てはめたものなので、元データの点をすべて通るわけではありません。
R²を求める
R²(決定係数)は、今回求めた直線がデータの変動をどの程度説明しているかを見る指標の一つです。今回の単回帰では、相関係数rvalueを2乗して求めます。** 2は2乗という意味です。
r2 = result.rvalue ** 2
print("R^2 =", r2)
R^2 = 0.9984342522980586
今回はR² ≒ 0.9984です。1に近く、この意味ではデータがよく直線に沿っていると分かります。
ただし、R²が1に近いだけで、その検量線が実験的に妥当だと判断できるわけではありません。ここでは、直線近似の結果を確認するための指標として扱います。
求めた直線をグラフに描く
まず、直線上のyを計算する考え方を確認します。
y_fit = (
result.slope * concentration
+ result.intercept
)
これはy = ax + bのxに、それぞれの濃度を代入する計算です。concentrationはNumPyの配列なので、6個の値それぞれに傾きを掛け、切片を足せます。
描画では、濃度の最小値から最大値まで100個の値を等間隔に作り、その位置で直線上のyを計算します。直線なので2点でも描けますが、ここでは描画用の位置をx_fitとして用意します。
x_fit = np.linspace(
concentration.min(),
concentration.max(),
100
)
y_fit = (
result.slope * x_fit
+ result.intercept
)
np.linspace()で作るのは、今回は0から5までの100個の値です。x_fitが直線を描く横軸の位置、y_fitがその位置の信号になります。
検量線を完成させる
元データは点、求めた直線は線として、同じグラフに重ねます。
plt.scatter(
concentration,
signal,
label="Data"
)
plt.plot(
x_fit,
y_fit,
label="Linear fit"
)
plt.xlabel("Concentration")
plt.ylabel("Signal")
plt.legend()
plt.show()

Dataが元データ、Linear fitが求めた直線です。6個の点と回帰直線が表示され、傾き・切片・R²を確認できたら、この記事のゴール達成です。
完成コード
ここまでの処理をまとめたコードです。Google Colabの1つのコードセルに貼り付けて実行できます。
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import linregress
concentration = np.array([
0, 1, 2, 3, 4, 5
])
signal = np.array([
0.12, 1.05, 2.08, 2.91, 4.02, 5.10
])
result = linregress(
concentration,
signal
)
r2 = result.rvalue ** 2
x_fit = np.linspace(
concentration.min(),
concentration.max(),
100
)
y_fit = (
result.slope * x_fit
+ result.intercept
)
plt.scatter(
concentration,
signal,
label="Data"
)
plt.plot(
x_fit,
y_fit,
label="Linear fit"
)
plt.xlabel("Concentration")
plt.ylabel("Signal")
plt.legend()
plt.show()
print("slope =", result.slope)
print("intercept =", result.intercept)
print("R^2 =", r2)
グラフの下に、次の数値が表示されます。
slope = 0.9897142857142854
intercept = 0.07238095238095266
R^2 = 0.9984342522980586
まとめ
今回は、説明用の濃度と信号のデータから散布図を描き、linregress()で直線を当てはめました。傾きと切片をy = ax + bに入れると直線の式になり、result.rvalue ** 2でR²を確認できます。
まず元データを見て、計算結果を確かめ、点と直線を重ねて見る。この流れで、Pythonによる検量線作成を試してみてください。
参考・動作確認環境
Google Colabで2026年9月26日に完成コードを実行し、グラフと数値を確認しました。Python 3.13.15/NumPy 2.1.3/Matplotlib 3.10.0/SciPy 1.16.3。追加インストールは不要でした。環境によって表示や数値の末尾が多少異なる場合があります。


コメント