Python基本+α操作 備忘録

#概要
C/C++やVBなどに慣れてしまった鶏脳にはなかなかPython(特にnumpy)が入ってこない。
入ってこない部分について、いつも調べていることや、発見したこと随時更新していく。

##listとndarray
list型はPython標準の配列チックなやつで、ndarray型はnumpyの配列チックなやつ。
変数名は前者に"lst*"、後者に"arr*"とする。


###【list型】
####宣言
```python
lst1 = list()
```

####代入(1次元配列)
```python
lst1 = [1, 2]
lst2 = [5, 6]
```

####代入(2次元配列)
例えば初めから2次元にすることがわかっていたら下記

```python
lst3 = [[10, 11]]
lst4 = [[20, 21]]
```

なお、list型から下記のように取得することも可能
他の言語に慣れたひとから見ると、気持ち悪い。

```python
lst5 = [1, 2]
a, b = lst5
lst6 = [[11, 12], [21, 22]]
c, d = lst6[0]
e, f = lst6[1]
```
a, bにはそれぞれ1, 2が、c, dには11, 12が、e, fには21, 22が代入される。
 

####追加(1次元配列)
```python
lst1 = [1, 2]
lst1.append(3)
```
lst1は*[1, 2, 3]*となる。
appendメソッドの結果に戻りはなく、直ちにlst1に作用されるので下記ではない。
lst1 = lst1.append(3)
直感的にはlst1にappendした結果が再代入され、[1, 2, 3]となるように読める。
しかし、なぜかlst1はNoneとなりイライラする。
後述するが、numpyで追加するときは必ず下記としなければならない。
**arr1 = np.append(arr1, 3)**

引数に、もちろんlist型を渡してもよい。

```python
lst1 = [1, 2]
lst2 = [5, 6]
lst1.append(lst2)
```
lst1は*[1, 2, 5, 6]*となる。


####追加(2次元配列)
次元の1つ大きな配列に次元の1つ小さな配列をappendしてあげる。
2次元のlst3に1次元のlst1をappendすると

```python
lst1 = [1, 2, 3]
lst3 = [[10, 11]]
lst3.append(lst1)
```
lst3は*[[10, 11], [1, 2, 3]]*となる。


####拡張追加(1次元配列)
extendメソッドで次元を維持したまま連結する。同じ次元の配列を使用する。

```python
lst1 = [1, 2, 3]
lst2 = [5, 6]
lst1.extend(lst2)
```
lst1は*[1, 2, 3, 5, 6]*となる。
extendメソッドの引数はtuple型も許される。

```python
lst1 = [1, 2, 3]
lst1.extend((5, 6))
```
lst1は*[1, 2, 3, 5, 6]*となる。
**lst1.extend(5)**のように直に実数を引数にするとエラー

####拡張追加(2次元配列)
同様に、

```python
lst3 = [[10, 11]]
lst4 = [[20, 21]]
lst3.extend(lst4)
```
lst3は*[[10, 11], [20, 21]]*となる。
appendもそうだが直接変数を書き換えてしまうので、場合によっては下記のようになるだろう。

```python
lst3 = [[10, 11]]
lst4 = [[20, 21]]
lstWork = lst3 
lstWork.extend(lst4)
```
list配列の型に合わせてappendとextendを使い分ければよい。

####ベクトルで計算
ここでいうベクトルとはlist型のこと
標準関数（カスタム関数も）で引数がスカラはOKでベクトルはNGなものをベクトルもOKにする。

```python
A = 10
lstA = [10, 10]

print(math.log(A))
print(math.log(lstA))
```
logの引数はスカラ値のみOKなので1行目のprint文はOKで、ベクトルはNGなので2行目はエラーとなる
でも[10, 10]を[log(10), log(10)]で計算させられればfor loopしなくても済む。
そこで下記のように元の関数をベクトルの引数も許すようにする。

```python
fncLog = np.vectorize(math.log)
print(fncLog(A))
print(fncLog(lstA))
```
1行目はスカラ値のlog(10)、2行目はベクトル値(list型)の[log(10), log(10)]が出力される。

