（神経科学で学ぶ）matlab 初心者向け講座 STA編 1/2

# はじめに
この講座ではmatlab初心者向けに解析で使ういくつかの機能について、神経科学に関係する内容に触れながらその使い方を解説します。[^1-1]

[^1-1]: 神経科学と言いましたが、今回の話が関係するのはそのごくごく小さなシステム神経科学という分野でのお話になります。もともと研究室向けに書いていた記事になりますのでご了承ください。

今回はSpike Triggered Average(STA)による受容野の測定を取り上げます。その過程で簡単な視覚野の神経細胞のモデルの作成、仮想視覚刺激呈示によるスパイク列の生成などを行うことで、matlabの基本的な機能に触れていきたいと思います。
長くなったので[前半][前半]と[後半][後半]に分けています。


matlabを全く触ったことの無い人はまず、[「MATLAB入門」][]から始めましょう。matlabが用意している関数にはできるだけ、mathworks社のサイトへリンクを貼っています[「関数 - カテゴリ別」][]。公式のドキュメントが整っているという点がmatlabの長所の1つです。使い方の分からない関数は[`doc`][]または[`help`][]を使うことで調べることができます。

``` matlab
% Reference page in Help browser
doc doc;

% Help for functions in Command Window
help help
```

実装例は整理した後で、githubにでもあげる予定です。[^1-2]

[^1-2]: 大した量にはならないと思います。コード部分を順番にコピペして実行できるようになると思います。エラー等が発生しましたら教えてください。

## Spike Triggered Average(STA)とは
視覚野の神経細胞の受容野を測定する手法の1つです。

この講座では、matlabの基本的な使い方の解説に主眼を置いています。受容野やSTAについて厳密な議論はせず、できるけ単純なモデルを使った説明を努めます。ざっくりとした概要を理解したい方は下記の阪大・大澤先生のページを参考にしてください
[「受容野の定量的測定法」][Ohzawa-STA]。[^1.1-1]

[^1.1-1]: 詳しく勉強したい方は以下のレビューがフリーでアクセスできて周辺も話題も含めて俯瞰できるように思います。
  > Meyer AF, Williamson RS, Linden JF, Sahani M. (2016)
  > Models of Neuronal Stimulus-Response Functions: Elaboration, Estimation, and Evaluation.
  > Front Syst Neurosci 10:109.
  > https://dx.doi.org/10.3389/fnsys.2016.00109
  > PMID: 28127278

## 2次元データの可視化
モデルの実装に入る前にmatlabの練習を1つ、おこないましょう。

matlabのデータの1番基本になるものはその名の通り行列です。行列形式で与えられたデータを可視化する方法を試してみましょう。使用するデータは何でも良いのですが、ここでは[`rand`][]関数で一様乱数を生成します。
[`figure`][]で新規ウィンドウを生成し、[`imagesc`][]でデータを表示します。[^1.2-1]

[^1.2-1]: matlabには行列を表示する関数として[`imshow`][]、[`image`][]、[`imagesc`][]の３つが用意されています。[`imagesc`][]は自動的に行列の値の最大・最小値をもとに色の配置を決めます。とりあえず行列の中身を見たいときはこれがお勧めです。描画設定をいじれば細かい見た目は変更できるので好みのものを使ってください。

``` matlab
% Create 4x4 matrix by random
M = rand(64);

% Create new figure
figure();

% Display matrix M
imagesc(M);
```

![Fig1_1.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/76c980fd-572c-badb-56e9-da40f425767d.png)


座標軸の調節には[`axis`][]を使います。ここではアスペクト比の調節`image`, 軸の不可視化`off`オプションを用います。データを描画する際に不必要な情報はできるだけ削りましょう。

``` matlab
% Adjust the aspect ratio and hide the axis
axis image off;
```

![Fig1_2.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/23cb96b7-190f-6eec-9eb6-660498cf1849.png)

