numpyのお勉強

#経緯
大学でnumpyを使うことになったので簡単にまとめて見た

#numpyについて
>
* NumPyは、プログラミング言語Pythonにおいて数値計算を効率的に行うための拡張モジュールである。効率的な数値計算を行うための型付きの多次元配列（例えばベクトルや行列などを表現できる）のサポートをPythonに加えるとともに、それらを操作するための大規模な高水準の数学関数ライブラリを提供する。
* Pythonは動的型付け言語であるため、プログラムを柔軟に記述できる一方で、純粋にPythonのみを使って数値計算を行うと、ほとんどの場合C言語やJavaなどの静的型付き言語で書いたコードに比べて大幅に計算時間がかかる。そこでNumPyは、Pythonに対して型付きの多次元配列オブジェクト (numpy.ndarray) と、その配列に対する多数の演算関数や操作関数を提供することにより、この問題を解決しようとしている。NumPyの内部はC言語 (およびFortran)によって実装されているため非常に高速に動作する。

wiki参照
要は、pythonだけどnumpy使えばc言語と同等の速度が見込めるってことです。

**※numpyでも遅い場合は、cupyを覚える必要があるかもしれない**
GPUをレンタルできると思わなかったので

##numpyのinstall
```bash:numpyのインストール
$pip install numpy
or
$pip3 install numpy
```

##numpyの基本
numpyはpythonで扱ったリスト（配列）とは違い、**数値**のみを扱います。

##numpy配列の生成方法について
numpyではpythonのリストを生成する要領で配列を生成できます。
それ以外の方法等もあとで後述。
生成方法としては`a = numpy.array([1、2、3])`で生成が可能です。
リストの中身を2次配列`a = numpy.array([[1,2,3][1,2,3]])`3次配列`a = numpy.array([[[1,2,3],[1,2,3][1,2,3]]])`にすればよいです。
pythonは動的に型を取得しますが、指定の配列にすることもできます。
やり方は`a = np.array( [ [1,2], [3,4] ], dtype=complex)`
numpyには作成において便利はメソッドが多く存在しています。
全ての値を0or1orランダムで作成することが可能です。

```python
import numpy as np
#　pythonの配列↓
array = [1,2,3,4]
#　numpyの配列↓
nparray = np.array([1,2,3,4])
```

###numpyの特殊な生成メソッドについて

| メソッド | 説明 |
|:-:|:--|
|numpy.array|numpyの配列を生成|
|numpy.zeros()|初期値0の配列を生成|
|numpy.ones()|初期値1の配列を生成|
|numpy.eye()|対角線上の値が1それ以外は0の配列を生成|
|numpy.empty()|初期値ランダムの配列を生成|
|numpy.full()|初期値を任意の値で生成|

```bash
$python
>>>import numpy as np
>>> np.zeros((3,4))
array([[ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.]])
>>> np.ones((3,4))
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]])
>>> np.eye(3,4)
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.]])
>>> np.empty((3,4))
array([[  0.00000000e+000,   2.68156175e+154,   2.96439388e-323,
          0.00000000e+000],
       [  0.00000000e+000,   0.00000000e+000,   0.00000000e+000,
          0.00000000e+000],
       [  0.00000000e+000,   0.00000000e+000,   0.00000000e+000,
          8.3440269$e-309]])
```
##np.arangeメソッドを利用した配列の生成
`arange`メソッドとは規則的な値を元に配列を生成するときに利用する。
たとえば、0から5ずつ100まで1次配列を作る場合などに利用する。

```bash
＄python
>>>import numpy as np
>>> np.arange(0,100,5) 
array([ 0,  5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80,
       85, 90, 95])
```

2次配列を生成する場合には`reshape`メソッドを利用する
**arangeで生成する個数と配列の要素数を同じにしなければならないので注意が必要**

```bash
＄python
>>>import numpy as np
>>> np.arange(0,60,5).reshape(4,3)
array([[ 0,  5, 10],
       [15, 20, 25],
       [30, 35, 40],
       [45, 50, 55]])
```
###任意の値を見るにはどうすればいいのか
pythonの配列で任意の位置の値見る場合は?