###【ndarray型】
下記としておく。
import numpy as np

####宣言
**arr1 = numpy.empty((4,3), dtype=np.int)**のように書けば空配列を生成できるようなことが書かれている。
でもこれは型の宣言ではない。~~散々調べたが一般的な変数の宣言はないみたい。~~
list型と同様に宣言する。
**arr1 = []**

####代入
いったんlist型にして一発目はarrayに渡して代入する。

```python
lst1 = [1, 2, 3]
arr1 = np.array(lst1)
arr2 = np.array([4, 5, 6])
```

arrayが使えるのは一発目だけ。続けて同じ変数に対しarrayを実行すると上書きされる。

####追加
appendで追加する。
appendメソッドの第一引数（追加される側）、第二引数（追加する側）はndarray型でもlist型でもOK
ただ、返りはndarray型配列となる。

```python
lst1 = [1, 2, 3]
arr1 = np.array(lst1)
arr2 = np.array([4, 5, 6])
arr2 = np.append(arr2, arr1 , axis=0)
#または、
#arr2 = np.append(arr2, lst1 , axis=0)
```
arr2は*[4 5 6 1 2 3]*となる。
1次元配列をappendしたので結果も1次元
2次元（例えば2行3列）にしたければ、下記でそうなる。
**arr2 = arr2.reshape([2,3])**

もしくはlistの時と同様に両方2次元で用意すると下記

```python
lst1 = [[1, 2, 3]]
arr1 = np.array(lst1)
arr2 = np.array([[4, 5, 6]])
arr2 = np.append(arr2, arr1 , axis=0)
#または、
#arr2 = np.append([[4, 5, 6]], arr1 , axis=0)
```
arr2 は下記となる。
*[[4 5 6]
 [1 2 3]]*

ついでに、このarr2の次元を取り出すときは下記

```python
a, b = arr2.shape
```
arr2は2行3列なので、aとbには*2*と*3*が入る。


####次元の追加
Kerasのfit()やpredict()などの引数にnumpy型データを渡す際によく行う。

reshapeで追加する。
上述のarr2は、arr2.shape = (2,3)、中身は下記だが、これに1次元追加しarr3とする。
*[[4 5 6]
 [1 2 3]]*

```python
arr3 = arr2.reshape([1, arr2.shape[0], arr2.shape[1])
```
arr3.shape = は*(1,2,3)*となる。arr2は参照しただけなので変化ない。
また、arr3の中身は下記となる。
*[[[4 5 6]
  [1 2 3]]]*



1次元配列をappendしたので結果も1次元



####ループで代入

```python
lst10 = [i for i in range(10)]
#または、
#for i in range(10):
#  lst10 = [i]
arr10 = []
arr10 = np.append(arr10, lst10, axis=0)

#一列にしたいなら、
#arr10 = arr10.reshape([10,1])
```

arr10は*[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]*となる。
なお、appendメソッドの第一引数は宣言済みでなければエラーが出る。
*NameError: name 'arr10' is not defined*　←相当はまったエラー
また、list型にはnumpyのreshape相当の機能がないため、上記のようにnumpy型に変換してreshapeするか、[相当のものを自前で実装する]("https://note.nkmk.me/python-list-ndarray-1d-to-2d/")か。

####reshapeの方向
上記に続き、２列にしたいとき、order引数で方向を指定する。
arr10は*[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]*を流用する。

```python
arr11 = arr10.reshape([5,2])
arr12 = arr10.reshape([5,2], order='F')
```
arr11はデフォルトなので下記のように行方向に埋められる。
*[[0. 1.]
 [2. 3.]
 [4. 5.]
 [6. 7.]
 [8. 9.]]*
arr12はorder='F'指定なので下記のように列方向に埋められる。
*[[0. 5.]
 [1. 6.]
 [2. 7.]
 [3. 8.]
 [4. 9.]]*



####配列同士の結合
よく使うので書いておく。両メソッドの引数は[ ]でくくる必要があり、これを忘れる。
2つのndarray型配列を横方向（hstack）か、縦方向（vstack）に結合する。
どちらの配列も次元は同じでないとエラーがでる。
どちらも行数や列数は一致していないとエラーが出る。パディングして整形してくれたりしない。

