python3 エンジニア認定データ分析試験チートシート2　～Numpy～

### Numpy
#### Numpyの概要
* データ型は揃える必要がある

データ構造は以下
* 配列用Ndarray
* 行列用matrix

#### インポート
```python:import.py
import numpy as np
```

#### 配列作成 array
```python:makearray.py
# 1次元配列
a = np.array([1, 2, 3])
# 2次元配列
b = np.array([[4, 5, 6],[7, 8, 9]])
```
#### 要素の確認　type,shape,dtype
```python:check.py
a
>> array([1, 2, 3])

print(a)
>> [1, 2, 3]

type(a)
>> numpy.ndarray

a.dtype
>>dtype('int64')

a.shape
>>(3,)
b.shape
>>(2, 3)
```

#### 配列の変形 reshape
```python:reshape.py
c1 = np.array([0, 1, 2, 3, 4, 5])
c2 = np.reshape((2,3))
c2
>> array([[0, 1, 2],[3, 4, 5]])
```

#### データ型指定と変更 dtype,astype
```python:dtype.py
d = np.array([1, 2], dtype=np.int16)
d
>> array([1, 2],dtype=int16)
d.dtype
>> dtype('1nt16')

d.astype(np.float16)
>>array([1, 2], dtype=float16)
```

#### インデックスとスライス
```python:index.py
a = array([[1, 2, 3],
           [4, 5, 6]])
# インデックス指定
a[1]
>>array([4, 5, 6])
a[1,2]
>>6
# スライス指定
a[:,1]
>>array([2,5])
a[:,[0,2]]
>>array([[1,3],
         [4,6]])
```

#### データ再代入
```python:datachange.py
a = np.array([[1, 2, 3],[4, 5, 6]])
# インデックス指定
a[1, 2] = 7
a
>>array([[1, 2, 3],
         [4, 5, 7]])
# スライス指定
a[:, 2] = 8
a
>>array([[1, 2, 8],[4, 5, 8]])
```

#### 深いコピーと浅いコピー ravel,flatten
ravelは参照、flattenはコピー
```python:shallowanddeep.py
a = np.array([1, 2, 3])
>>array([1, 2, 3])
a1 = a
a1[1] = 5
a1
>>array([1, 5, 4])

# 浅いコピーの為元のaの要素も影響を受ける
a
>>array([1, 5, 4])

a2 = a.copy()
a2[0] = 6
a2
>>array([6, 5, 4])
# 深いコピーの為元のaの要素も影響を受けない
a
>>array([1, 5, 4])

#ravel,flattenの違いについて確認
c = np.array([[0, 1, 2],[3, 4, 5]])
c2 = c.ravel()
c3 = c.flatten()
c2[0] = 6
c3[1] = 7

# ravel(参照) 
c2
>>array([[6, 1, 2],[3, 4, 5]])
# flatten(コピー)
c3 
>> array([[0, 7, 2],[3, 4, 5]])

c #ravelの影響しか受けない
>>array([[6, 1, 2],[3, 4, 5]]
```

#### python標準リストとNumpyのスライスの扱いの違い
標準リストはコピーが渡されるが、
Numpyのスライスは参照なので注意
```python:list.py
# Python標準
py_list1 = [0, 1]
py_list2 = py_list1[:]
py_list[0] = 2
print(py_list1)
print(py_list2)
>>[0,1]
  [2,1]

# Numpyの場合
np_array1 = [0, 1]
np_array2 = np_array1[:]
np_array[0] = 2
print(np_array1)
print(np_array2)
>>[2 1]
  [2 1]
```

#### 数列の作成 arange
```python:arange.py
import numpy as np
arr = np.arange(start=0, stop=10, step=2)
print(arr)
>>array([0 2 4 6 8])
```

#### 乱数 seed,random,rand,randint,uniform,randn
* seed(seed値)
乱数生成のシード（初期値）を設定
同じシード値を使用すると、同じ乱数のシーケンスが生成される
```python:seed.py
# Seed値の設定
np.random.seed(123)
```