デフォルトのカラーマップは[`parula`][] が指定されています。[^1.2-2]  
![parula](https://jp.mathworks.com/help/matlab/ref/colormap_parula_update17a_ja_JP.png)

[^1.2-2]: このカラーマップの利点は以下の記事を読んでください。
  * [A New Colormap for MATLAB – Part 1 – Introduction](https://blogs.mathworks.com/steve/2014/10/13/a-new-colormap-for-matlab-part-1-introduction/)
  * [A New Colormap for MATLAB – Part 2 – Troubles with Rainbows](https://blogs.mathworks.com/steve/2014/10/20/a-new-colormap-for-matlab-part-2-troubles-with-rainbows/)
  * [A New Colormap for MATLAB – Part 3 – Some Reactions](https://blogs.mathworks.com/steve/2014/11/12/a-new-colormap-for-matlab-part-3-some-reactions/)
  * [A New Colormap for MATLAB – Part 4 – The Name](https://blogs.mathworks.com/steve/2014/12/10/a-new-colormap-for-matlab-part-4-the-name/)

カラーマップは[`colormap`][]で変更することができます。例えば黒から白へ変化するグレースケールは[`gray`][]で使用することができます。

![gray](https://jp.mathworks.com/help/matlab/ref/colormap_gray_ja_JP.png)

``` matlab
% Change the colormap to gray
colormap gray
```

![Fig1_3.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/0c4fe56a-1167-0e1f-2a49-286a1666a6bf.png)

colormapの色と値の対応は[`colorbar`][]を使うことで確認できます。

``` matlab
% Display colorbar
colorbar;
```

![Fig1_4.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/db526714-51e4-0e58-2fd2-eb64e0f39c84.png)


# スクリプトファイルの作成
Gaborフィルタの計算を題材にスクリプトファイルを作成してみましょう。

コマンドラインから[`edit`][]コマンドを用いて以下を実行します。ここでは`script2DGabor`という名前のファイルを作成していますが、各自で好きな名前を付けても構いません。このような形式で保存されたファイルはスクリプトファイルと呼ばれます[「プログラミングとスクリプト」][]。一連の手続きをスクリプトファイルに記述し、保存しておくことで再び同じ処理を実行することができます。

``` matlab
% Create a script file
edit script2DGabor
```

今回は簡単のため、時間方向は無視して、空間方向（XY平面）のみで構成される受容野を考えます。正弦波とガウシアンの掛け合わせで表現されるGaborフィルタを仮定します。

## 格子点行列の生成
まずは古典的に[`meshgrid`][]を使って格子点行列を生成します。[^2.1-1]

[^2.1-1]: 暗黙的な拡張を利用すれば、格子点行列を作る必要はなくなります。可読性が悪くなるためここでは素直に[`meshgrid`][]を利用します。

[`linspace`][]を使って`[-1 ~ 1]`のベクトルを入力として与えます。出力`X`, `Y`はそれぞれの軸の格子点の値に対応します。[`subplot`][]は1つのウィンドウに複数の描画を並べる際に活躍します。

``` matlab
% Create X, Y grid by [-1 ~ 1] N=64 vector
[X, Y] = meshgrid(linspace(-1, 1, 64));

figure();

subplot(2, 3, 1);
imagesc(X);
axis image off;

subplot(2, 3, 2);
imagesc(Y);
axis image off;

```

![Fig2_1.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/a8862b17-ebb3-856a-f2c4-98d25386818a.png)

鋭い人なら気が付いたかもしれませんが、Yの増加する方向が上下逆転していると思います。matlabのimage表示関数はデータを上から下へと描画するように設定されています。[^2.1-2]

[^2.1-2]: これは画像データがコンピュータ上でどのように扱われているのかに関係します。表示の向きを変えるには以下のような方法があります。
  * `axis xy`オプションを使う[「axis y 軸の方向」](https://jp.mathworks.com/help/matlab/ref/axis.html#buk989s-1-ydirection)
  * AxesオブジェクトのYDirプロパティを`'normal'`に設定する[「Axes のプロパティ」](https://jp.mathworks.com/help/matlab/ref/matlab.graphics.axis.axes-properties.html#budumk7-XDir)
  * [`flipud`][]で行列の上下を逆転する
  * 気にせず他の場所で都合を合わせる。

## 座標軸の回転
方位を定義して座標軸を回転します。

Gaborフィルタの重要なパラメータの1つに方位`OR`があります。方位の定義（0度の定義, 増加する回転方向）は研究グループによってバラバラです。先ほど生成した格子点に対して回転ベクトルをかけて座標軸を回転させます。ここでは縦じまを0度とし、方位の値が大きくなるにつれ、時計周りに回転するように定義します。

円周率の値として[`pi`][]を使用します。matlabではいくつかの定数が定義されています。これらの値は上書きできます。同じ名前の変数は定義しないようにしましょう[「定数とテスト行列」][]。

ここで注意しなければならないのはmatlabでの演算の扱いです。matlabは線形代数の規則に従う演算（行列演算: `A*B`）と要素単位で行う演算（配列演算: `A.*B`）を区別します[「配列と行列の演算」][]。matlabでは、いわゆる「暗黙的な拡張」によりエラーが発生せずに、意図しない結果が得られることがあります[「基本的な演算で互換性のある配列サイズ」][]。今回、ＸＹ軸の格子点データ`X`, `Y`に対して回転ベクトルの係数（スカラー）`cos(OR)`, `sin(OR)`を掛ける際、配列演算であることを強調するために`*`ではなくあえて`.*`を用います。[^2.2-1]

[^2.2-1]: 非常に細かいところですが、ここで実装しているのは点の回転移動ではなく座標軸の回転です。
  回転後の座標軸として用いている変数`A`は以下の$X'$に相当します。
  これを取り違えると回転の方向が逆転します。

  座標軸の回転

  ``` math
  \begin{pmatrix}
  X' \\
  Y'
  \end{pmatrix}
  = 
  \begin{pmatrix}
  cos{\theta} & sin{\theta} \\
  -sin{\theta} & cos{\theta}
  \end{pmatrix}
  \begin{pmatrix}
  X \\
  Y
  \end{pmatrix}
  ```

  点の回転移動

  ``` math
  \begin{pmatrix}
  x' \\
  y'
  \end{pmatrix}
  = 
  \begin{pmatrix}
  cos{\theta} & -sin{\theta} \\
  sin{\theta} & cos{\theta}
  \end{pmatrix}
  \begin{pmatrix}
  x \\
  y
  \end{pmatrix}
  ```

``` matlab
% Define OR: orientation (30 degree)
OR = pi/6;

% Rotated axis
A = cos(OR).*X + sin(OR).*Y;

subplot(2, 3, 3);
imagesc(A);
axis image off;

```

![Fig2_2.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/5738c2a9-c701-fe3f-5025-81fc55ff5c20.png)

## 2次元の正弦波の生成
座標軸の回転が定義出来たら三角関数[`cos`][]を適用して正弦波を作成します。

matlabの関数のほとんどは行列の入力に対応しており、たいていの場合、行列の各要素に演算を適用した結果を返します。パラメータとして空間周波数`SF`を定義しましょう。

``` matlab
% Define SF: spatial-frequency (2 cycle per image)
SF = 2;

% Calculate Sinusoidal wave
Sinus = cos(2*pi*SF.*A);

subplot(2, 3, 4);
imagesc(Sinus);
axis image off;

```

![Fig2_3.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/2606388e-70dc-ff13-45f9-58b6c131db58.png)


## 2次元のガウシアンの生成
受容野は空間的に限局します。ガウシアンを用いることでその様子を近似します。

まず関数[`hypot`][]を使って中心からの距離を定義する行列`R`を定めます。正規分布関数の定義では、係数 $ \frac{1}{\sqrt{2\pi\sigma^2}} $ を掛けますが、ここでは省きます。

``` matlab
% Define SG: spatial envelope (1/4 of image)
SG = 1/2;

% Radius from center
R = hypot(X, Y);

% Calculate Gaussian envelope
Gauss = exp( -R.^2./(2*SG^2) );

subplot(2, 3, 5);
imagesc(Gauss);
axis image off;

```

![Fig2_4.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/26eebd00-2101-73e2-4684-a05c73ccb044.png)

## Gaborフィルタの生成
2つの行列を要素ごとに掛け合わせ、Gaborフィルタを生成します。

先ほども述べた通り、matlabでの行列演算と配列演算の違いに注意しましょう。特に今回の場合、同じサイズの正方行列を利用するため、どちらの書き方でもエラーは出ませんが、異なる計算結果が出力されます。  

``` matlab
% Calculate Gabor-filter by multiplication of Sinusoidal wave and Gaussian
Gabor = Sinus.*Gauss;

subplot(2, 3, 6);
imagesc(Gabor);
axis image off;

```

![Fig2_5.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/dc8fcf83-e0db-1fc6-51bc-14b7d050944d.png)

## スクリプトファイルの実行
一度ワークスペースを空にした後スクリプトファイルを実行してみましょう。

ワークスペースは[`clear`][]を使うことでリセットできます。
また、ワークスペース上の変数を表示するには[`whos`][]を利用します。

```matlab
% Remove all the items from workspace
clear all;

% excute scriptfile
script2DGabor;

% List variables in workspace, with sizes and types
whos;
```

# 関数ファイルの作成
関数ファイルの作成することで、処理を引数ともに実行できます。

初期視覚野神経細胞のモデルとしてGaborフィルタを定義したわけですが、パラメータとして設定した方位`OR`や空間周波数`SF`などは個々の細胞ごとに異なります。これらのパラメータを**入力引数**として持ち、Gaborフィルタを**出力**するような関数を考えます。

先ほど作成したGaborフィルタを計算するスクリプトファイルをもとに順に関数を作成していきます。

[`function`][]キーワードを使い、ファイル名を関数名と一致させることで、関数ファイルを作成することができます。ここでは関数名、ファイル名共に`make2DGabor`を使用します。

コマンドライン上で以下を実行し、新規ファイルを作成します。

``` matlab
%Create function file
edit make2DGabor
```

ファイルの拡張子からはスクリプトファイルと関数ファイルの違いはわかりません。
関数ファイルでは以下のように１番初めの実行可能行で[`function`][]キーワードを用いて関数の宣言を行う必要があります。

``` matlab:make2DGabor.m
function Gabor = make2DGabor ...
```

## 入力引数を整理
はじめに入力引数を整理しましょう。

先ほどのスクリプトファイルをよく読んでどの変数を入力引数にすべきかを考えます。ここでは追加として、出力するフィルタのサイズ`SZ`, 受容野の中心位置を定めるパラメータ`CX`, `CY`, フィルタの位相`PH`を追加しておきます。

``` matlab:make2DGabor.m
function Gabor = make2DGabor(SZ, CX, CY, OR, SF, SG, PH) ...
```

## ヘルプテキストの作成
[`function`][]のすぐ下にコメントを挿入することで、ヘルプテキストを作成することができます。

ヘルプテキストは[`help`][]を使用すると参照できます。

``` matlab
help make2DGabor
```

関数の動作、入力引数・出力引数の説明などを記述しておきます。

``` matlab:make2DGabor.m
function Gabor = make2DGabor(SZ, CX, CY, OR, SF, SG, PH)
% make2DGabor make 2D Gabor filter
%   Gabor = make2DGabor(SZ, CX, CY, OR, SF, SG, PH)
%   SZ    : Size of output filter
%   CX    : Center position X
%   CY    : Center position Y
%   OR    : Orientation
%   SF    : Spatial-Frequency
%   SG    : Spatial envelope
%   PH    : Phase
%   
%   To visualize with imagesc function, use below option
%   axis image off
...
```

## 関数の実装
先ほど作成したスクリプトファイルの内容を元に関数の処理を実装していきます。

出力引数と同名の変数を定義し、そこに値を代入することで、関数の出力を得ることができます。(ここでは変数`Gabor`が出力引数になります)

今回はファイルの先頭から処理を記述していきましたが、関数を実装していく中で、新しい入力引数が必要になることは多々あります。先に実装の見通しを立てておくことは役立ちますが、必ずしもこの順番で関数を作っていく必要はありません。

``` matlab:make2DGabor.m
function Gabor = make2DGabor(SZ, CX, CY, OR, SF, SG, PH)
% make2DGabor make 2D Gabor filter
%   Gabor = make2DGabor(SZ, CX, CY, OR, SF, SG, PH)
%   SZ    : Size of output filter
%   CX    : Center position X
%   CY    : Center position Y
%   OR    : Orientation
%   SF    : Spatial-Frequency
%   SG    : Spatial envelope
%   PH    : Phase
%   
%   To visualize with imagesc function, use below option
%   axis image off

% Create X, Y grid by [-1 ~ 1] axis vector
[X, Y] = meshgrid(linspace(-1, 1, SZ));

% Rotated axis
A = cos(OR).*(X-CX) + sin(OR).*(Y-CY);

% Calculate Sinusoidal wave
Sinus = cos(2*pi*SF.*A - PH);

% Radius from center
R = hypot(X-CX, Y-CY);

% Calculate Gaussian envelope
Gauss = exp( -R.^2./(2*SG^2) );

% Calculate Gabor-filter by multiplication of Sinusoidal wave and Gaussian envelope
Gabor = Sinus.*Gauss;
```

## 様々なパラメータを持つGaborフィルタの可視化
関数を使用することで、異なるパラメータを持つGaborフィルタを簡単に生成することができます。

自分の実装した関数の挙動が正しいか、それぞれの引数に適当な値を入れて確かめましょう。

``` matlab
figure();
colormap parula(1024);

subplot(2, 3, 1);
imagesc(make2DGabor(64, 0, 0, 0, 2, 1/4, 0));
axis image off;
caxis([-1, 1]);

subplot(2, 3, 2);
imagesc(make2DGabor(128, 0.25, 0, 0, 2, 1/4, 0));
axis image off;
caxis([-1, 1]);

subplot(2, 3, 3);
imagesc(make2DGabor(128, 0.25, 2/3, 0, 2, 1/4, 0));
axis image off;
caxis([-1, 1]);

subplot(2, 3, 4);
imagesc(make2DGabor(128, 0.25, 2/3, pi/4, 2, 1/4, 0));
axis image off;
caxis([-1, 1]);

subplot(2, 3, 5);
imagesc(make2DGabor(128, 0.25, 2/3, pi/4, 4, 1/4, 0));
axis image off;
caxis([-1, 1]);

subplot(2, 3, 6);
imagesc(make2DGabor(128, 0.25, 2/3, pi/4, 4, 1/8, 0));
axis image off;
caxis([-1, 1]);
```

![Fig3_1.PNG](https://qiita-image-store.s3.amazonaws.com/0/159851/8b045f0d-fc97-94c8-248e-f3698cc30fcc.png)

****

[後半へ続く][後半]

[前半]: https://qiita.com/gkhtnk/items/22400e46a9983a4e54dd
[後半]: https://qiita.com/gkhtnk/items/dd154cc641cc35d7d4bc

[Ohzawa-STA]: http://ohzawa-lab.bpe.es.osaka-u.ac.jp/ohzawa-lab/rf/method.htm

[「MATLAB入門」]: https://jp.mathworks.com/help/matlab/getting-started-with-matlab.html
[「関数 - カテゴリ別」]: https://jp.mathworks.com/help/matlab/functionlist.html
[「プログラミングとスクリプト」]: https://jp.mathworks.com/help/matlab/learn_matlab/scripts.html
[「定数とテスト行列」]: https://jp.mathworks.com/help/matlab/constants-and-test-matrices.html
[「配列と行列の演算」]: https://jp.mathworks.com/help/matlab/matlab_prog/array-vs-matrix-operations.html
[「基本的な演算で互換性のある配列サイズ」]: https://jp.mathworks.com/help/matlab/matlab_prog/compatible-array-sizes-for-basic-operations.html

[`doc`]: https://jp.mathworks.com/help/matlab/ref/doc.html
[`help`]: https://jp.mathworks.com/help/matlab/ref/help.html
[`rand`]: https://jp.mathworks.com/help/matlab/ref/rand.html
[`figure`]: https://jp.mathworks.com/help/matlab/ref/figure.html
[`imagesc`]: https://jp.mathworks.com/help/matlab/ref/imagesc.html
[`imshow`]: https://jp.mathworks.com/help/matlab/ref/imshow.html
[`image`]: https://jp.mathworks.com/help/matlab/ref/image.html
[`axis`]: https://jp.mathworks.com/help/matlab/ref/axis.html
[`parula`]: https://jp.mathworks.com/help/matlab/ref/parula.html
[`colormap`]: https://jp.mathworks.com/help/matlab/ref/colormap.html
[`gray`]: https://jp.mathworks.com/help/matlab/ref/gray.html
[`colorbar`]: https://jp.mathworks.com/help/matlab/ref/colorbar.html
[`edit`]: https://jp.mathworks.com/help/matlab/ref/edit.html
[`meshgrid`]: https://jp.mathworks.com/help/matlab/ref/meshgrid.html
[`linspace`]: https://jp.mathworks.com/help/matlab/ref/linspace.html
[`subplot`]: https://jp.mathworks.com/help/matlab/ref/subplot.html
[`pi`]: https://jp.mathworks.com/help/matlab/ref/pi.html
[`cos`]: https://jp.mathworks.com/help/matlab/ref/cos.html
[`hypot`]: https://jp.mathworks.com/help/matlab/ref/hypot.html
[`clear`]: https://jp.mathworks.com/help/matlab/ref/clear.html
[`whos`]: https://jp.mathworks.com/help/matlab/ref/whos.html
[`function`]: https://jp.mathworks.com/help/matlab/ref/function.html
[`flipud`]: https://jp.mathworks.com/help/matlab/ref/flipud.html