【超初心者向け】Numpy基礎50本ノック



みなさん、こんにちは。
こちらは[「ABEJAアドベントカレンダー2020」](https://qiita.com/advent-calendar/2020/abeja)の15日目の記事です。

# はじめに: 文系営業がDeep Learningの勉強を始めて
AIどころかエンジニア知識もない文系出身の私がABEJAにビジネス担当として飛び込んで、早数年。
やっと営業やマーケティングに困らない知見はついたものの、ABEJAにはデータサイエンス・ビジネスどちらもできるような人ばかり。少しでも追いつこうと、[日本ディープラーニング協会のE資格](https://www.jdla.org/certificate/engineer/)の勉強をはじめました。

ただ、Pythonの実装経験がない中で学習し始めたため、[ゼロから作るDeep Learning](https://www.oreilly.co.jp/books/9784873117584/)の最初の30ページあたりで既につまずき、その後は1ページ進むのに1時間かかるような始末。
特にNumpyの関数がわからず苦戦したので、自身の勉強メモを一覧化して、同じような挫折を味わいかけている方に共有しようと思った次第です。

# 対象となる読者の方
- 機械学習やDeep Learningの勉強を始めたが、pythonコードがちんぷんかんぷんで挫折しかかっている方
- データを行列として扱うためにはNumpyが便利なんだな・・くらいの理解の方
- [100 numpy excercises](https://github.com/rougier/numpy-100/blob/master/100_Numpy_exercises.md)にまだ挑んでいない方、あるいは挫折した方


# Numpyとは
- 前提として、Numpyは多次元配列を扱うことに長けている、数値計算ライブラリです。機械学習やDeep Learningにおいては、データを行列として扱って計算する形で利用します。
- Numpyの各関数や機能を使う際、はimport文、`import numpy as np`を使って宣言した上で、`np.関数名`で宣言します。
- 本文のndarrayとは、"N dimentional array"の略で、同じ属性や大きさを持った要素を持つN次元配列を扱うためのクラスのことです。通常np.array関数などによって生成します。

# 50本ノックスタート
## 1. 配列をつくる
### 01. np.array
配列を作る

```python.qiita.py
np.array([1, 2, 3])
->array([1, 2, 3])
```


### 02. np.zeros
要素がゼロの配列を作る

```python.qiita.py
np.zeros(3, dtype=float)
->array([0., 0., 0.])
```


### 03. np.zeros_like
aと同じ形式の、要素がゼロの行列を作る

```python.qiita.py
a = np.array([1, 2, 3])
np.zeros_like(a) 
->array([0, 0, 0])
```


### 04. np.ones
要素が1の配列を作る

```python.qiita.py
np.ones(3)
->array([1., 1., 1.])
```

### 05. np.eye
単位行列を作る

```python.qiita.py
np.eye(2)
->array([[1., 0.],
       [0., 1.]])
```


### 06. np.arange
規則的な配列を作る

```python.qiita.py
np.arange(0,2, 0.5) # 始点、終点(アウトプットはこの値を含まない）、間隔を記載
->array([0. , 0.5, 1. , 1.5])
```


### 07. np.linspace
規則的な配列を作る

```python.qiita.py
np.linspace(1, 10, 10) # 始点、終点(アウトプットはこの値を含む)、個数を記載
->array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
```


## 2. 様々な乱数の配列をつくる
### 08. np.random.rand
0.0以上、1.0未満の一様分布の乱数を生成

```python.qiita.py
np.random.rand(2) # 0〜1の乱数を2つ作成
->array([0.82954644, 0.76417915])# 乱数は都度生成される（ので左の限りではない）

np.random.rand(2, 2) # 0〜1の乱数で2x2の行列を生成
->array([[0.31443211, 0.59234688],
       [0.71882085, 0.66036226]]) 
```
ちなみに、np.random.random_sample()でも同様に0.0以上、1.0未満の一様分布の乱数を生成できます。


### 09. np.random.randint
範囲での任意の整数の乱数を生成

```python.qiita.py
np.random.randint(1, 7) # 1~７未満の範囲で任意の整数が返される
->2
```

### 10. np.random.randn
平均0、分散1の標準正規分布に従う値を生成

```python.qiita.py
np.random.randn() # 値を一つ作成
->0.413523804959048 

np.random.randn(2, 2) # 値を2×2行列で作成
->array([[-1.37996541, -1.41321606],
       [-1.22184305, -1.46967812]]) 
```

### 11. np.random.normal
正規分布の任意の平均・標準偏差を指定できる

```python.qiita.py
np.random.normal(5, 1) # 平均5、標準偏差1の正規分布
->5.255820663352234
```

### 12. np.random.choice()
ランダムに要素を抽出

```python.qiita.py
a = np.array(10)
np.random.choice(a) # 配列aの中からランダムに要素を抽出 
->9
```

## 3. 配列の参照をする
### 13. ndarray.ndim
行列の次元数を参照

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.ndim
->2
```

### 14. ndarray.size
行列の要素数を参照

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.size
->6
```

### 15. ndarray.shape
行・列数を参照

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.shape
->(2, 3)
```

### 16. ndarray.dtype
要素の型を参照

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.dtype
->dtype('int64')
```

### 17. [p, q]
配列のp行q列目の要素の参照

```python.qiita.py
a = np.array([1, 2, 3])
a[1] # 先頭を0番目とした際の1番目の値を参照
->2

a[0:2] # 0番目から、２番目の一つ手前までを参照
->array([1, 2])


b = np.array([[1, 2 ,3],[4, 5, 6]])
b[0,1] # 0行1列目を参照
->2

b[:,1] # 1列目を参照
->array([2, 5])

b[:,[1,2]] # 1,2列目を参照
->array([[2, 3],
       [5, 6]])
```
なお、コロンを用いてリストや文字列の一部を参照したり取り出すことができることをスライスと呼びます。


## 4. 配列を操作する
### 18. ndarray.reshape(p, q)
p行q列の配列に変換

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.reshape(3, 2)
->array([[1, 2],
       [3, 4],
       [5, 6]])
```

### 19. ndarray.resize(p, q)
p行q列の配列に変換し、変更された配列がもとの配列より大きければ、必要なだけ0で埋める

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.resize(3, 3)
print(a)
->[[1 2 3]
 [4 5 6]
 [0 0 0]]
```

### 20. ndarray.ravel
多次元配列を1次元の行列にする

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.ravel()
->array([1, 2, 3, 4, 5, 6])
```

なお、ndarray.flattenでも同じ事ができますが、ravelの方がより高速に処理できることがあるそう。

### 21. np.flip
配列の要素を反対にする

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
np.flip(a)
->array([[6, 5, 4],
       [3, 2, 1]])
```

a[::-1,::-1]といったスライスでも同様のことができます。


### 22. ndarray.transpose 
配列を転置する

```python.qiita.py
a = np.array([[1, 2 ,3],[4, 5, 6]])
a.transpose()
->array([[1, 4],
       [2, 5],
       [3, 6]])
```

### 23. np.append
配列に要素を追加

```python.qiita.py
a = np.array([1, 2, 3])
np.append(a, 4) # 末尾に4を追加
->array([1, 2, 3, 4])

np.append(0, a) # 先頭に０を追加
->array([0, 1, 2, 3])
```

### 24. np.where
条件を満たす要素を返す、あるいはTrue・Falseを判定する

```python.qiita.py
a = np.arange(6).reshape((2, 3))
np.where(a < 3, True, False) # a<3の場合True、そうでない場合Falseを返す
->array([[ True,  True,  True],
       [False, False, False]])

np.where(a < 3) # a<3を満たす要素の範囲を抽出
->(array([0, 0, 0]), array([0, 1, 2]))
```

### 25. np.all
配列の要素が全て条件を満たすか否かを判定する

```python.qiita.py
a = np.arange(6).reshape((2, 3))
np.all(a < 3)
->False

np.all(a < 6)
->True
```

### 26. np.any
配列の要素のいずれかが条件を満たすか否かを判定する

```python.qiita.py
a = np.arange(6).reshape((2, 3))
np.any(a < 3)
->True

np.any(a > 6)
->False
```

### 27. ndarray.argmax
多次元配列の中の最大値の要素を持つインデックスを返す

```python.qiita.py
a = np.array([1, 2, 3])
a.argmax()
->2
```

### 28. np.sort
配列を小さい順に並べ替える

```python.qiita.py
a = np.array([8, 2, 1, 5, 6])
np.sort(a)
->array([1, 2, 5, 6, 8])
```

### 29. np.argsort
配列を小さい順に並べ替えた際の配列のインデックスを出力

```python.qiita.py
a = np.array([8, 2, 1, 5, 6])
np.argsort(a)
->array([2, 1, 3, 4, 0])
```

### 30. np.expand_dims
配列に次元を追加

```python.qiita.py
a = np.arange(6).reshape(2, 3)

np.expand_dims(a, 0)
->array([[[0, 1, 2],
        [3, 4, 5]]])

np.expand_dims(a, 0).shape 
->(1, 2, 3)

np.expand_dims(a, 1)
->array([[[0, 1, 2]],

       [[3, 4, 5]]])

np.expand_dims(a, 1).shape
->(2, 1, 3)
```

### 31. np.broadcast_to
ブロードキャストと呼ばれる、異なる形状の配列を自動統一を実施
特にbroadcast_toは配列を任意の形状にブロードキャストする際に使用する

```python.qiita.py
a = np.arange(2)
np.broadcast_to(a,(2, 2))
->array([[0, 1],
       [0, 1]])
```

### 32. np.broadcast_arrays
複数の配列をブロードキャストして形状を揃えたい際に使用する

```python.qiita.py
a = np.arange(3)
b = np.arange(3).reshape(3, 1)
np.broadcast_arrays(a, b)
->[array([[0, 1, 2],
       [0, 1, 2],
       [0, 1, 2]]), array([[0, 0, 0],
       [1, 1, 1],
       [2, 2, 2]])]
```


## 5. 配列の演算

### 33. 一般的な四則演算

```python.qiita.py
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

a + b
->array([5, 7, 9])

a - b
->array([-3, -3, -3])

a * b 
->array([ 4, 10, 18])

a / b
->array([0.25, 0.4 , 0.5 ])

a **2 # aの2乗
->array([1, 4, 9])
```


### 34. np.add(a, b)
a, bの足し算

```python.qiita.py
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.add(a,b)
->array([5, 7, 9])
```

### 35. np.subtract(a, b)
a, bの引き算

```python.qiita.py
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.subtract(a, b)
->array([-3, -3, -3])
```

### 36. np.sum
配列の要素の合計値

```python.qiita.py
a = np.array([[1, 2, 3],[4,5,6]])

np.sum(a)
->21

np.sum(a, axis=0) #列ごとの合計値
->array([5, 7, 9])

np.sum(a, axis=1) #列ごとの合計値
->array([ 6, 15])
```

### 37. np.multiply(a, b)
a, bの要素ごとの積（アダマール積）

```python.qiita.py
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.multiply(a,b)
->array([ 4, 10, 18])
```

### 38. np.dot(a, b)
a, bの配列同士の積

```python.qiita.py
a = np.arange(3).reshape(1, 3)
b = np.arange(3).reshape(3, 1)

np.dot(a,b)
->array([[5]])
```

※なお、引数が両方とも一次元配列の場合は内積を返します。

### 39. np.mean
平均の算出

```python.qiita.py
a = np.arange(10).reshape(2, 5)
np.mean(a)
->4.5
```

### 40. np.std
標準偏差の算出

```python.qiita.py
a = np.arange(10).reshape(2, 5)
np.std(a)
->2.8722813232690143
```

### 41. np.power
累乗の計算

```python.qiita.py
np.power(2,3) #2の３乗
->8

a = np.array([1, 2, 3])
np.power(a, 2)
->array([1, 4, 9])
```

### 42. np.sqrt
平方根の計算

```python.qiita.py
np.sqrt(4) 
->2.0

a = np.array([1, 4, 9])
np.sqrt(a) 
->array([1., 2., 3.])
```

### 43. np.max
最大値を返す

```python.qiita.py
a = np.array([1, 2, 3])
np.max(a) 
->3
```

### 44. np.min
最小値を返す

```python.qiita.py
a = np.array([1, 2, 3])
np.min(a) 
->1
```

### 45. np.abs
絶対値を求める

```python.qiita.py
np.abs(-1)
->1
```

### 46. np.exp(x)
eのx乗

```python.qiita.py
np.exp(1)
->2.718281828459045
```

### 47. np.log(x)
eを底とするxの対数

```python.qiita.py
np.log(1)
->0.0
```

### 48. np.sin
sinを求める

```python.qiita.py
np.sin(1)
->0.8414709848078965
```

### 49. np.cos

```python.qiita.py
np.cos(1)
->0.5403023058681398
```

### 50. np.tan

```python.qiita.py
np.tan(1)
->1.557407724654902
```


# さいごに
いかがでしたでしょうか。Pythonや機械学習の初学者の方の参考に少しでもなれば幸いです。
また、この記事は初心者が書いていますので、違う点やアドバイスなどあればお気軽に教えていただけたら嬉しいです。
(もっと機械学習に使うものがあれば、追記か入れ替えをしようかと思っています）

ちなみに、この記事を書く際にも、ABEJAのデータサイエンティストや、データサイエンス知見のあるプロジェクトマネジャーの皆さんにアドバイスをもらいました（皆さんありがとう・・・）。
ABEJAには技術・事業双方に知見を持つメンバーがたくさんいます。少しでもご興味のある方、[採用募集](https://hrmos.co/pages/abeja)しておりますのでぜひお声がけください！