Numpy [基礎編]

Numpyのドキュメントを一通りまとめて行きます。
今回はかなり基礎的な部分です。
ドキュメントを直接読みたい人は、[こちら](https://docs.scipy.org/doc/numpy-1.12.0/numpy-user-1.12.0.pdf)で確認してみてください！

＊ 以下のarrayは配列（行列）オブジェクトを表すこととします。

# ０. インストールについて

僕は、AnacondaでPythonをダウンロードしたので、元からNumpyは入っていました。

入っていない人は[ここ](http://www.numpy.org/)からダウンロードしてください。

# １. 基本的な属性

- array.ndim : 行列の次元を表します。最初に何個 "[" が続いているかに対応します。
- array.shape : 行列の形がタプルで返されます。
- array.size : 行列の要素の全個数です。
- array.dtype : 要素のデータ型を表します。Pythonで扱えるデータ型に加えて、np.int64などのnumpy特有のものもあります。また、dtype='complex'は複素数を表します。
- array.itemsize : 各要素のバイト数を表します。int64型なら8byteですね。


# ２. 行列を作る

- Pythonのデータ型から行列を作る。

```
np.array([[1, 2, 3], [4, 5, 6]])

>>> array([1, 2, 3],
          [4, 5, 6]])
```

- np.zeros(行列の形) : タプルを引数にとり、全ての要素が0である行列を返す
- np.ones(行列の形）　 : こちらは全ての要素が1である行列を返す

```
np.zeros((3, 4))

>>> array([0, 0, 0, 0],
          [0, 0, 0, 0],
          [0, 0, 0, 0]])
```

- np.empty(行列の形) : ランダムな要素をもつ行列を作る

```
np.empty((4, 5))

>>> array([[  0.1 ,   0.15,   0.2 ,   0.25,   0.3 ],
           [  0.4 ,   0.5 ,   0.6 ,   0.8 ,   1.  ],
           [  1.5 ,   2.  ,   2.5 ,   3.  ,   4.  ],
           [  5.  ,   6.  ,   8.  ,  10.  ,  15.  ]])
```

- np.arange(要素の個数) : Pythonのrange()と同じようなやつ。１次元の行列を作る。
- np.arange(a, b, c) : このような形の時は、aから始まり、cずつ足した要素をもち、b未満の最大値を取るまで続く。

```
np.arange(12)

>>> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])


np.arange(10, 30, 5)

>>> array([10, 15, 20, 25])
```

- np.linspace(a, b, c) : aからbまで等間隔にc個の要素をもつ１次行列を返す。

```
np.linspace(0, 2.0, 9)

>>> array([0., 0.25, 0.5, 0.75, 1., 1.25, 1.5, 1.75, 2.])
```

- np.random.rand(行列の形) : 与えられた形で、各要素が0以上1以下の行列を返す
- np.random.randn（行列の形） : 上と同じだが、要素が整数。

＊これは、（a, b, (行列の形)）という引数の取り方もできて、その時は `a < x < b` の範囲からランダムに要素が選ばれる。

- np.fromfunction(関数, 行列の形[, データ型]) : 行数と列数の関係を表現できる。

```
np.fromfunction(lambda i,j: i+j, (3, 3), dtype='int64')

>>> array([[0, 1, 2],
           [1, 2, 3],
           [2, 3, 4]])
```

# ３. 行列をprintする

pythonのprintメソッドを使うと、コンマのない綺麗な行列を表示できる。

```
print(np.arange(12).reshape(3, 4))

>>> [[0 1 2 3]
     [4 5 6 7]
     [8 9 10 11]]
```

# 4. 数学的な操作は、全要素に適用される

行列に対して足し算とか掛け算とかをすると、全要素に対してその操作が適用されます。

```
a = np.array([10, 20, 30, 40])
b = np.array([1, 2, 3, 4])

a - b >>> array([9, 18, 27, 36])
b * 2 >>> array([2, 4, 6, 8])
a < 35 >>> array([True, True, True, False])
```

行列の積が欲しければ、dot()関数を使います。

```
A = np.array([[1, 1], [0, 1]])
B = np.array([[2, 0], [3, 4]])


A * B >>>  array([[2, 0],
                  [0, 4]])  # 対応する要素同士の掛け算が実行される。

A.dot(B) / np.dot(A, B) >>>  array([[5, 4],
                                    [3, 4]])
```

＊合計・最大値・最小値の取得は、np.sum(), np.max(), np.min()で取得できる。


＊操作したい軸を選べる。

```
a = np.arange(12).reshape(3, 4) >>> array([[0, 1, 2, 3],
                                           [4, 5, 6, 7],
                                           [8, 9, 10, 11]])

a.sum(axis=0) >>> array([12, 15, 18, 21])  # 垂直方向
a.min(axis=1) >>> array([0, 4, 8])  # 水平方向

```

# 5. 行列のインデックス・スライス・イテレーター化

１次行列だけでなく、複数の次元を持つ行列も、PythonのListと同じように扱える。
ただし、複数の次元を持つ行列をイテレーターとして扱う時は、flat属性を使う。ディクショナリをイテレーターとして扱う時にitems()を使うのと似ていますね！

```
A = np.arange(12).reshape(3, 4)

A[(0, 2)] >>>  2  # インデックス
A[1:2, 0:2] >>> array([[4, 5]])  # スライス

for element in A.flat:
    print(element)       >>> 0~11までがプリントされる
```

# 6. 行列の形を変える

- np.floor(行列) : 各要素を四捨五入する。（形には関係ないと思いますが、ドキュメントにはここに書かれていました笑）

- np.ravel(行列) : 行列を１次元に変換する。
- array.reshape(行列, 新しい形[, order]) : 行列を欲しい形に変えます（orderについては後述）。
- array.T : 転置行列を与える。


### **order**
>The elements of a are read using this index order. ‘C’ means to index the elements in row-major, C-style order, with the last axis index changing fastest, back to the first axis index changing slowest. ‘F’ means to index the elements in column-major, Fortran-style order, with the first index changing fastest, and the last index changing slowest. Note that the ‘C’ and ‘F’ options take no account of the memory layout of the underlying array, and only refer to the order of axis indexing. ‘A’ means to read the elements in Fortran-like index order if a is Fortran contiguous in memory, C-like order otherwise. ‘K’ means to read the elements in the order they occur in memory, except for reversing the data when strides are negative. By default, ‘C’ index order is used.

と書かれているように、 `C-style` では行の順番を優先、 `F-style` では列の順番を優先します。

```
A = np.arange(12).reshape(3, 4)

np.reshape(A, (2, 6), 'C')  >>>  array([[ 0,  1,  2,  3,  4,  5],
                                        [ 6,  7,  8,  9, 10, 11]])
np.reshape(A, (2, 6), 'F')  >>>  array([[ 0,  8,  5,  2, 10,  7],
                                        [ 4,  1,  9,  6,  3, 11]])
```

あと、`A-style` と `K-style` がありますが、基礎編では無い気がするので、機会があれば今後の記事で説明します。


# 7. 行列を合成する

- np.vstack((行列１, 行列2)) : 垂直方向に合体する。
- np.hstack((行列1, 行列2)) : 水平方向に合体する。

- np.column_stack((行列1, 行列2)) : １次行列の組み合わせなら２次元行列を作り、多次元の組みならhstackと同じ振る舞いをする。

```
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([[1, 2], [3, 4]])
d = np.array([[5, 6], [7, 8]])

np.vstack((a, b))  >>> array([[1, 2, 3],
                              [4, 5, 6]])

np.hstack((a, b))  >>> array([1, 2, 3, 4, 5, 6])

np.column_stack((a, b))  >>> array([[1, 4],
                                    [2, 5],
                                    [3, 6]])

np.column_stack((c, d))  >>> array([[1, 2, 5, 6],
                                    [3, 4, 7, 8]])
```

- np.newaxis : 新しい次元を生成する。

```
a = np.array([4, 2])
b = np.array([[1, 2], [3, 4]])


a[:, np.newaxis]  >>> array([[4],
                             [2]])

b[:, np.newaxis] >>> array([[[1, 2]],

                            [[3,4]]])
```

# 8. 行列を分割する

- np.hsplit(行列, 数) : 水平方向に指定した数に等分する。
- np.vsplit(行列, 数) : 垂直方向に指定した数に等分する。


# 9. 行列をコピーする

- array.view() : Pythonでいうshallow コピー。あんまり使わないと思います。
- array.copy() : Pythonでいうdeep コピー。