Python入門 Pythonによる科学計算

#index
- [Python入門 Pythonの基礎](https://qiita.com/RitaChan/private/6d12adfaf84fd805e226)
- [Python入門 Pythonによる科学計算](https://qiita.com/RitaChan/private/e754f3cd03d6ed4babc1)
- [Python入門 機械学習の基礎(教師なし学習/ 主成分分析)](https://qiita.com/RitaChan/items/45f435f14d2e785316cb)



#概要
ここでは色々な計算テクニックを身に付けるために、Numpyのインデックス参照やブロードキャストについて見ていく。

#今回使うライブラリのインポート
```Python
import numpy as np
import numpy.random as random
import scipy as sp

import matplotlib.pyplot as plt
import matplotlib as mpl
```

#Numpyを使った計算の応用
##インデック参照
```python
sample_array = np.arange(10)
print('sample_array:',sample_array)
```
```:出力
sample_array: [0 1 2 3 4 5 6 7 8 9]
```
上の結果からわかるように`sample_array`は0から9までの数字(配列)を作成している。
[連番や等差数列を生成するnumpy.arange関数の使い方](https://deepage.net/features/numpy-arange.html)

次にスライス操作を行ってみる。

```python
#元のデータ
print(sample_array)

# 前から数字を5つ取得して、sample_array_sliceに入れる（スライス）
sample_array_slice = sample_array[0:5]
print(sample_array_slice)
```
```:出力
[0 1 2 3 4 5 6 7 8 9]
[0 1 2 3 4]
```
次は、sample_array_sliceの先頭から3つを10という値に置き換える。
**この時、元の変数であるsample_arrayの値も変わっていることに注意**

```python
# sample_array_sliceの3文字目までは、10で置換
sample_array_slice[0:3] = 10
print(sample_array_slice)

# スライスの変更はオリジナルのリストの要素も変更されていることに注意
print(sample_array)
```
```:出力
[10 10 10  3  4]
[10 10 10  3  4  5  6  7  8  9]
```
##データのコピー
先ほどの代入元の変数の値も変わってしまわないように**元のデータを参照せず、元のデータをコピーした物を参照させるようにすれば良い。**

```py

# copyして別のobjectを作成
sample_array_copy = np.copy(sample_array)
print(sample_array_copy)

sample_array_copy[0:3] = 20
print(sample_array_copy)

# 元のリストの要素は変更されていない
print(sample_array)
```
```:出力
[10 10 10  3  4  5  6  7  8  9]
[20 20 20  3  4  5  6  7  8  9]
[10 10 10  3  4  5  6  7  8  9]
```
##ブールインデックス参照
`bool（True or False）`によって、どのデータを取り出すかを決める機能。
sample_namesは、「a」「b」「c」「d」「a」という値を要素として持つ要素数5の配列、dataは、標準正規乱数からなる5×5の配列を作成。

```py
sample_names = np.array(['a','b','c','d','a'])
random.seed(0) # 発生する乱数の種を固定
data = random.randn(5,5)

print(sample_names)
print(data)
```
```py:出力
['a' 'b' 'c' 'd' 'a']
[[ 1.764  0.4    0.979  2.241  1.868]  #a
 [-0.977  0.95  -0.151 -0.103  0.411]  #b
 [ 0.144  1.454  0.761  0.122  0.444]  #c
 [ 0.334  1.494 -0.205  0.313 -0.854]  #d
 [-2.553  0.654  0.864 -0.742  2.27 ]] #a
```
要素の値が「'a'」である部分だけTrueになる結果を取り出す

```py
sample_names == 'a'
```
```:出力
array([ True, False, False, False,  True])
```
data変数の[]の中に条件として指定すると、Trueになっている箇所のデータだけが取り出せる

```py
data[sample_names == 'a']
```
```:出力
array([[ 1.764,  0.4  ,  0.979,  2.241,  1.868],
       [-2.553,  0.654,  0.864, -0.742,  2.27 ]])
```
##条件制御
`numpy.where(条件の配列, Xのデータ, Yのデータ)`
これは、条件の配列がTrueのときは 𝑋 のデータ、そうでなければ 𝑌 のデータが取り出される。

```py
cond_data = np.array([True,True,False,False,True])

x_array= np.array([1,2,3,4,5])

y_array= np.array([100,200,300,400,500])

print(np.where(cond_data,x_array,y_array))
```
```:出力
[  1   2 300 400   5]
```
#Numpyの演算処理
##重複の削除
`unique`を使うことで、要素の重複を削除できる

```py
cond_data = np.array(['ばなな','りんご','パイナップル','ばなな','パイナップル'])
print(cond_data)

# 重複削除
print(np.unique(cond_data))
```
```:出力
['ばなな' 'りんご' 'パイナップル' 'ばなな' 'パイナップル']
['ばなな' 'りんご' 'パイナップル']
```
##ユニバーサル関数
全ての要素に関数を適用できる

```py
sample_data = np.arange(10)
print('元のデータ：', sample_data)
print('すべての要素の平方根：',np.sqrt(sample_data))
print('すべての要素のネイピア指数関数：',np.exp(sample_data))
```
```:出力
元のデータ： [0 1 2 3 4 5 6 7 8 9]
すべての要素の平方根： [0.    1.    1.414 1.732 2.    2.236 2.449 2.646 2.828 3.   ]
すべての要素のネイピア指数関数： [1.000e+00 2.718e+00 7.389e+00 2.009e+01 5.460e+01 1.484e+02 4.034e+02
 1.097e+03 2.981e+03 8.103e+03]
```
##最小、最大、平均、合計の計算
Pandansでもできるが、Numpyでもできる

```py
data = np.arange(9).reshape(3,3)

print(data)

print('最小値:',data.min())
print('最大値:',data.max())
print('平均:',data.mean())
print('合計:',data.sum())

# 行列を指定して合計値を求める
print('行の合計:',data.sum(axis=1))
print('列の合計:',data.sum(axis=0))
```
```:出力
[[0 1 2]
 [3 4 5]
 [6 7 8]]
最小値: 0
最大値: 8
平均: 4.0
合計: 36
行の合計: [ 3 12 21]
列の合計: [ 9 12 15]
```
[axis = 0, axis=1 がわからなくなった人へ](https://qiita.com/Phoeboooo/items/b464b7df3c64a33caf94)
##真偽値の判定
`any`や`all`を使うと、要素の条件判定ができる。

`any`:いずれか少なくとも1つ満たすものがあればTrue
`all`:全て満たす場合にTrue

```py
cond_data = np.array([True,True,False,False,True])

print('Trueが少なくとも１つあるかどうか:',cond_data.any())
print('すべてTrueかどうか:',cond_data.all())
```
```:出力
Trueが少なくとも１つあるかどうか: True
すべてTrueかどうか: False
```
条件を指定してからsumを指定すると、条件に合致する要素の個数を調べられる。

```py
data2 = np.arange(9).reshape(3,3)
print(data2)
print('5より大きい数字がいくつあるか:',(data2>5).sum())
```
```:出力
[[0 1 2]
 [3 4 5]
 [6 7 8]]
5より大きい数字がいくつあるか: 3
```
##対角成分の計算
対角成分(行列の左上から右下にかけての対角線上に並ぶ成分)

```py
data3 = np.arange(9).reshape(3,3)
print(data3)

print('対角成分:',np.diag(data3))
print('対角成分の和:',np.trace(data3))
```
```:出力
[[0 1 2]
 [3 4 5]
 [6 7 8]]
対角成分: [0 4 8]
対角成分の和: 12
```
#配列操作とブロードキャスト
##再形成
```py
sample_array = np.arange(10)
sample_array
```
```:出力
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
```
`reshape(2, 5)`で2行5列の行列に再形成

```py
sample_array2 = sample_array.reshape(2,5)
sample_array2
```
```:出力
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
```
5行2列の行列を再形成

```py
sample_array2.reshape(5,2)
```
```:出力
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7],
       [8, 9]])
```
##データの結合
`concatenate`を使うと、データの結合が可能

```python

sample_array3 = np.array([[1,2,3],[4,5,6]])
sample_array4 = np.array([[7,8,9],[10,11,12]])

# 行方向に結合。パラメータのaxisに0を指定
np.concatenate([sample_array3,sample_array4],axis=0)
```
```:出力
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
```
行方向の結合は、`vstack`でも可能

```py
np.vstack((sample_array3,sample_array4))
```
```:出力
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
```
列方向の結合はaxisに1を設定する。

```py
np.concatenate([sample_array3,sample_array4],axis=1)
```
```:出力
array([[ 1,  2,  3,  7,  8,  9],
       [ 4,  5,  6, 10, 11, 12]])
```
列方向の結合は、`hstack`でも可能。

```py
np.hstack((sample_array3,sample_array4))
```
```:出力
array([[ 1,  2,  3,  7,  8,  9],
       [ 4,  5,  6, 10, 11, 12]])
```
##配列の分割
`split`を使うと配列を分割できる。
splitに[1,3]というパラメータを指定することによって以下のように分割させる。
①１の手前までの行(0番目の行)
②１から３の手前までの行(1番目と2番目の行)
③３以降全ての行(3番目の行)

```py
# sample_array_vstackを3つに分割し、first、seocnd、thirdという3つの変数に代入
first,second,third=np.split(sample_array_vstack,[1,3])
print(first) #出力: [[1 2 3]]

print(second) #出力: [[4 5 6]
              #      [7 8 9]]

print(third) #出力: [[10 11 12]]
```
##繰り返し処理
`repeat`を使うと、それぞれの要素を繰り返し生成できる

```py
data4 = np.arange(5)
print(data4)

data4.repeat(3)
```
```:出力
[0 1 2 3 4]
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])
```
##ブロードキャスト
配列の大きさが異なっている時、自動的に要素をコピーして、対象の大きさを揃える機能。
[NumPyのブロードキャストのメモ](http://sucrose.hatenablog.com/entry/2014/12/15/000352)
[機械学習の Python との出会い](http://www.kamishima.net/mlmpyja/nbayes2/broadcasting.html)
[公式](https://docs.scipy.org/doc/numpy/reference/ufuncs.html#broadcasting)

```py
sample_array = np.arange(10)
print(sample_array)

sample_array + 3
```
```:出力
[0 1 2 3 4 5 6 7 8 9]
array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12])
```