##### 【一様乱数】
* random
0~1範囲のランダムの数値をタプルの行列分作成
返り値：浮動点少数
```python:ramdom.py
# ３行２列の一様乱数を作成
np.random.random((3, 2))
>>>array([[0.69646919, 0.28613933],
       [0.22685145, 0.55131477],
       [0.71946897, 0.42310646]])
```

* rand(d0,　..., dn次元)
引数に次元を与え、0から1までの範囲で一様分布に従う乱数を生成
出力形式はNumPy配列、返り値は浮動小数点数
```python:rand.py
np.random.rand(4, 2)
>>> array([[0.69646919, 0.28613933],
       [0.22685145, 0.55131477],
       [0.71946897, 0.42310646],
       [0.9807642 , 0.68482974]])
```

* randint
引数：low, high=None, size=None, dtype='l'
lowからhigh（highは含まれない）までの範囲で整数の乱数を生成
sizeは出力の形状を指定
```python:randint.py
np.random.randint(1, 10)
>>>3
```

* uniform
引数：low=0.0, high=1.0, size=None
指定した範囲で一様分布に従う乱数を生成
lowは範囲の下限、highは上限を指定
sizeは指定しない場合、単一の整数が生成される
返り値は浮動点少数
```python:uniform.py
# Size指定なし
np.random.uniform()
>>>0.48093190148436094
# 始点、終点指定
np.random.uniform(2,5.0)
>>>4.089407556793585
# # high,配列指定パターン
np.random.uniform(3.0, size=(4,4))
>>>array([[1.60706163, 2.42772133, 2.54629709, 1.89737046],
       [1.56106206, 2.15378708, 1.0384716 , 1.63034052],
       [2.0381362 , 2.21576496, 2.31364397, 1.54190059],
       [2.12285551, 2.88064421, 2.20391149, 1.52400919]])
# 配列のみ指定のパターン
np.random.uniform(size=(4, 3))
>>>array([[0.69646919, 0.28613933, 0.22685145],
       [0.55131477, 0.71946897, 0.42310646],
       [0.9807642 , 0.68482974, 0.4809319 ],
       [0.39211752, 0.34317802, 0.72904971]])
```

#### 【正規分布】
* randn(d0, d1, ..., dn)  
引数は全てオプション
```python:randn.py
# 標準正規分布からランダムな浮動小数点数を1つ生成
np.random.randn()
>>>-2.426679243393074
# 標準正規分布からランダムな浮動小数点数を5つ生成（1次元配列）
np.random.randn(5)
>>>array([-0.42891263,  1.26593626, -0.8667404 , -0.67888615, -0.09470897])
# 標準正規分布からランダムな浮動小数点数で3行2列の2次元配列を生成
np.random.randn(3, 2)
>>>array([[ 1.49138963, -0.638902  ],
       [-0.44398196, -0.43435128],
       [ 2.20593008,  2.18678609]])
```

### 数列作成 zeros,ones,eye,full,nan,linspace,diff
#### 同じ要素の配列を作成
* zeros: 指定した形状とタイプの新しい配列を作成し、要素をゼロで初期化
* ones: 指定した形状とタイプの新しい配列を作成し、要素を1で初期化
```python:onezeros.py
np.zeros(4)
>>>array([0., 0., 0., 0.])
np.zeros((2,3))
>>>array([[0., 0., 0.],
       [0., 0., 0.]])
np.ones(2)
>>array([1., 1.])
np.ones((2,3))
>>>array([[1., 1., 1.],
       [1., 1., 1.]])

```
#### 単位行列の作成 eye
* 左下から右下にかけて対角要素1を埋める正方行列を作成する
* 要素数nを指定してn行n列を作成できる。※正方行列なので引数は１つ
```python:eye.py
np.eye(3)
>>>array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])
```
#### 指定値で埋める　full
* full: 指定した値で満たされた配列を生成します
```Python:full.py
# 1次元配列の場合
np.full(3, 3.14)
>>>array([3.14, 3.14, 3.14])
# ２次元配列の場合
np.full((2,4),np.pi)
>>>array([[3.14159265, 3.14159265, 3.14159265, 3.14159265],
       [3.14159265, 3.14159265, 3.14159265, 3.14159265]])
```