```bash
＄python
>>>import numpy as np
>>> a　= np.arange(0,60,5).reshape(4,3)
>>> a
array([[ 0,  5, 10],
       [15, 20, 25],
       [30, 35, 40],
       [45, 50, 55]])
>>> a[0][1]
5
>>> a[0,1]
5
>>>a[0]
[ 0,  5, 10]
```
## numpyの基本メソッド
python標準で扱ったメソッド名もあるので覚えやすいかもしれないです。

| メソッド| 用途 |例 結果|
|:-:|:-:|:-:|
|ndarray.ndim|何次元配列を出力|a.ndim 1|
|ndarray.shape|何次元配列の縦横等の最大値を出力|a.shape 3|
|ndarray.size|配列の要素数を出力|a.size 3|
|ndarry.dtype|配列のデータのタイプを出力|a.dtype int64|
|ndarray.data|配列のポインタの位置を出力|a.data|

```tutorial.py
import numpy as np
a = np.array([1,2,3])
print(a.ndim)
print(a.shape)
print(a.size)
print(a.dtype)
print(a.data)
```

##numpyの配列の初期化について
numpyには生成した配列を初期化するメソッドも備わっています。
生成じ使ったメソッドに_likeをつけます

| メソッド | 説明 |
|:-:|:--|
|numpy.zeros_like()|0で初期化|
|numpy.ones_like()|1で初期化|
|numpy.full_like()|指定の値で初期化|

##numpyの四則演算について
numpyは行列計算を得意としているため、基本的には行列計算に関する四則演算を説明する

```tutorial.py
import numpy as np
a = np.arange(0,45,5).reshape(3,3)
b = np.arange(0,27,3).reshape(3,3)
# 足し算
c = a + b
#　引き算
d = a - b
#　要素ごとの掛け算
f = a * b
#　べき乗計算
g = a ** 4
#　行列積
h = a.dot(b)
i = np.dot(a,b)
```
## おまけ
| メソッド | 説明 |
|:-:|:--|
|np.sum() or hoge.sum()|配列の要素の値の合計値を出力|
|np.max() or hoge.max()|配列の要素の値での最大値を出力|
|np.min() or hoge.min()|配列の要素の値での最大値を出力|


##numpyを使うと速度が早いのか
```tutorial.py
import numpy as np
import time
a = np.ones((1000,1000),dtype = np.int16)
b =  [[1] * 1000] * 1000
start = time.time()
total1 = np.sum(a)
end = time.time() - start
print(total1)
print ("end1:{0}".format(end) + "[sec]")

total2 = 0
start2 = time.time()
for y in range(1000):
    for x in range(1000):
        total2 = total2 + b[y][x]
end2 = time.time() - start2
print(total2)
print ("end2:{0}".format(end2) + "[sec]")

start3 = time.time()
total3 = sum(map(sum, b))
end3 = time.time() - start3
print(total3)
print ("end3:{0}".format(end3) + "[sec]")

```
```bash
実行結果
$python tutorial.py
1000000
end1:0.0007929801940917969[sec]
1000000
end2:0.14750194549560547[sec]
1000000
end3:0.008537054061889648[sec]
```

##　配列の結合
numpyでは配列を簡単に結合することが可能である。

|メソッド|説明|
|:-:|:-:|
|numpy.vstack（）|縦方向に結合|
|numpy.hstack（）|横方向に結合|

```bash
>>>python
>>>import numpy as np
>>> aaa = np.full((2,2),10)
>>> aaa
array([[10, 10],
       [10, 10]])
>>> bbb = np.full((2,2),20)
>>> bbb
array([[20, 20],
       [20, 20]])
>>> np.vstack((aaa,bbb))
array([[10, 10],
       [10, 10],
       [20, 20],
       [20, 20]])
>>>>>> np.hstack((aaa,bbb))
array([[10, 10, 20, 20],
       [10, 10, 20, 20]])
```
これを応用して九九表を作ることが可能になるんじゃないだろうか。
挑戦してほしい

```python
import numpy as np
a1 = np.arange(1,10,1)
a2 = np.arange(2,19,2)
a3 = np.arange(3,29,3)
a4 = np.arange(4,39,4)
a5 = np.arange(5,49,5)
a6 = np.arange(6,59,6)
a7 = np.arange(7,69,7)
a8 = np.arange(8,79,8)
a9 = np.arange(9,89,9)
print(np.vstack((a1,a2,a3,a4,a5,a6,a7,a8,a9)))
```

##参考にしたサイト
[numpyのreference](https://docs.scipy.org/doc/numpy/reference/)