```python
arr1 = np.arange(12).reshape((4, 3))
arr2 = arr1 + 100
arrh = np.hstack([arr1, arr2])
arrv = np.vstack([arr1, arr2])
```
arrhは横方向につないだので下記が入る。
*[[  0   1   2 100 101 102]
 [  3   4   5 103 104 105]
 [  6   7   8 106 107 108]
 [  9  10  11 109 110 111]]*

arrvは縦方向につないだので下記が入る。
*[[  0   1   2]
 [  3   4   5]
 [  6   7   8]
 [  9  10  11]
 [100 101 102]
 [103 104 105]
 [106 107 108]
 [109 110 111]]*


###【任意の行、列の参照】
ここでは、列ごとに意味が異なる配列であることを想定し、列を切口とした処理を書く。
例えば1列目から、

　開始時刻, 終了時刻, 地域ID, 気温, 気圧, などなど

において、気温が20~21の行のみ抽出する処理のイメージ
データサイズが小さければエクセルで簡単に処理できるが、データサイズが大きいとファイルを開くだけでも時間がかかり実用的でなく、そもそもエクセル上限の**1,048,576行 X 16,384列**に収まる必要がある。
毎秒ロギングした1年分のデータですら31,536,000行に達しエクセルでは開けない。
pythonであればPCの利用可能メモリいっぱいまで使用できるし、numpyはこの手の処理が後述のように数行で記述でき、処理も尋常になくに速い。
また、メモリに乗り切れないほど大きくても[pandasなら処理可能]("http://sinhrks.hatenablog.com/entry/2014/11/21/231534")

####参照の基本

```python
arr1 = np.arange(12).reshape(4,3)
#行の指定
arr2 = arr1[2]
#または、 arr2 = arr1[2,:]
#列の指定
arr3 = arr1[:,2]
```
arr1には下記が入る。
*[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]*
arr2にarr1の2+1行目の*[6 7 8]*、arr3にarr1の2+1列目の*[ 2  5  8 11]*が入る。

####列同士の四則演算
例えばarr1の2列目と3列目をたしたものを改めて2列目とするa_addを求める。
列操作なのでaxis=1とする。

```python
arr4 = arr1[:, 1] + arr1[:, 2]                 # 1次の配列

a_del = np.delete(arr1, 1, axis=1)             # いったん2列目を削除して
a_add = np.insert(a_del, 1, arr4, axis=1)      # そこに計算結果を挿入
```


####条件にあう行数のカウント
arr1の0+1列目において、0より大きく, 9より小さな要素を数える。
列を指定したので、つまり行数となる。

```python
cnt = np.count_nonzero((arr1[:,0] >0) & (arr1[:,0] <9))
```
3と6がヒットするので、cntは2となる。
カッコ内の条件がorなら "|"とすること。

####条件にあう行のコピー
arr1の01+1行目において、値が4 または7の行を別のndarray変数にコピーする。

```python
arrrow = np.array(np.where((arr1[:,1]==4) | (arr1[:,1]==7)))[0]
arr4 = arr1[arrrow]
```
arrrowにはヒットした2,3行目を示す*[1 2]*が返り、arr4には下記が入ってその該当行がコピーされる。
*[[3 4 5]
 [6 7 8]]*

####条件にあう行の削除
考え方は上記のコピーと同じく、値が4 または7の行を削除する。

```python
arrrow = np.array(np.where((arr1[:,1]==4) | (arr1[:,1]==7)))[0]
arr1 = np.delete(arr1, arrrow, axis=0)
```
arr1は下記となる。
*[[ 0  1  2]
 [ 9 10 11]]*


####最大・最小のインデックスの取得
重複しないようにarr5にランダムの整数を入れておく。
その中の最小値、最大値が何番目にあるかインデックスを取得する。

```python
arr5 = np.random.rand(10)
print(arr5)

imax = int(np.argmax(arr5))
imin = int(np.argmin(arr5))
```