#### 欠損値の生成 nan
行列にnanを含むときは浮動点少数が返却される
```python:nan.py
# 1次元配列
np.nan
>>>nan
# 2次元配列
np.array([1,2, np.nan])
>>>array([ 1.,  2., nan])
```

### 範囲指定で均等割りデータを作成 linspace
指定した間隔で等間隔の数値を生成
返り値は浮動点少数
引数：start, stop, num,endpoint, retstep, dtype
**必須**
    start : 数列の開始値を指定
    stop  : 数列の終了値を指定
**オプション**
num : int型 生成する数を指定。デフォルトは50
endpoint : bool型 
True(デフォルト) 終了値はstopを含む 
False 終了値はstopを含まない  
retstep : bool型 Trueの時、数列以外にステップ（隣接する値間の差）も返す
dtype : データ型 配列のデータ型を指定。指定しない場合、start、stop、numのデータ型から推測される
以下は０～１までの値範囲を５等分した配列を作成

```python:linspace.py
# 0から1までの数値を5等分
np.linspace(0, 1, 5)
>>>array([0.  , 0.25, 0.5 , 0.75, 1.  ])
# ２０分割の行列を生成
np.linspace(0, np.pi, 21)
>>>array([0.        , 0.15707963, 0.31415927, 0.4712389 , 0.62831853,
       0.78539816, 0.9424778 , 1.09955743, 1.25663706, 1.41371669,
       1.57079633, 1.72787596, 1.88495559, 2.04203522, 2.19911486,
       2.35619449, 2.51327412, 2.67035376, 2.82743339, 2.98451302,
       3.14159265])
# retstepオプションの使用(差分がいくつかを返却する)
np.linspace(0, 1, 5, retstep=True)
>>>(array([0.  , 0.25, 0.5 , 0.75, 1.  ]), 0.25)
```
### 差分を表示 diff
各要素の差分を表示する。
```python:diff.py
l = np.array([2, 6, 4, 6, 1, 3])
np.diff(l)
>>>array([ 4, -2,  2, -5,  2])
```

### 配列の連結 concatenate
#### concatenate関数
引数：axis = 0(列),1(行)
```python:concatenate.py
# 例題用の配列作成
a = np.array([1, 5, 4])
a1 = np.array([1, 5, 4])
>>>[1 5 4]
   [1 5 4]
# 連結
np.concatenate([a, a1])
>>>array([1, 5, 4, 1, 5, 4])

# 例題用の配列作成
b = np.array([[1,2,3],[4,5,6]])
b1 = np.array([[10,11,12],[13,14,15]])

# 列方向→への連結
np.concatenate([b,b1],axis=0)
>>>array([[ 1,  2,  3, 10, 11, 12],
          [ 4,  5,  6, 13, 14, 15]])
# 行方向↓への連結
np.concatenate([b,b1],axis=1)
>>>array([[ 1,  2,  3],
          [ 4,  5,  6],
          [10, 11, 12],
          [13, 14, 15]])
```

### スタックhstack,vstack
前提としてhstackは列数、vstackは行数があっていること
```python:hstack.py
# hstack
np.hstack([b,b1])
>>>array([[ 1,  2,  3, 10, 11, 12],
          [ 4,  5,  6, 13, 14, 15]])
```

```python:vstack.py
# vstack
np.vstack([c,c1])
>>>array([[ 1,  2,  8],
          [ 4,  5,  8],
          [30, 60, 45]])
```

### 分割 hsplit,vsplit
返り値は2つ

```python:hsplit.py
# 例題用の配列作成
b3 = np.array([[ 1,  2,  8],
               [ 4,  5,  8],
               [30, 60, 45]])
# hspilt
first, second = np.hsplit(b3, [2])
#　返り値1
first
>>>>array([[ 1,  2],
           [ 4,  5],
           [30, 60]])
#　返り値2
second
>>>array([[ 8],
          [ 8],
          [45]])
```

```python:vsplit/py
# vspilt
first1, second1 = np.vsplit(b3,[2])
#　返り値1
first1
>>>array([[1, 2, 8],
          [4, 5, 8]])
#　返り値2
second1
>>>array([[10, 11, 12],
          [13, 14, 15]])
```

