tenoto/lecture_fitting @ GitHub
lecture
python
高エネルギー天文学で使われる HEASoft や素粒子・原子核実験で使われる ROOT などの専用解析ツールを使わず、データ解析を行いたい場合もあります。Python だけで放射線データの基本的な解析をする手順を記載します。
まず、実際の測定データを扱う前に、疑似データを乱数から生成して遊んでみましょう。
この一連のページでは、Python での解析と実例を紹介します。コードは Google Colaboratory に一部置いています。Google Colaboratory では標準で多くのライブラリをもっていますが、足りないものは !pip install xxx
で使えるようにできます。
以下のすべてを Google Colab には置いていないので、注意してください。後半では、GitHub にコードを置いているで、Google Colab でも GitHub でも使いやすい方を試してください
乱数はいくつかのライブラリで扱えますが、様々な目的で使える numpy で呼ぶのが使いやすいことが多いです。
ここで、seed(0)
は乱数のシード設定、randn(1000)
は標準正規分布(平均 0, 標準偏差 1)に従う乱数を 1000 個、生成して numpy の array に収容しています。この array は、素粒子物理学やX線天文学などで、放射線イベントの何らかの物理量の列に対応する、というイメージで evt という変数名にしてあります。
生成された数値データをヒストグラムに詰めてプロットするには、numpy.histogram
が使えます。ヒストグラムの上限、下限とその間のビン数を指定するのは、ROOT (もっと古い古代語だと display45, pow?)などと同じにできます。
hy はヒストグラムに詰められた値で、xedges はヒストグラムの境界の値のため、ヒストグラムの中心値は hx = 0.5*(xedges[1:]+xedges[:-1])
で計算して hx に詰めておく必要があります。統計誤差は、各ヒストグラムの平方根で hyerr に詰めています。
(注意) 実は、天文学で扱うことがある小統計の統計誤差について、平方根を使うのには注意が必要です。Gehrels "N.Confidence Limits for Small Numbers of Events in Astrophysical Data", ApJ, 303, 336 (1986) や、HEASoft の mathpha のコード中での取り扱い Ian "The MATHPHA User’s Guide" (1995) の "3 CALCULATION & PROGATION OF ERRORS" などが参考になります。
これらを用いて、
とすれば、
のようにプロットできます。marker='', drawstyle='steps-mid'
のオプションにすることで、標準的な物理での誤差付きヒストグラムの表現にしています。
上のヒストグラムは、標準正規分布から生成しているので、十分に統計を溜めれば、平均 0 で、標準偏差 1 のフィット結果に収束するはずです。
素粒子物理学で蓄積されてきたフィット(より正確には、評価関数の最小化)のアルゴリズムは、Minuit のライブラリになっており、python では、iminuit という名前で、一般的な関数最小化法として使用できます。これは、モデルのデータへの尤度フィットや、尤度プロファイル分析から、モデルのパラメータ誤差推定値を得るためにも使用できます。iminuit チュートリアルが参考になります。
さらに、probfit で iminuit で使いたい標準的な関数や、尤度関数、Chi-square 計算などを呼べるようにします。
gaussian 関数を、Extended することで規格化因子を付けています。その上で、BinnedChi2 でガウス関数を生成したデータに適用したときの評価関数をつくり、Minuit で最小化できるように準備します。この際、初期値を入れています。m.migrad()
がこの評価関数の最小値を探してくる本体です。
最小値を求めた後、以下のコマンドで非対称な誤差を計算します。
これらの誤差は、以下で表示できます。
フィットした後のパラメータや誤差を取り出すには、
などとすればよいです。上記のフィットの結果から、中心値は なので、の範囲で最初の乱数生成の仮定と一致し、幅については との範囲で一致していることがわかります。
なお、今回は BinnedChi2
を使いましたが、他にも色々あります(詳細は別途)。
では、ここから実際に、測定データで試してみます。
まず、必要なライブラリを読み込んでおきます。
最後の iminuit と probfit が今回の放射線計測のために重要で、これらのライブラリは、pip install xxx
(xxx はライブラリ名)などで準備してください。
もし Google Colaboratory でデータアップロードする場合、
でアップロードできます。
data ディレクトリ下に置いてあるdata/201109_id30_137cs_15min.csv
という CSV ファイルは、Time (sec) と ADC channel (pha) の列になっています。
python のライブラリ pandas には csv 形式を読める read_csv が用意されています。詳しくは、仕様を読んでもらえれば良いですが、コラム名はファイル内には含まれていないので、index_col=False, names=['sec','pha']
で指定しています。
また、dtype
でコラムのフォーマットを指定しています。sec
の方は、誤ったフォーマットにすると数値がずれてしまうので注意。
読み込んだものは、data frame (df) に取り込まれます。
matplotlib の hist を使って、ヒストグラムを描画できます。ヒストグラムは上限、下限とビン数を指定し、表示方法を指定できます。今回、ビン数は 2^9 にしています。これは、ビンまとめするときに、2の乗数だと便利なことが多いからです。別に整数なら何でも構いません。
この図で、上の青は 137Cs、下のオレンジは環境放射線(何も放射線源を当てていない)の場合です。放射線を当てた方が、150 cps ほど増えていることがわかります。
同じことを numpy でもできます。この際、yerr で誤差をつけてみました。
今回の測定では、1秒でもそれなりの統計がありますが、例えば 8 秒でライトカーブが描きたい場合は以下のようになります。
最初のプロットでは、8 秒ビンごとの個数が表示されているので、1秒あたりに換算しています
今回の測定は時間的に一定で、平均値は 426.5 counts/s です。np.mean(lc_src_y)
とかやると計算できます。では、1秒値のライトカーブのレートがどの程度、分散があるのか調べてみます。これは、1秒値のレートの array をヒストグラムに詰めることになります。
となります。では、これをガウス関数でフィットしてみましょう。
mygaus という関数を定義し、フィットの初期値を探すために、測定データを再現できるようなモデルを表示してみます。
そこそこ良さそうな初期値を見つけたら、Chi-square で最適化してみます。
みたいになります。
とすると、フィットで得られたパラメータとその誤差を表示できます。
ガウス関数でフィットした中心値は 426.1+/-0.2 counts/sec ということで、最初に出した平均値 426.5 counts/s と の範囲で一致しています。
また、ガウス関数の幅 20+/-0.2 counts/sec ですが、sqrt(426)~20.6 counts/sec に(当たり前ですが)一致します。もし、この測定が統計的なバラツキ以外に変動をもつなら、レート平均値の平方根よりも幅が広がります. 今回の測定では、統計的な変動だけであることがわかります。
時系列データはいろいろな解析ができます。連続して観測されるイベント間の到来時刻差(delta-t distribution)は、np.diff(df_137cs['sec'])
で得ることができます。
これを片対数プロットに描くと、次のように直線になります。つまり、指数関数になります。137Cs の放射線の崩壊はランダムで、到来時間差は指数関数になります。
これは次のコードで描画し、指数関数でフィットできます。
さて、この指数関数のパラメータは、
ということで、 の ですが、 counts/sec とレートに対応することも確認できます。この delta-t distibution は本来ランダムな信号を検出器が正しく測定できているかなどの評価に使えます。
ちなみに、data frame を読み込むときの sec
のフォーマットを間違うと、この delta-t ditribution は崩れることに注意してください。
次に放射線のスペクトルを描いてみます。今回は、統計が少し悪く誤差が見えるデータの方が解説しやすいので、50 秒のデータでまずは描いてみましょう。
青が 137Cs を照射した場合で、オレンジ色が環境バックグラウンドの測定です。ADC channel で 60 以上の箇所は、バックグラウンドと137Cs は一致しています。
測定時間で割り算して(縦軸を counts から counts/sec にする)、バックグラウンドを引いたデータについて、662 keV の輝線をガウス関数でフィットしてみます。
今回のモデルでは、ガウス関数に加えて1次関数も加えています。図にはデータ点と、ベストフィットのモデル関数(赤)を示していて、下のパネルには残差を示しています。
最適のモデルパラメータは、
ということで、ガウス関数のピークは 50.464+/-0.076 で、幅は Sigma=1.927+/-0.085、このピークのレートは 99.1+/-4.4 cps とわかります。
最初のライトカーブはエネルギーなどで選別はしていませんでしたが、137Cs の 662 keV をガウス関数で近似した場合のピークと幅が求まったので、3 に入るイベントだけを抜き出した時間変動も描けるようになりました。
このライトカーブでは、137Cs の 662 keV 輝線のレートから、同じエネルギー帯域(ADC channel)のバックグラウンドのレートの平均値を引いています。
になります。ちなみに、スペクトルフィットでガウス関数と一次関数の和でモデル化したときのガウス関数のエリア(99 cps) と比べると 8 cps ほど多いのは、コンプトン等の成分が入っているから??でしょうか。ここは未確認です。
137Cs 同様に、60Co でのフィットを行うと以下のようになる。
今回は、2つのガウス関数と連続成分は2次関数を仮定している。
さて、3本のラインでフィットができたので、Energy (keV) と ADC channel の相関を見てみる。
Ion | Energy (keV) | ADC channel | ADC error |
---|---|---|---|
137Cs | 661.65 | 50.46 | 0.08 |
60Co | 1173.23 | 89.90 | 0.05 |
60Co | 1332.50 | 102.27 | 0.06 |
となるので、
という関係にあることがわかる。2^10=1024 channel は約 13 MeV に対応することがわかる。
最後に、ADC channel をエネルギーに変換できるようになったので、X軸をエネルギー、Y軸を Counts/bin ではなく、Counts/sec/keV の次元にそろえてみる。
などとなる。
今回は、順を追って理解できるように、クラス化したり、メソッド(関数)化したりはしていません。使ってくると、同じ操作はまとめたくなります。
たとえば、上記のヒストグラムに詰める操作は、以下のようなクラスを定義すると、ROOT っぽい操作をすることもできます。
を定義して、
なお、ここでの du_clevt['EVENTS'].data['PI']
はイベントの array です。FITS 形式の説明のあとで使えるようになります。