レポートで使う Python 大全（numpy や matplotlib など）

私的メモ。

## Jupyter

### 環境構築

`jupyter_contrib_nbextensions` は、後述する pdf 出力用や、 Jupyter で補完を効かせるためなどに入れるもので、必須ではない

```bash
pip install jupyter jupyter_contrib_nbextensions
pip install matplotlib numpy # その他必要な物あれば追加
```

### ブラウザで Jupyter を起動

ブラウザではなく VSCode 内で良いのではないかと思いつつ、結局乗り換えていない。

```bash 
jupyter notebook --ip=127.0.0.1 --allow-root
```

### Jupyter のテーマを変更する

`jupyterthemes` モジュールを入れてもいいし、自分で `~/.jupyter/custom/custom.css` をいじってもいい。

前者の場合
・[Jupyter Notebookの背景色やフォントをかっこよく変更する](https://qiita.com/uratatsu/items/cdc83308ce85a32d0512)
とかが参考になる。

私は https://gist.github.com/HelloRusk/54f5e0b60e39ae8826bfcc580d524e40 を custom.css として使っている。

### Jupyter を pdf として出力する

```bash 
jupyter nbconvert hoge.ipynb --to pdf
```

## おまじない

```python 
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

from tqdm.notebook import tqdm
```

Jupyter Notebook でプログレスバーを出す時は、`tqdm.notebook` を使う方が見やすい。

## numpy

### べき乗

```python
np.power(2, 3) # 8
```

### 相関係数

```python
x = np.array([
  [1, 2, 1, 9, 10, 3, 2, 6, 7],
  [2, 1, 8, 3, 7, 5, 10, 7, 2]]) 

np.corrcoef(x)
```

返り値は相関係数行列になっているので、(1, 2)要素（または(2, 1)要素）を確認する。


### 最大（小）値を取る index

argmax, argmin がある。

```python
np.argmax(arr)
```

### 等差数列

リスト内包表記で作っても全然いいのだけど、`np.arange` がある。

```python
np.arange(5)
# array([0, 1, 2, 3, 4])

np.arange(3, 10, 2)
# array([3, 5, 7, 9])
```

### 指定区間を N 等分した数列

```python 
np.linspace(2.0, 3.0, num=5)
# array([2.  , 2.25, 2.5 , 2.75, 3.  ])
```

### 等比数列

```python
np.logspace(3, 7, num=5, base=2)
# array([8, 16, 32, 64, 128])
```

### 要素の平均を取る

`np.mean` と `np.average` のどちらでもよい。
`axis=0` では行方向の平均を取る（すなわち `N * M` 行列が `1 * M` 行列になる）。

```python
a = np.array([[33, 44, 54, 23],
              [25, 55, 32, 76]])

np.average(a, axis = 0)
# array([ 29. ,  49.5,  43. ,  49.5])

np.average(a, axis = 1)
# array([ 38.5,  47. ])

# np.mean でも同じ
```

### 行列の初期化

引数がタプルであることに注意

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

全要素を1で初期化する`np.ones`もある。

### ベクトルの内積, 行列の積

いずれも `np.dot`

```python 
np.dot(A, B)
```

`@` 演算子も使える。

### ベクトル・行列の連結

色々紛らわしいので https://note.nkmk.me/python-numpy-concatenate-stack-block/ を見た方がよい。`numpy.stack` を使った方が良い説がある。

```python
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])

np.concatenate([v1, v2])
# [1 2 3 4 5 6]
```

引数全体がリストである必要がある点に注意

### 多次元配列を一次元配列につぶす

`np.ravel` が組み込みの `flatten` よりも速い。

```python 
x = np.array([[1, 2, 3], [4, 5, 6]])
np.ravel(x)
# array([1, 2, 3, 4, 5, 6])
```

### ベクトルの長さ（ノルム）

普通使うのは2ノルム

```python 
np.linalg.norm(X)
np.linalg.norm(X,ord=1) # 1ノルム
```

ちなみに、`np.linalg.norm` は行列のノルム[^2] にも対応している。

[^2]: https://ja.wikipedia.org/wiki/%E8%A1%8C%E5%88%97%E3%83%8E%E3%83%AB%E3%83%A0

### 行列の列の抽出

`mat[:,2]` みたいに書く

```python
mat =\
[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]
 [20 21 22 23 24 25 26 27 28 29]
 [30 31 32 33 34 35 36 37 38 39]
 [40 41 42 43 44 45 46 47 48 49]
 [50 51 52 53 54 55 56 57 58 59]
 [60 61 62 63 64 65 66 67 68 69]
 [70 71 72 73 74 75 76 77 78 79]
 [80 81 82 83 84 85 86 87 88 89]
 [90 91 92 93 94 95 96 97 98 99]]

mat[:,2]
# array([ 2, 12, 22, 32, 42, 52, 62, 72, 82, 92])
```

### 配列から特定の要素を削除

```python 
test = np.array([1, 2, 3, 4, 5])
np.delete(test, [1, 3])
# array([1, 3, 5])
```
**スライスとして削除することはできない！！**

### 単位行列

```python
np.identity(5)
np.eye(5)
```

どっちでもいい。

### 逆行列

```python
np.linalg.inv(A)
```

### 転置行列

```python 
A.T
```

### 行列式

```python
np.linalg.det(A)
```


### 固有値・固有ベクトル

| 関数 | 説明 | 
| :--- | :--- | 
| np.linalg.eig | 一般行列の固有値・固有ベクトル | 
| np.linalg.eigh | エルミートor実対称行列の固有値・固有ベクトル | 
| np.linalg.eigvals | 一般行列の固有値<br>（**固有ベクトルは計算しない**）|
| np.linalg.eigvalsh | エルミートor実対称行列の固有値 <br>（**固有ベクトルは計算しない**）|


対称行列では固有値を求めるアルゴリズムが一般行列と異なるので、精度のためにも、**対称行列では eigh, eigvalsh を使うようにする**。

なお、固有値と固有ベクトルを両方求めるメソッドでは、返り値が `(固有値, 固有ベクトル)` のタプルとして返ってくる。

一般化固有値問題を解きたい場合は `scipy.linalg.eig` を用いる。

```python
from scipy import linalg
linalg.eig(A, B)
```

なお、固有ベクトルを取る際は**縦ベクトル**を取る必要があることに注意！！！
すなわち、`i` 番目の固有値 `w[i]` に対応する固有ベクトルは `v[:,i]` となる。


### 乱数

#### 正規分布に従う乱数

```python
np.random.normal(size = 1000000)
np.random.normal(loc=1.0, scale=2.0, size=20) 
```

多変量正規分布の場合、`multivariate_normal` を使う

```python
mean = (1, 2)
cov = [[1, 0], [0, 1]]
np.random.multivariate_normal(mean, cov, 100)
```

#### 一様分布に従う乱数

```python
np.random.uniform(size = 1000000)
np.random.uniform(low=1.0, high=2.0, size=20) 
```

値をひとつ返したい時は

```python
np.random.rand()
```

とする。

#### カイ二乗分布に従う乱数

```python 
np.random.chisquare(2, 10000)
```

### その他雑多なネタ

#### 連立一次方程式を解く

```python 
a = np.array([[3,1], [1,2]])
b = np.array([9,8])
np.linalg.solve(a, b)
# array([2.,  3.])
```

#### 行列の変形

(1, 12)サイズの行列を(3, 4)サイズに変形するとか。

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

#### 条件数

数値解析で、誤差の影響の受けやすさの指標となる値（値が大きいと誤差に弱いことが言える）

```python
np.linalg.cond(A, 2)
```

## matplotlib

### 基本的なグラフ

```python 
plt.figure(figsize=(8, 6)) # 横8×縦6のグラフ
plt.title("This is title.") # グラフにタイトルを付ける

# 軸に名前を付ける
plt.xlabel("試行回数")

# 軸の範囲を変更する
plt.ylim(0, 2.5)

# label= で凡例、color= で色、linestyle= で線のスタイルを変更
plt.plot(X, Y, label="one") 
plt.plot(X2, Y2, label="two")

plt.legend()
plt.show()
```

### 対数グラフ

```python
plt.yscale('log')
```

で y 軸が log スケールになる。

### ヒストグラム

```python 
plt.hist(x, bins=100)
```

`bins`はビン数（階級の数）

### 散布図

```python 
plt.scatter(x, y, marker='x', c='red')
```

### 等高線

最適化の様子を描く時とかで、補助的に等高線を入れたいことがある。

```python 
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 100)
 
X, Y = np.meshgrid(x, y)
Z = np.sqrt(X**2 + Y**2)

plt.contour(X, Y, Z)
plt.gca().set_aspect('equal')
```

等高線はかなり難しいので http://ailaby.com/contour/ をよく読んだ方がいい。

## その他 Python コーディングテクニック

### ファイルを一行ずつ処理

```python
f = open("sample.txt", "r")

for line in f:
    # example
    print(line.strip())
    # strip() はデフォルトでは両端の空白文字を取り除く 

f.close()
```

### 複数の list をまとめてループ（zip）

```python 
names = ['Alice', 'Bob', 'Charlie']
ages = [24, 50, 18]

for name, age in zip(names, ages):
    print(name, age)
# Alice 24
# Bob 50
# Charlie 18
```

### インデックス付きループ（enumerate）

```python 
l = ['Alice', 'Bob', 'Charlie']

for i, name in enumerate(l):
    print(i, name)
# 0 Alice
# 1 Bob
# 2 Charlie
```

### format 文

```python 
print("i={} lambda={} x={}".format(i, l, x))
```

あるいは

```python 
print(f"i={i} lambda={l} x={x}")
```

### 数学関数

一般的な関数は numpy のものでなくとも普通に `math` モジュールにある。詳しくは https://docs.python.org/ja/3/library/math.html を確認

```python 
from math import sin, cos, pi, exp
```