#### 転置 T
n行m列の配列をm行n列に変形する
```python:T.py
a = array([[1, 2, 8],
       [4, 5, 8]])

a.T
>>>array([[1, 4],
       [2, 5],
       [8, 8]])
```

#### 次元追加　newaxis
```python:newaxis.py
a = array([1, 5, 4])
# 列方向に次元追加
a[np.newaxis,:]
>>>array([[1, 5, 4]])

# 行方向に次元追加
a[:,np.newaxis]
>>>array([[1],
          [5],
          [4]])
# ２次元の場合
b = array([[1, 2, 8],
           [4, 5, 8]])
# 列方向に次元追加
b[:,np.newaxis]
>>>array([[[1, 2, 8]],
          [[4, 5, 8]]])
# 行方向に次元追加
b[np.newaxis,:]
>>>array([[[1, 2, 8],
           [4, 5, 8]]])
```

#### グリッドデータの作成
2つの1次元配列を入力として受け取り、それらをグリッドに展開する
```python:meshgrid.py
# グリッドデータの作成
m = np.arange(0,4)
>>>array([0, 1, 2, 3])
n = np.arange(4,7)
>>>array([4, 5, 6])

#　グリッドデータ作成
xx, yy = np.meshgrid(m, n)
# 行方向に要素数[n]個分繰り返す
xx
>>>array([[0, 1, 2, 3],
          [0, 1, 2, 3],
          [0, 1, 2, 3]])
# 列方向に要素[n]を要素数[m]個分繰り返す
yy
>>>array([[4, 4, 4, 4],
          [5, 5, 5, 5],
          [6, 6, 6, 6]])
```

### ユニバーサルファンクション　abs sin cos tan log exp
要素ごとに配列操作を行うための関数
#### abs(絶対値)
```python:abs.py
a = array([[-3, -2, -1],
           [ 0,  1,  2]])

np.abs(a)
>>>array([[3, 2, 1],
          [0, 1, 2]])
```
#### sin関数
```python:sin.py
b = np.array([-1.        , -0.77777778, -0.55555556, -0.33333333, -0.11111111,
               0.11111111,  0.33333333,  0.55555556,  0.77777778,  1.        ])
np.sin(b)
>>>array([-0.84147098, -0.70169788, -0.52741539, -0.3271947 , -0.11088263,
           0.11088263,  0.3271947 ,  0.52741539,  0.70169788,  0.84147098])
```
#### cos関数
```python:cos.py
np.cos(b)
>>>array([0.54030231, 0.71247462, 0.84960756, 0.94495695, 0.99383351,
          0.99383351, 0.94495695, 0.84960756, 0.71247462, 0.54030231])
```
#### log関数
```python:log.py
c = array([0, 1, 2])
# log関数(自然対数)
np.log(c)
>>>array([      -inf, 0.        , 0.69314718])
# -infはマイナス無限大を表す
d = np.array([[1, 2, 3],
              [4, 5, 6]])
# log関数(常用対数)
np.log10(d)
>>>array([[0.        , 0.30103   , 0.47712125],
          [0.60205999, 0.69897   , 0.77815125]])
```
#### 自然対数の底
```python:exp.py
e = np.array([0, 1, 2])

np.exp(a)
>>>array([1.        , 2.71828183, 7.3890561 ])
```

### ブロードキャスト
#### 和
```python:cast.py
a = np.array([0, 1, 2])

# 和
a + 10
>>>array([10, 11, 12])

b = np.array([[-3, -2, -1],
              [ 0,  1,  2]])
# 和2
# 次元数が異なる配列通しの計算
a + b
>>>array([[-3, -1,  1],
          [ 0,  2,  4]])
# 和3
a1 = a[:,np.newaxis]
>>>array([[0],
          [1],
          [2]])
# a(3行1列) + a1(3行1列)の計算
# 引き延ばされて計算される。
# [0, 1, 2] + [0, 0, 0]
# [0, 1, 2] + [1, 1, 1]
# [0, 1, 2] + [2, 2, 2]
a + a1
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4]])
```
#### 差
```python:subtraction.py
a = np.array([0, 1, 2])
b = np.array([[-3, -2, -1],
              [ 0,  1,  2]])
# 差
b - a
>>>array([[-3, -3, -3],
          [ 0,  0,  0]])
# 差2
a - b
>>>array([[3, 3, 3],
          [0, 0, 0]])
```