imax, iminにはそれぞれ最大値、最小値が格納されているインデックスが代入される。


####n番目に大きい・小さいインデックスの取得
先のarr5を宙でソートしてn番目を指定し、それをwhereで探す。
その中の最小値、最大値が何番目にあるかインデックスを取得する。

```python
n = -2     # ソートした後ろから2番目、つまり2番目に大きいやつ
arr_wk = np.array(np.where(arr5 == sorted(arr5.ravel())[n]))
inmax = int(arr_wk)

n = 1     # ソートした前から2番目、つまり2番目に小さいやつ
arr_wk = np.array(np.where(arr5 == sorted(arr5.ravel())[n]))
inmin = int(arr_wk)
```

inmax, inminにはそれぞれn番目に大きい値、小さい値が格納されているインデックスが代入される。

##その他のコンテナ
###【tuple型】
####代入
( )でくくる。
呼び出すときはindexを[ ]で指定する。

```python
tpl = ('A', 10)
print(type(tpl))
print(tpl[0])     #> A
a, b = tpl
print(a , b)      #> A 10
```

###【dictionary型】
####代入1
( )でくくる。dct = {'key1': 1, 'key2': 2, 'key3': 3}
呼び出すときはindexを[ ]で指定する。

```python
dic = {}            # 宣言
dic['key0'] = 0
print(dic['key0'])  #> 0
```
####代入2
複数キーがある場合

```python
dic = {}            # 宣言
dic = {'key1': 1, 'key2': 2, 'key3': 3}
print(dic['key1'])  #> 1
```

####追加
構文は代入1と同じ。
既にキーが存在していれば、valueが上書きされる。

```python
dic = {}            # 宣言
dic['key0'] = 0
print(dic)          #> {'key1': 1, 'key2': 2, 'key3': 3, 'key0': 0}
print(dic['key0'])  #> 0
dic['key0'] = 10    # 上書きされる
print(dic)          #> {'key1': 1, 'key2': 2, 'key3': 3, 'key0': 10}
```

####メソッドkeys()で参照と確保

```python
print(dic.keys())             #> dict_keys(['key1', 'key2', 'key3', 'key0'])
lst_keys = list(dic.keys())
print(lst_keys)               #> ['key1', 'key2', 'key3', 'key0']
## loopなら
for k in dct.keys():
  print(k)                    # keyを先頭から表示
  print(dct[k])               # そのvalueもついでに表示
```

指定したキーが見つからないとエラーになるので、メソッドget()でトラップする。

```python
a = dic['key5']               #> KeyError: 'key5'

a = dic.get('key5',999)       # キーがなければ999を返す。
print(a)                      #> 999
```

####メソッドitems()で参照と確保
keyとvalueが対の構成なので、両方を扱うときはtuple型になる。

```python
print(dic.items())            #> dict_items([('key1', 1), ('key2', 2), ('key3', 3), ('key0', 10)])
```
list()で抜き出してもlist型の中身はtuple型になる。

```python
lst_items = list(dic.items())
print(lst_items)               #> [('key1', 1), ('key2', 2), ('key3', 3), ('key0', 10)]
```

これを参照するときはこんな感じ

```python
for a, b in lst_items:
  print(a, b)                 # keyとそのvalueを表示

# 最初の[ ]はlistの配列で、次の[ ]はそれがtuple型なのでtupleの配列
print(lst[3][0])              #> key0
print(lst[3][1])              #> 10DataFrame
```

###【DataFrame型 (pandas)】
####初期化
DataFrameのcolumeだけ準備する。

```python
df = pd.DataFrame(columns=['key1', 'key2', 'key3', 'key4'])
```

####データの追加
行を追加する。

```python
df = df.append({'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4}, ignore_index=True)
df = df.append({'key1': 5, 'key2': 6, 'key3': 7, 'key4': 8}, ignore_index=True)
print(df)
```
下記が表示される

*　key1 key2 key3 key4
0    1    2    3    4
1    5    6    7    8*

####csvファイルに書き出し
Pathはファイル名csv付きのフルパス文字列

```python
df.to_csv(Path)
```