Python備忘録

何度も使うのに正確な書き方忘れてしまう内容リスト

## よく使うライブラリの略称

```python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import pymatgen as mg
```
## コマンドラインオプション

```console
python3 -O # __dubug__ にFalseを代入
python3 -u # stdout, stderrをバッファしない
```

## システムコマンド
IPython, Jupyterにおいて、システムのコマンドを`!`に引き続いて書くことで実行できる。
[Jupyter Notebookでシステムコマンドを実行し文字列のリストとして取得 | note.nkmk.me](https://note.nkmk.me/jupyter-notebook-system-command/)

例えば、

```python3
!python ./test.py > log.log
```
とすると、jupyter上でも、`test.py`を実行して、その標準出力をファイルにリダイレクトできる。
マジックコマンドで`%run ./test.py > log.log`だと、こうした挙動にはならない

## ディレクトリの作成
https://docs.python.org/ja/3/library/os.html#os.mkdir

```python3
import os
dir_path = 'path/4/directory/you/wanna/make'
os.mkdir(dir_path)                    #末端ディレクトリの親ディレクトリを自動生成しない
os.makedirs(dir_path)                 #末端ディレクトリの親ディレクトリを自動生成する
os.makedirs(dir_path, exist_ok=True)  #末端ディレクトリが既に存在していても構わずディレクトリを作成する
```

## ファイルを読み取り専用で開いてリストに格納

```python:version1.py
fname = 'file_name_to_be_opened_including_path'
f = open(fname,'r')
lines = f.readlines()
f.close()
Nlen = len(lines)
print(Nlen, ': No. of lines of the file, ',fname)
```

```python:version2.py
fname = 'file_name_to_be_opened_including_path'
with open(fname, 'r') as f: #withを使うとcloseまでセット
    lines = f.readlines()
    Nlen = len(lines)
    print(Nlen, ': No. of lines of the file, ',fname)
```


## `readlines`で読み込んだデータを配列に格納

```python
array1 = np.zeros(Nlen,dtype='int')
array2 = np.zeros(Nlen,dtype='float64')
array1 = np.zeros(Nlen,dtype='float64')
for i in range(Nlen):
    temp = lines[i].split()    #スペース区切りで文字列をリストに
    array1[i] = np.int(temp[0])
    array2[i] = np.float(temp[1])
    array3[i] = np.float(temp[2])
```

## ファイルの書き出し

```python
fname = 'file_name_to_be_opened_including_path'
f = open(fname,'x')
#f = open(fname,'w')
temp = 'Characters to be written.'
f.write(temp)
f.close()
```

## 浮動小数点を適当な桁数の文字列に変換

```python
str1 = '{:.5f}'.format(float1) #小数点以下5桁の文字列に変換
```
参考: [【Python】formatを用いた書式設定の基本  |  Hbk project](https://hibiki-press.tech/python/format/1015)

## ランダムなUUIDを生成

```python
import uuid
uuid.uuid4() #ランダムなUUIS
temp = str(uuid.uuid4()) #文字列に変換
```

## 辞書 (dict class)

```python
val1 = "hoge"
val2 = 2
val3 = 0.0+1.0j
d = {'key1': val1, 'key2': val2}
d.update({'key3': val3})   # 追加
print(type(d))
print(len(d))
print(d.keys())                   # キーのみ出力
print(d.values())                 # 値のみ出力
#
for k in d.keys():
    print(d[k], type(d[k]))
list(d.keys())             # dict_keys を listへ変換
#dit_keysはlistにせずとも、そのままで for k in d.keys() とすれば、動く
value = d['key1']          # value に val1を代入
print(value, type(value))
```

## JSON
辞書クラスには複素数の変数が入っていてもいいが、JSONに変換しようとすると、エラーで止まるのでちゅうい。

```python
import json
val1 = "hoge"
val2 = 2
d = {'key1': val1, 'key2': val2} # 辞書クラスを定義
d_str = json.dumps(d) #辞書 d をstringに変換
print(d_str)
f = open('test_json.json',"w")
json.dump(d, f, indent=2) #辞書 d を"test_json.json"にインデント2文字で書き込む
f.close()
f = open('test_json.json', "r")
d2 = json.load(f) #ファイル"test_json.json"に記載された内容を辞書 d2 にかくn
f.close()
```

## classの定義
[はじめに &mdash; pep8-ja 1.0 ドキュメント](https://pep8-ja.readthedocs.io/ja/latest/#id24)によると、

> クラスの名前には通常 CapWords 方式を使うべきです。


```python
class MyClass:
    """Explanation of this class"""
    variable = 'hogehoge'
    def __init__(self, val = 0.1):
        self.a = 'hoge'
        self.b = 10
        self.c = 1.0e2
        self.d = np.zeros(self.b)
        self.val = 1.0*val

    def func1(self):
        v = self.b + 1
        self.b += 1
        return v

    def func2(self,x):
        v = self.c + x
        self.c += x
        return v
MC = MyClass(val = 0.2)
print(MC.variable)
print(MC.a)
print(MC.func1())
print(MC.func2(2.0))
print(MC.d)
```

### 他のclassを継承

```python
class MyClass(AnotherClass):
```

### classの中身を確認

```python
import inspect
MC = MyClass()
members = inspect.getmembers(MC)
for a in members:
    print(a)
```

#### 変数の代入しそびれの回避
Classで、変数の初期値を`None`にしておけば、上記のclassメンバーの確認をすることで、メンバーに`None`がいるかどうか確認できる。具体的には

```python:check_class_member.py
import inspect
members = inspect.getmembers(data)
for a in members:
    if (a[1] is None):
        print(a)
```
で、メンバーが`None`になっているものは出力される。

### `@property`を使ったクラス内変数の処理
まだきちんと理解していない。
[「関数型プログラミング」と「オブジェクト指向」ってなんやねんPython編 - Qiita](https://qiita.com/nokonoko_1203/items/1cbce5566a6ea0a4029b)
[python property - Qiita](https://qiita.com/Sylba2050/items/d6f23ac13a0cc5da0c17)

### `@classmethod`クラスメソッド
クラスからメソッドを呼び出すときは`MyClass.func1()`として呼び出すことはできず、`MC = MyClass`としてインスタンスにしてから`MC.func1()`として呼び出す。`@classmethod`のデコレーターを使うことで、`MyClass.func1()`のように呼び出すことができるようになる。
[Pythonのクラスメソッド(class method)の定義の仕方とstaticmethodとの違い - St_Hakky’s blog](https://www.st-hakky-blog.com/entry/2017/11/15/155523)

## listの操作

```python
l = []         #空list, lの宣言
l.append(hoge) #lに要素 hoge を追加
```

## Numpy関数
```python
import numpy as np
np.arange(xs,xe,dx)
np.linspace(xs,xe,Nx)
np.linspace(xs,xe,Nx,endpoint = True)
np.round(real_value,2) #real_valueを小数点以下2桁で丸める
np.append(a,b) #aにbを追加したarrayを生成
np.array([ [0.0, 1.0, 2.0] , [3.0, 4.0, 5.0]]) #2x3のnumpy arrayの生成
np.array(some_list) #リストsome_listをnumpy arrayに変換
np.argmax(x) # xの最大値を与える引数を返す
np.random.seed(seed=123) #np.randomのシードを123に固定
np.random.rand(10) #乱数を成分に持つ要素数10の一次元numpy array
np.random.rand(10, 20) #乱数を成分に持つ10x20のnumpy array
np.clip(x, minval, maxval) #array xの値をminvalからmaxvalに制限したarrayをかえす。minval, maxalのどちらかを指定したくない場合は None にする
np.where(np.abs(x)<0.1, 0.0, x) #array xの絶対値が0.1よりも小さいときは0にし、そうでないときはそのままxの要素を返す関す
np.append(a,b, axis=0) #二次元配列aの列方向右に二次元bを追加
np.append(a,b, axis=1) #二次元配列aの行方向下に二次元bを追加
np.angle(c) #複素数cの偏角をradで返す
```
### numpy array (a1[n,m,1])を(a2[n,m])にしたい場合

```python
a2 = a1[:,:,0]
```

### numpy arrayの部分配列の切り取り方

[n,m]の配列aについて

```python
b = a[n, 0] #一次元配列になる
b = a[n, 0:1] #[n,1]の二次元配列になる
```

### `save`/`load`
```python
# tt, Jtを filename に保存
np.savez('filename', tt=tt, Jt=Jt) #複数のndarrayを一つのファイルに保存するときはsavez関数
# tt, Jtを filename から読込
temp = np.load('filename')
print(temp.files) # ['tt', 'Jt']
tt = temp['tt']
Jt = temp['Jt']
```

### fft
[Discrete Fourier Transform (numpy.fft) &#8212; NumPy v1.24 Manual](https://numpy.org/doc/stable/reference/routines.fft.html)

```python
t = np.arange(0.0, tend, dt)
ft = f(t)
# dt = t[1] - t[0]
Nt = len(t)
omega = np.fft.fftfreq(NT)*(2.0*np.pi/dt)
fF = np.fft.fft(ft)
```

$$
A_k = \sum_{m=0}^{n-1}a_m \exp \left( -2\pi \mathrm{i} \frac{mk}{n} \right) \\\\
a_m = \frac{1}{n}\sum_{k=0}^{n-1}A_k \exp \left( +2\pi \mathrm{i} \frac{mk}{n} \right)
$$
$\sum_{k=0}^{n-1} \left| A_k \right|^2 = n\sum_{m=0}^{n-1} \left| a_m \right|^2$

## Scipyの呼び方
例えば`scipy.special.ellipk`を呼びたいとき

```python
from scipy import special
hogehoge = special.ellipk(hoge)
```
あるいは

```python
from scipy.special import ellipk
hogehoge = ellipk(hoge)
```
## matplotlib
いろんなAPIのプロット例: [matplotlibのpyplot APIをいろいろ試す - そうなんでげす](https://www.soudegesu.com/python/pyplot-api/)
### matplotlibでグリッドをつけてプロット

```python
import matplotlib.pyplot as plt
plt.figure()
plt.xlabel('hoge',fontsize = 18)
plt.ylabel('hoge',fontsize = 18)
plt.xticks(fontsize = 18)
plt.yticks(fontsize = 18)
plt.plot(x,y,label='label',linewidth = 3.0)
plt.grid()
plt.legend(fontsize = 18)
plt.savefig("output.pdf")
plt.show()
```

```python
plt.plot(x,y, 'o') # plot w/ points
plt.plot(x,y, 'o', linestyle='solid') # plot w/ line-points
plt.fill_between(x,y1,y2,facecolor='k',alpha=0.25,label='label')
plt.fill_between(x,np.amin(y)*np.ones(len(y)),y,facecolor='k',alpha=0.25,label='label')
plt.quiver(x,y,dx,dy) #ベクトル場 plt.arrowよりもこちらの方が向いている
plt.scatter(x,y) #散布図
```

```python
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') #凡例を描画領域の外に出せる
```

```python
plt.xlim(left=0.0,right=1.0)
plt.ylim(bottom=0.0,top=1.0)
```

```python
plt.axis('scaled') #グラフの縦横の長さをそろえる
plt.axis('off') #軸を消す
plt.gca().set_aspect('equal', adjustable='box') #https://qiita.com/show2214/items/064c671709dda08d78c8
```

```python
plt.title('(a)', loc='left',fontsize = 24, x=-0.2, y=1.00) #図に(a)とか(b)をつける
```

[Matplotlibで出力するPDFの下のほうが切れてしまうときは bbox_inches=&#39;tight&#39; - Scala日記](http://ym.hatenadiary.jp/entry/2018/08/16/111825)

```python
plt.savefig("output.png", bbox_inches="tight")
```
### `contourf`をjetカラーマップでプロット

```python
import matplotlib.pyplot as plt
plt.figure()
plt.contourf(x, y, 2Ddata, cmap=plt.cm.jet)
plt.colorbar()
plt.show()
```
```python
import matplotlib.pyplot as plt
from matplotlib import cm, colors
Nlevel = 41 #レベルの分割数
minval = 1.0e-9
maxval = 1.0e-1
lev_exp = np.linspace(np.floor(np.log10(np.abs(minval)) ), np.ceil(np.log10(np.abs(maxval)) ), Nlevel)
levs = np.power(10, lev_exp)
plt.contourf(x, y, 2Ddata, levs, norm=colors.LogNorm(), cmap=plt.cm.jet)
plt.colorbar()
plt.show()
```

#### 2次元プロット用グリッド生成
```python
nx, ny = (30, 20)
x = np.linspace(-2, 2, nx)
y = np.linspace(-1, 1, ny)
x2D, y2D = np.meshgrid(x, y, indexing="ij") #indexingを"xy"にすると形状が転置される
print(nx, ny)
print(x.shape, y.shape)
#print(x,y)
print(x2D.shape, y2D.shape)
#print(x2D, y2D)
import matplotlib.pyplot as plt
plt.show()
plt.figure()
plt.contourf(x2D, y2D, x2D**2+y2D**2, cmap=plt.cm.jet)
plt.colorbar()
plt.gca().set_aspect('equal', adjustable='box') #https://qiita.com/show2214/items/064c671709dda08d78c8
plt.show()
```

### `matshow`

```python
import matplotlib.pyplot as plt
plt.matshow(2Darray,cmap=plt.cm.jet)
plt.colorbar()
plt.show()
```

### 三次元のプロット
三次元ベクトル`al`で決まる直方体のプロット。

```python
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

al = np.zeros(3, dtype='float64')
al[0] = 1.0
al[1] = 2.0
al[2] ~ 3.0

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.plot([0.0, al[0]],[0.0, 0.0],[0.0, 0.0],'k--')
ax.plot([0.0, 0.0],[0.0, al[1]],[0.0, 0.0],'k--')
ax.plot([0.0, 0.0],[0.0, 0.0],[0.0, al[2]],'k--')
ax.plot([0.0, al[0]],[al[1],al[1]],[0.0, 0.0],'k--')
ax.plot([0.0, 0.0],[0.0, al[1]],[al[2],al[2]],'k--')
ax.plot([al[0], al[0]],[0.0, 0.0],[0.0, al[2]],'k--')
ax.plot([0.0, al[0]],[0.0, 0.0],[al[2],al[2]],'k--')
ax.plot([al[0], al[0]],[0.0, al[1]],[0.0, 0.0],'k--')
ax.plot([al[0], al[0]],[0.0, 0.0],[0.0, al[2]],'k--')
ax.plot([0.0, 0.0],[al[1],al[1]],[0.0, al[2]],'k--')
ax.plot([0.0, al[0]],[al[1],al[1]],[al[2],al[2]],'k--')
ax.plot([al[0], al[0]],[0.0, al[1]],[al[2],al[2]],'k--')
ax.plot([al[0], al[0]],[al[1],al[1]],[0.0, al[2]],'k--')
ax.axis('equal')
plt.show()
```

### 数式のフォントにComputer Modernを使いたい
フォント自体は以下からダウンロードできる
[Using Computer Modern on the web](https://www.checkmyworking.com/cm-web-fonts/)

#### フォント周りのコマンド

```python
# 関連pathの表示
import matplotlib
matplotlib.matplotlib_fname()
## 使えるフォントリストの表示
import matplotlib.font_manager as fm
import pprint
font_list = [f.name for f in fm.fontManager.ttflist]
pprint.pprint(font_list)
```

## Path

### コードの中でpathを追記したい場合
```python
import os
import sys
import pprint

sys.path.append(os.path.dirname('/path/to/be/added'))
pprint.pprint(sys.path) #Pathの確認
```

### 環境変数で追記したい場合
```bash
export PYTHONPATH=/path/to/be/added:$PYTHONPATH
```

## module

```bash
.
├── dir1/
│   ├── dir2/
│   │   └── module2.py
│   └── module1.py
└── main.py # <= 呼び出し側
```
の`module1.py`と`module2.py`をimportしたい場合は

```python
from dir1 import module1
from dir1.dir2 import module2
```
[Python で別ファイルの import](https://tex2e.github.io/blog/python/import-files)

### `/some/path/module3.py`を呼びたいとき
```python
import sys
sys.path.append('/some/path')
from path import module3
```

### moduleのreload
コードの開発時、デバッグ時にはモジュールも変更してその挙動を確認したい。
一方、デフォルトでは`import my_module`で、moduleを読み込むと、ipythonやjupyter上ではメモリ上に確保されて、`my_module.py`の変更を反映しない。この変更分を反映させるための方法。

#### コードの中でインタラクティブにリロード
この変更を反映させるためには`importlib`を使う

```python
import importlib
importlib.relaod(my_module)
```
で、`my_module.py`の変更を反映。
引数はモジュールでないとだめで、個別の関数等は引数にとれない。例えば`from my_module import my_func`のようにして、`my_func`をインポートしていて、`my_module`をインポートしていない場合には相性が悪い。その時は、ipython, jupyter notebookを使っている場合は、以下の方法をつかう。

#### マジックコマンドで挙動を指定(ipython, jupyter notebook)
以下のマジックコマンドを付けておけば、モジュールは`%run *.py`で実行毎に読み込まれる。コードの開発時用。

```python
%load_ext autoreload
%autoreload 2
```

[ipython を起動しながら自作モジュールを修正した場合 - Qiita](https://qiita.com/Accent/items/f6bb4d4b7adf268662f4)

### `__init__.py`の役目
なんだか、小難しい。要再勉強。
[Python の __init__.py とは何なのか - Qiita](https://qiita.com/msi/items/d91ea3900373ff8b09d7)

### module間で変数や関数を共有
`config.py`のようなものを作って、他のmoduleからはこれを`import config`で呼ぶ。
[プログラミング FAQ &#8212; Python 3.8.2 ドキュメント](https://docs.python.org/ja/3/faq/programming.html#how-do-i-share-global-variables-across-modules)

##  時間計測
```python
import time
ts = time.time()
te = time.time()
elapse_time = te - ts # in sec.
```


## 単位変換リスト

```python
import sys
import numpy as np
import math
import matplotlib.pyplot as plt
#Mathematical constants
pi = np.pi
tpi = 2.0*pi
fpi = 4.0*pi
zI = 1.0j
#Physical constants
#https://ja.wikipedia.org/wiki/%E5%8E%9F%E5%AD%90%E5%8D%98%E4%BD%8D%E7%B3%BB
Bohr2nm = 0.0529177210903 #nanometer
Hartree2eV = 27.211386245988 #eV
Atime2fs = 0.024188843265857 #fs
Afield2Vpnm = Hartree2eV/Bohr2nm #V/nm
Avolume2nm3 = Bohr2nm**3 #nm^3
#https://ja.wikipedia.org/wiki/%E5%BE%AE%E7%B4%B0%E6%A7%8B%E9%80%A0%E5%AE%9A%E6%95%B0
sol = 137.035999084 #speed of light
ch_eVnm = 1241.5 #eV * nm
chbar_eVnm = 197.3 # eV * nm
halfepsc_Aunit2Wpcm2 = 3.509e16 # W/cm^2 \frac{1}{2}*\epsilon_0 * c
Afluence2Jpcm2 = halfepsc_Aunit2Wpcm2*Atime2fs*1.0e-15 # J/cm^2 ,W/cm^2 * fs = femto J/cm^2
```

### 一度代入した変数に再代入しようとすると停止するプログラム
定数用のclassを用意して、二回目の代入でシステムを停止する処理を記載する。

[Python で定数を定義する | まくまくPythonノート](https://maku77.github.io/python/syntax/const.html)


## Jupyter

```bash
jupyter notebook --port 8888 #Jupyter notebook をport番号8888で開く
```

## pip

```console
pip install pymatgen
pip install pymatgen==2020.10.20 #バージョンの指定
pip list #インストールされたもののリストを表示
pip freeze #pip listに近いやつ
```

## デバッグあれこれ
### 組み込み定数 `__debug__`
コード内の`__debug__`は`python3 -O hoge.py`では`False`が代入されて、通常の実行では`True`が代入されている。

```python:hoge.py
if not __debug__ :
    some_debug_procedure()
```
としておくと、`python3 -O hoge.py`としたときだけ、`some_debug_procedre()`が呼ばれる。