#### 掛け算
```python:multiplication.py
b * 2
>>>array([[-6, -4, -2],
          [ 0,  2,  4]])

a * b
>>>array([[ 0, -2, -2],
          [ 0,  1,  4]])
```
#### 割り算
```python:division.py
a = np.array([0, 1, 2])
c = np.array([[1, 2, 3],
              [4, 5, 6]])

a / 2
>>>array([0. , 0.5, 1. ])

a / c
>>>array([[0.        , 0.5       , 0.66666667],
          [0.        , 0.2       , 0.33333333]])
```

#### べき乗
```python:exponentiation.py
b ** 2
>>>array([[9, 4, 1],
          [0, 1, 4]])
```

#### 平均値
```python:mean.py
c = array([[1, 2, 3],
           [4, 5, 6]])

np.mean(c)
>>>3.5
```

#### ドッド積
m行n列 n行m列 のような行列は掛け算が可能。

```python:dotproduct.py
a = np.array([0, 1, 2])
b = np.array([[-3, -2, -1],
              [ 0,  1,  2]])
b @ a
>>>array([-4,  5])
# b @ aは(2, 3) @ (1, 3)となり、行列積の条件を満たさないように見えるが、
# 実際にはnumpyは1次元配列と2次元配列のドット積を計算する際に特別な処理を行う。
# 具体的には、1次元配列aが2次元配列bとドット積を計算する場合、numpyはaを(3,)として扱い、bの各行とaのドット積を計算する。
d = np.array([[0, 1],
              [2, 3],
              [4, 5]])
# 2行3列× 3行2列なので2行2列の配列が返却される m,n・n,m = m,m
b @ d
>>>array([[ -8, -14],
          [ 10,  13]])

# 3行2列× 2行3列なので3行3列の配列が返却される m,n・n,m = m,m 
d @ b
>>>array([[  0,   1,   2],
          [ -6,  -1,   4],
          [-12,  -3,   6]])
```

### 判定
```python:decision.py
a = np.array([0, 1, 2])
b =  np.array([[-3 -2 -1],
               [ 0  1  2]])

a > 1
>>>array([False, False,  True])

# 0以上の要素数をカウント
np.sum(b > 0)
>>>2

# 配列要素いずれかがTrueの場合Trueを返す
np.any(b > 0)
>>>True

# 全ての要素がb > 0を満たすか
np.all(b > 0)
>>>False

# 条件に合致した配列のみを出力する
b[b > 0]
>>>array([1, 2])

# count_nonzero関数 0より大きい値の要素数を返す。
np.count_nonzero(b > 0)
>>>2

# 各要素を比較
b == c
>>>array([[False, False, False],
       [False, False, False]])

# ブロードキャストによって配列を変形し判定される
a == b
>>>array([[False, False, False],
       [ True,  True,  True]])

c = array([[1, 2, 3],
           [4, 5, 6]])

# |(または)でbとcまたはaとcが等しい場所を見つける。
# b == c にはTrueがないので評価されず、a == cの2列目がTrueになるので評価され、
# 返される値は2行目がTrueになる。
(b == c)|(a == b)
>>>array([[False, False, False],
       [ True,  True,  True]])


# allcloseは各要素の絶対値を比較し差が非常に小さい、近似的に等しい(1e-05の範囲)時にTrueを返す。
# 1e-05は科学的記数法で、1 * 10^-5を表し、これは0.00001と等しい
# allclose関数がTrueを返すのは比較対象の絶対値が0.00001以下の限りなく等しい場合になる。
np.allclose(b,c)
>>>False

# 比較対象となる配列の絶対値の範囲を指定できる。
# 以下は10以下の場合Trueとみなす。
np.allclose(b,c, atol=10)
>>>True
```