MATLAB使いなら論理インデックスですっきり処理しよう

別垢で社内向けに非公開記事で書いてたんですけど
もう転職する気しかないし、別に外に出てほしくない情報とかよく考えたら書いてないので、個人垢で公開記事にしますシリーズ

そもそも社内で読んでる人たぶんいなかった

# MATLABを知らないけど興味本位でページを開いた人に対する注意

**MATLABの配列インデックスは
"１"
から始まる……** 

# for文めんどくせえ

次のような配列があるとします。

```Matlab
>> array = [2 4 6 8 10 12 14 16];
```

配列arrayが8になるときのインデックスを検索するにはどうしたらよいでしょうか？ 方法の一つとしてはforループを回すことが考えられます。

```Matlab
 for i = 1 : length(array)
    if array(i) == 8
        disp(i);
    end
 end
```

しかしforループの実行は逐次処理であり、動作が遅く、コード文も長くなります。と言うには別にちょっとしたfor文ならどうってことないですが……

どちらかというとこっちの方が問題かな：

for文による処理は **「コードが実現しようとしていること」ではなくて「実現しようとしていることの為に行っている処理」を読むことになるので理解に時間がかかります。**

例えば上の例では
`for i = 1 : length(array)`でarrayの要素数ごとにイテレータを回すこと
`if array(i) == 8`でarrayのうち要素の内容が8であったものの場合にのみ条件文を通ること
`disp(i)`で条件文を通った内容を持つ要素のインデックス番号を表示すること。
といった風に一行ずつ解読することで、
arrayの中身が8になるときのインデックスを確認するコードであることがやっとわかります。

「配列arrayが8になるときのインデックス」

これを**文章と同じだけ簡潔にコード上で表現できるようにしましょう**

# 倫理インデックスの使い方

## 配列の要素ごとに真偽値を得る

論理インデックスを使用すれば次の答えが返ってきます。

```Matlab
>> array = [2 4 6 8 10 12 14 16];
>> array == 8

ans =

     0    0    0    1    0    0    0    0
```

findをつければインデックス値が返ってきます

```Matlab
>> array = [2 4 6 8 10 12 14 16];
>> find(array == 8)

ans =

     4
```

`find(array == 8)`とすることで、
一行で「arrayのうち要素が8であるときのインデックスを確認する」という
コードの意味が読み取れるようになりました。

また、複数の回答がある場合は複数の該当するインデックス番号を確認できます。

```Matlab
>> array = [2 4 6 8 10 12 14 16];
>> array > 5

ans =

     0    0    1    1    1    1    1   1

>> find(array > 5)

ans =

     3    4    5    6    7    8
```

## 論理インデックスの動作速度を比較する

論理インデックスは並列処理で実行されるため、動作速度も圧倒的に早いです。
Matlabのprofile機能を利用してfor文との実行速度の違いを検証してみました。

```Matlab:Test.m
function Test
    array = 1 : 1000000;
    profile on
    execute(array)
    profile off
    profile viewer
end

function execute(array)
    for i = 1 : length(array)
        if rem(array(i),9) == 0
            disp(i)
        end
    end
end
```

for文を利用した上の例では、execute関数の実行速度は**3.664 s**でした。

```Matlab:Test.m
function Test
    array = 1 : 1000000;
    profile on
    execute(array)
    profile off
    profile viewer
end

function execute(array)
    disp( array( rem(array,9) == 0 ) )
end
```

論理インデックスを利用した場合、**0.386 s**で処理が終了しました。


## 条件に合致する配列の要素をまとめて処理する

複数インデックスはまとめて処理することもできます。 配列arrayのうち値が5を超える要素を削除してみましょう。

```Matlab
>> array = [2 4 6 8 10 12 14 16];
>> array(array > 5) == [];
>> array

ans =

     2    4
```

findで返ってきたインデックス値の配列を使っても同じことが出来ます。

```Matlab
>> array = [2 4 6 8 10 12 14 16];
>> array(find(array > 5)) == [];
>> array

ans =

     2    4
```

## 昇順・降順での検索

findの第二引数に数字を与えると何番目に見つけたインデックスまでを返すか指定できます。

```Matlab
>> array = [2 2 4 4 4 4 8 8];
>> find(array == 4, 2)

ans =

     3    4
```

最後に見つけたインデックスの値が欲しいのであれば end を使いましょう

```Matlab
>> array = [2 2 4 4 4 4 8 8];
>> index = find(array == 4)
>> index

ans =

     3    4    5    6

>> index(end)

ans =

     6
```

降順に検索することも可能です

```Matlab
>> array = [2 2 4 4 4 4 8 8];
>> find(array == 4, 2, 'last')

ans =

     5    6
```

## 配列のいずれかが/すべてが要素を含むとき

配列のいずれかが任意の値を含むか確認したいときは any を使いましょう

```Matlab
>> array = [2 2 4 4 4 4 8 8];
>> any(array == 8)

ans =

     1
```

配列のすべてが任意の値であるか確認したいときは all を使いましょう

```Matlab
>> array = [2 2 2 2 2 2];
>> all(array == 2)

ans =

     1
```

## cell配列への論理インデックス

cell配列にも並列処理は使用可能です

```Matlab
>> array = {'monday' 'tuesday' 'Wednesday' ''};
>> strcmp(array,'monday')

ans =

     1     0     0     0
```

cell配列へのisemptyにはcellfunを使用します。

```Matlab
>> array = {'monday' 'tuesday' 'Wednesday' ''};
>> cellfun('isempty',array)

ans =

     0     0     0     1
```

strfindの並列処理には工夫が必要です

```Matlab
>> array = {'monday' 'tuesday' 'Wednesday' ''};
>> ~cellfun('isempty',strfind(array,'mon'))

ans =

     1     0     0     0
```

これもうちょっとなんとかならないっすかね？