相関を求めて
Table of Contents
Gauche 0.9.13
久し振りにgoshが新版になった。 Gauche A Scheme Implementation 何時ものように下記にて更新。
% gauche-config --reconfigure | sh % make % make install
SRFIが貪欲に出てくるのを見ると、schemeを愛してますって熱狂が伝わってき ますよ。
自己相関 by chatGPT
前回、コスタス行列を調べた時、自己相関なんてのが出てきた。何だっけ? たまには、ChatGPTに質問してみるか。100の説明より、一つのコードだな。世 間で一番使われてる奴でいいだろう。
Pythonで配列 `bldat` の自己相関を求めるには、NumPyというライブラリを使用すると便利です。以下に、NumPyを使用した自己相関を求めるPythonコードの例を示します。
import numpy as np # サンプルのデータ (bldat) を作成 # ここでは仮のデータを使用します。実際のデータに置き換えてください。 bldat = np.random.rand(100) # 自己相関を計算 autocorrelation = np.correlate(bldat, bldat, mode='full') # 自己相関の結果を表示 print("自己相関の結果:") print(autocorrelation) # 自己相関のプロット import matplotlib.pyplot as plt # ゼロラグの位置を取得 zero_lag_index = len(bldat) - 1 # グラフをプロット plt.figure(figsize=(10, 5)) plt.plot(range(-zero_lag_index, zero_lag_index + 1), autocorrelation) plt.xlabel("ラグ") plt.ylabel("自己相関") plt.title("自己相関のプロット") plt.grid() plt.show()
このコードでは、まずNumPyを使って仮のデータを生成し、その後、`np.correlate()` 関数を使用して自己相関を計算しています。自己相関の結果を表示し、さらにMatplotlibを使用して自己相関のプロットを作成しています。
実際のデータを `bldat` に代入してコードを実行することで、そのデータの自己相関を求めることができます。
numpyを使って説明を省略してるな。これが世間の風潮ですか。プロットもサー ビスしときますんで、今後もご贔屓にして下さい、とな。
ならば、このサービスを受容できる様にしとくのが礼儀ってものだろう。 OpenBSDにはvenv環境が無いので、それを作ってからmatplotlibをコンパイル しようと思った。忍者だとか色々必要だと言われたので、諦めた。素直に py3-matplotlibを入れたよ。numpyもつられて入ってきた。まあ、愛用してた gnuplotよりは、豪華な環境になった。
上のコードをそのまま走らせると、盛んにワーニングが出てくる。CJKのカナ・ フォントが入っていないぞと。そして久し振りに豆腐とご対面したよ。グラフ の説明ぐらいなら英語で十分なんだけどな。一般人向けなコードを生成してる んだな。
オイラーみたいに臍曲りは、numpyにサラリと移譲しちゃうのは嫌い。 pure-pythonで提示を要求。
NumPyを使用せずに、Python標準ライブラリのみを使用して、配列 `bldat` の自己相関を計算する方法を示します。以下がそのコードです:
# サンプルのデータ (bldat) を作成 # ここでは仮のデータを使用します。実際のデータに置き換えてください。 bldat = [0.2, 0.5, 0.8, 0.6, 0.3, 0.1, 0.7, 0.4, 0.9, 0.2] # 自己相関を計算する関数を定義 def autocorrelation(data, lag): n = len(data) mean = sum(data) / n c0 = sum((x - mean) ** 2 for x in data) / n num = sum((data[i] - mean) * (data[i + lag] - mean) for i in range(n - lag)) den = sum((x - mean) ** 2 for x in data) return num / den # 自己相関の結果を計算 max_lag = len(bldat) - 1 autocorrelation_values = [autocorrelation(bldat, lag) for lag in range(max_lag + 1)] # 自己相関の結果を表示 print("自己相関の結果:") print(autocorrelation_values)
このコードは、自己相関を計算するための関数 `autocorrelation` を定義し、それを使用して自己相関を計算しています。データの平均値と分散を使用して自己相関を計算し、計算結果をリスト autocorrelation_values
に格納しています。最後に、自己相関の結果を表示します。
実際のデータを `bldat` に代入してコードを実行することで、そのデータの自己相関を求めることができます。
どうもChatGPTって、内包表記が好きみたいだな。その方がコードがhaskellチッ クになって、オイラーも好きであります。
>>> import numpy as np >>> help(np.correlate)
で、豊富なコメントを参照できるんだな。忘れておったわい。
それから、少し高度な説明として、自己相関関数とパワースペクトラム こう いうのが見付かった。
ただの相関値
提案してきたやつを見ると、ただの相関値を計算するやつと似ていそう。
import statistics x = [1, 2, 3, 4, 5] y = [5, 4, 3, 2, 1] correlation = statistics.correlation(x, y) print("corr:", correlation)
関数の中身を引っ張り出してみると、概略ではこうなってた。
def correlation(x, y, /): n = len(x) xbar = fsum(x) / n ybar = fsum(y) / n sxy = fsum((xi - xbar) * (yi - ybar) for xi, yi in zip(x, y)) sxx = fsum((xi - xbar) ** 2.0 for xi in x) syy = fsum((yi - ybar) ** 2.0 for yi in y) return sxy / sqrt(sxx * syy)
自己相関 by R
セカンド・オピニオンでRにも聞いてみる。語源はChatGPTが何気に使っていた 用語。とっかかりが必要だ。
> ??correlation : nlme::corSymm General Correlation Structure nlme::VarCorr Extract variance and correlation components stats::acf Auto- and Cross- Covariance and -Correlation Function Estimation stats::cancor Canonical Correlations stats::cor.test Test for Association/Correlation Between Paired Samples Concepts: Kendall correlation coefficient, Pearson correlation coefficient, Spearman correlation coefficient stats::plot.acf Plot Autocovariance and Autocorrelation Functions stats::var Correlation, Variance and Covariance (Matrices)
色々と案内してくれてるけど、目指すは、stats::acfかな。
> help(acf) Description: The function ‘acf’ computes (and by default plots) estimates of the autocovariance or autocorrelation function. Function ‘pacf’ is the function used for the partial autocorrelations. Function ‘ccf’ computes the cross-correlation or cross-covariance of two univariate series. :
やっぱりソース祭を開催。~/src/R-4.3.1/src/library/stats/R/acf.R
acf <- function (x, lag.max = NULL, type = c("correlation", "covariance", "partial"), plot = TRUE, na.action = na.fail, demean = TRUE, ...) { type <- match.arg(type) if(type == "partial") { m <- match.call() :
気合が入ってるんで、福読本が欲しいなあ。起点は、奥村先生の 所。統計・データ解析
概念的には、自己相関関数入門 かな。
信号処理と相関
で検索すると出てくるのは、ほとんど式の羅列の大学の講義資料ばかり。諦め ないで探していたら、ディジタルデータ処理資料 に出会った。
ソースはMATLABだ。互換のoctaveを使えばいいのか。
plot by matplotlib
そもそもの発端になった記事をトレースしてみる。
import matplotlib.pyplot as plt gol = [0,1,4,13,28,33,47,54,64,70,72] ## Golomb ruler idx = [0,2,9,10,12,22,32,42,52,62,72] ## sync 10 cycle bldat = [0 for x in range(75)] for x in gol: # or idx bldat[x] = 1 def acf(dat, lag): n = len(dat) mean = sum(dat) / n num = sum((dat[i] - mean) * (dat[i + lag] - mean) for i in range(n - lag)) den = sum((x - mean) ** 2 for x in dat) return num / den max_lag = len(bldat) - 1 acf_values = [acf(bldat, lag) for lag in range(max_lag + 1)] plt.figure(figsize=(10, 5)) plt.stem(range( max_lag + 1), acf_values) plt.grid() plt.show()
ゴロム数列(gol)と、ほぼ10置きのインパルス(idx)を(コード上で切り替えて)発生させ、 それの自己相関を表示。ゴロムの方は、どんな組み合わせと取っても、周期が一致す る事はない。従ってlag=0、波形が重なった所でのみ、相関係数が1になり、そ れ以外は(ほぼ)0になる。
一方のインパルスは、ほぼ10おきにインパルスが出てくるので、波形を10シフトしても、そ こでの相関値は高くなる。やっと意味が理解できましたよ。
import matplotlib.pyplot as plt import math import random a = 0.017453292519943295 # pi/180 for degree to radian bldat = [math.sin(3 * a * t) + random.uniform(-1.0, 1.0) for t in range(1024)] def acf(dat, lag): n = len(dat) mean = sum(dat) / n num = sum((dat[i] - mean) * (dat[i + lag] - mean) for i in range(n - lag)) den = sum((x - mean) ** 2 for x in dat) return num / den max_lag = len(bldat) - 1 acf_values = [acf(bldat, lag) for lag in range(max_lag + 1)] fig = plt.figure(figsize=(10, 7)) up = fig.add_subplot(2, 1, 1) # rows cols loc dn = fig.add_subplot(2, 1, 2) up.plot(range( len(bldat) ), bldat) up.grid() dn.plot(range(max_lag + 1 ), acf_values) dn.grid() plt.show() fig.savefig("test.svg")
こちらは、3Hzのサイン波に±1のノイズを重畳させ、その元波形と自己相関を 取ってみたものだ。120度毎に相関値が高くなっている事を確認できる。
matplotlibで複数グラフの描画方法とグラフの保存の例題にもなってる。こう してライブラリィーの利用方法を身に付けていくんだなあ。例題の為の例題よ りかは、よっぽど為になるぞ。
using numpy
上記のpure python使用のコードで、波形サイズを4096に変更すると、結果の グラフが表示されるまで、とんでもなく待たされる。これはもう、numpyを使 うしかないかな。今回は闇に包まれていた自己相関の関数も理解できたから、 ずっと遅い奴で我慢する事は無いだろう。
import matplotlib.pyplot as plt import numpy as np a = 0.017453292519943295 # pi/180 for degree to radian bldat = np.array([np.sin(3 * a * t) + np.random.uniform(-1.0, 1.0) for t in range(4096)]) autocorrelation = np.correlate(bldat, bldat, mode='full') zero_lag_index = len(bldat) - 1 fig = plt.figure(figsize=(10, 7)) up = fig.add_subplot(2, 1, 1) # rows cols loc dn = fig.add_subplot(2, 1, 2) up.plot(range( len(bldat) ), bldat) up.grid() dn.plot(range(-zero_lag_index, zero_lag_index + 1), autocorrelation) dn.grid() #plt.show() #fig.savefig("test.svg")
ほんのちょっと修正しただけ。なお、結果を確かめる部分とファイルに落す部 分は、当然コメントにしてる。
vbox$ time python3 wave.py 0m35.62s real 0m34.60s user 0m00.93s system vbox$ time python3 wave-np.py 0m03.60s real 0m02.49s user 0m00.89s system
結果は1桁違った。これがnumpyの威力です。強いてはpythonが皆に好かれる原 動力になったのは間違いないでしょう。
read/write data for numpy
numpyは高性能な演算をやってくれる事を身をもって体験した。ならば、外部 に用意されてるCSVファイル等のデータをnumpy用に取り込んでこそ、そのワー が発揮されるはず。さて、どうする家康? とっかかりは、これかな。
[NumPy超入門]多次元配列「ndarray」に触ってみよう
CSVファイルも簡単に読み込めるとな。
NumPyでCSVファイルを読み込み・書き込み(入力・出力)
外部に開いててよかった、numpyいい気持。
numpyと心中する気なら、np.save/load()が使える。 NumPy】配列データの書き込み/読み込み
import numpy as np csv = np.loadtxt('am.csv', delimiter=',', dtype='int32', usecols=(0, 1, 2)) hi = csv[:,1]
CSVの場合は、delimiter指定が必須。
emacs calc
emacsに付属の高性能RPN式の電卓。 Vectors as Lists (Tutorial) を見ていたら、外部のデータとやり取り出来る ようだ。これで、ちょいとした演算が可能になるな。
For moving data into and out of Calc: G calc-grab-region. Grab the region defined by mark and point into Calc. R calc-grab-rectangle. Grab the rectangle defined by mark, point into Calc. : calc-grab-sum-down. Grab a rectangle and sum the columns. _ calc-grab-sum-across. Grab a rectangle and sum the rows. Y calc-copy-to-buffer. Copy a value from the stack into the editing buffer.
123 0.22 234 0.33 321 1.55
こんなデータがbufferに有る時、これらをレクタングル指定してから、C-x * r すれば、calcが開いて、データがスタックに取り込まれる。
1: [ [ 123, 0.22 ] | [ 234, 0.33 ] | [ 321, 1.55 ] ]
これは便利だな。後は山程あるコマンドをどう使いこなすかの勝負だ。
corr
勝負は、相関の計算で付けてみる。肝は、2つの配列をスタックに用意する事 だ。
The ‘H u C’ (‘calc-vector-correlation’) [‘vcorr’] command computes the linear correlation coefficient of two vectors. This is defined by the covariance of the vectors divided by the product of their standard deviations.
例として最高、最低血圧の相関を求めてみる。最初の140にカーソルをあわせ て C-space。次に、最後の76, の , にカーソルを合わせる。これで、矩形選 択終了。後は、C-x * r すれば良い。
23090105,140,80,60 : 23093004,143,76,56
v . 1: [ [140, 80], |grab [[119], [131], [118],$ [145, 78], | trn [[119, 131, 118, 131,$ [132, 74], |grab [[117], [139], [145],$ ..., | trn [[117, 139, 145, 130,$ [143, 76] ]
CSVファイルの内容を取り込み。v . で概要を確認。最初と最後をさりげなく 表示してくれるって、小癪な配慮だな。
v t 1: [ [140, 145, 132, ..., 143], |corr 0.386011055468 [80, 78, 74, ..., 76] ] |grab [[140, 80], [145, 78]$ v u 2: [140, 145, 132, ..., 143] | trn [[140, 145, 132, 147,$ 1: [80, 78, 74, ..., 76] |unpk [140, 145, 132, 147, $ H u C 1: 0.100366294405 |unpk [140, 145, 132, 147, $
血圧の組を転置して、最高、最低にまとめる。そしてアンパックして分離。こ れで2つの引数が揃ったので相関を計算。
計算するとスタックからデータが消えてしまう。後々データが必要なら、事前 に t 2 and t 1 して、変数にコピーしとく。再ロードするには、r 2 and r 1 すれ ば良い。
一般統計関数
引数が一つのvector用、統計関数。
The ‘u M’ (‘calc-vector-mean’) The ‘H u M’ (‘calc-vector-median’) The ‘u S’ (‘calc-vector-sdev’) The ‘u X’ (‘calc-vector-max’) The ‘u N’ (‘calc-vector-min’) The ‘u #’ (‘calc-vector-count’) The ‘u +’ (‘calc-vector-sum’)
これらの統計を計算する時、スタックの値をどんどん消費するので、事前に RETURNを何回も叩いて、スタック・トップを複製しておく。統計関数を適用す ると、結果がスタックに積まれる。これは不要になるので(結果が右側の窓に 保存されてる)、BackSpaceを叩いて削除する。
この方法を使えば、どんどん統計値を計算(記録)出来る。便利であります。
see source
emacsのソースはgzファイルなので、zgrepして、関係者を見付け出すのさ。
vbox$ cd /usr/local/share/emacs/29.1/lisp/calc vbox$ zgrep calc-vector-correlation *el.gz calc-ext.el.gz: ("calc-stat" calc-vector-correlation calc-vector-count calc-stat.el.gz:(defun calc-vector-correlation (arg)
("calc-stat" calc-vector-correlation calc-vector-count calc-vector-covariance calc-vector-geometric-mean calc-vector-harmonic-mean calc-vector-max calc-vector-mean calc-vector-mean-error calc-vector-median calc-vector-min calc-vector-pop-covariance calc-vector-pop-sdev calc-vector-pop-variance calc-vector-product calc-vector-rms calc-vector-sdev calc-vector-sum calc-vector-variance)
最初のファイルには、上記の様な関数のカタログやらキーマップ、それと主要 な関数が登録されてた。大事なのは calc-stat.el の方さ。
(defun calc-vector-correlation (arg) (interactive "P") (calc-hyperbolic-func) (calc-vector-covariance arg)) (defun calc-vector-covariance (arg) (interactive "P") (calc-slow-wrapper (let ((n (if (eq arg 1) 1 2))) (if (calc-is-hyperbolic) (calc-enter-result n "corr" (cons 'calcFunc-vcorr (calc-top-list-n n))) (if (calc-is-inverse) (calc-enter-result n "pcov" (cons 'calcFunc-vpcov (calc-top-list-n n))) (calc-enter-result n "cov" (cons 'calcFunc-vcov (calc-top-list-n n)))))))) (defun calcFunc-vpcov (vec1 &optional vec2) (math-covariance (list vec1) (list vec2) t 1)) (defun math-covariance (vec1 vec2 pop mode) (or (car vec2) (= mode 0) (progn (if (and (eq (car-safe (car vec1)) 'var) (eq (car-safe (calc-var-value (nth 2 (car vec1)))) 'vec)) (setq vec1 (symbol-value (nth 2 (car vec1)))) (setq vec1 (car vec1))) :
最後の関数が難関。にわかに参照しても構造を理解していないと歯がたたない。
いきなり難しいのは止めて、calc-vector-sdev ぐらいからスロースタートす ると、宜しあるね。
my血圧
長年、血圧降下薬を朝に服用していたんだけど、その薬効が低下してきて、 時々、高記録が出るようになった。で、2023-10-13 から、夜用も追加しても らった。これで、朝まで安心。
[sakae@deb blood]$ newstats 231014 mode(#) median mean N from to hi@am 116(3) 119.0 121.27 18 231014 231031 hi@pm 124(3) 127.0 128.38 18 231014 231031 lo@am 71(4) 67.0 66.61 18 lo@pm 68(4) 68.0 68.05 18 [sakae@deb blood]$ newstats 2307 mode(#) median mean N from to hi@am 138(8) 140.0 139.71 100 230701 231008 hi@pm 131(7) 131.0 129.63 100 230701 231008 lo@am 74(13) 74.0 73.2 100 lo@pm 68(14) 67.0 66.41 100