numpyとpandasの役立つ処理まとめ

# numpyとpandasの役立つ処理まとめ
実務で使う中で良いと思ったものをメモとして載せておきます。

## numpy 

### 数える：np.unique
- np.unique, return_countsが結構便利。
- pandasのvalue_counts、collectionsのCounterでもいいけど、numpyをimportしてない時はまずないので一番使いやすい。

 ````python
t=np.random.randint(0, 10, 10)
print(t)
#[2 0 9 9 8 1 3 2 1 8]
print(np.unique(t, return_counts=True))
#(array([0, 1, 2, 3, 8, 9]), array([1, 2, 2, 1, 2, 2], dtype=int64))
````

### 対数変換：np.log1p, np.expm1
- np.logよりnp.log1pのほうが役立つときもあるから覚えとく、対応はnp.expとnp.expm1

 ````python
t = np.array([2**i for i in range(-5, 5)])
print(t)
[ 0.03125  0.0625 0.125 0.25 0.5 1. 2. 4. 8. 16.]
print(np.log1p(t))
[0.03077166 0.06062462 0.11778304 0.22314355 0.40546511 0.69314718 1.09861229 1.60943791 2.19722458 2.83321334]
print(np.log(t))
[-3.4657359  -2.77258872 -2.07944154 -1.38629436 -0.69314718  0.  0.69314718  1.38629436  2.07944154  2.77258872]

````

### 小さい順の確認：np.argsort
- argsortでn番目の値がどこにあるかがわかる、t[np.argsort(t)]でソートになる
- argsortを二回繰り返すことでこの値がn番目なのかを表示できる
- 同値の場合はインデックスが小さい順

````python
pp = np.random.randint(0, 10, 10)
print(pp)  # [8 0 9 7 2 5 6 1 0 0]
print('argsort', pp.argsort())  # argsort [1 8 9 7 4 5 6 3 0 2]
print('何番目に小さいか', pp.argsort().argsort())  # 何番目に小さいか [8 0 9 7 4 5 6 3 1 2]
````

### one-hot化：np.eye
- np.eyeに数字の配列を渡すとto_categoricalと同値となる

````python
n =5
t = np.random.randint(0, n, 10)
np.eye(n)[t] #one-hot配列が出来上がる
````


## pandas

### 補間pd.interpolate

- [9, 10, 10, 10, 11, 11, np.nan, np.nan, 13, 15, 17,]みたいな整数の積算値でdiffを取ると「nan 1.0 0.0 0.0 1.0 0.0 nan nan nan 2.0 2.0」てなって美しくない。
- 少数としては増えてるというのを、変化したてないところをnanにした後にinterpolateで書く

````python
nn = pd.Series([9, 10, 10, 10, 11, 11, np.nan, np.nan, 13, 15, 17,])
print(*nn.diff()) # nan 1.0 0.0 0.0 1.0 0.0 nan nan nan 2.0 2.0

 #indexが0より後でdiffが正じゃないところをnanに変える
nn.loc[~(nn.interpolate(method='pad').diff()>0) & (nn.index!=nn.index[0])] = np.nan
print(*nn.interpolate().diff().round(3)) # nan 1.0 0.333 0.333 0.333 0.5 0.5 0.5 0.5 2.0 2.0
````

### 時間設定・集計：pd.todatetime, tz_convert, resample
- タイムゾーン大事
- resampledで時間ごとに集計可能
````python
df['datetime'] = pd.to_datetime(df['datetime'], utc=True)
df = df.set_index(['datetime'])
df.index = df.index.tz_convert('Asia/Tokyo')
df30 = df.interpolate(method='pad', axis=1).resample('30T').mean()
````
### チェーンエラー対策：pd.copy, values
- dfから一部抽出したいときはcopy()を使うことを意識する、SettingWithCopyWarning対策
- https://qiita.com/HEM_SP/items/56cd62a1c000d342bd70
- pythonはDF、list等は関数に参照で渡される。関数の中で参照かコピーか判明していないと不便なので初めにcopyするなど、明示的に連携を切る意識も大事
- 値だけ取り出すときはvalues
- 関数に渡してもpandasは中で書き換わるので、df = input_df.copy()としたりもする。メモリに余裕あれば。

````python
join_df30_light = join_df30[kido_yoso_1df.index].copy()
y = join_df30_light[target_col].values
x = join_df30_light.drop(target_col, axis=1).values
````

### ループ処理高速化 values
- pandasはアクセスがめちゃくちゃ遅い、valuesで変換してnumpyにして扱うのがよい
- https://kunai-lab.hatenablog.jp/entry/2018/04/08/134924
- itertuplesでも同じくらいの速度なのでこちらを使う場合も。itterowsは良くない。

````python
aa=pd.Series(np.random.rand(100000))
aa2=aa.values
for i in range(len(aa)):
    aa[i]  # 846ms
    aa2[i]  # 16ms
````

### データ参照：df.iloc, df.columns.get_loc
- locとilocは文字と数字どちらかしか取れないので、ちょっと気を付ける
````python
df.loc[df.index[num], col_name]
df.iloc[num, df.columns.get_loc(col_name)]
````

### 数える・区分：df.value_counts(), pd.cut
- value_countsどんなのが多いのか調べるときに便利
- cutはたまに使うかも
````python
tt=pd.DataFrame({'a':np.random.randint(0,20,20)})
print(tt['a'].value_counts())
tt['cut'] = pd.cut(tt['a'], 4)
tt['cut_shitei'] = pd.cut(tt['a'], [-1, 5, 7, 10, 100])
tt['qcut'] = pd.qcut(tt['a'], 4, labels=False)
tt.sort_values('a')
````

### エンコーディング：df.groubpy().fransform()
- groupbyのtranformを使えばカテゴリ変数のエンコーディングがしやすい
- tranformの中はaggと同じでlambdaとかも使える
````python
df=pd.DataFrame()
df['a'] = np.random.randint(0, 4, 100)
df['b'] = np.random.rand(100)
df['c'] = df.groupby('a').transform(lambda x: x.argsort().argsort())
````

### 追加：df.append
- 空のDF作ってappendで追加してくのがやりやすかった
````python
df = pd.DataFrame()
df = df.append(df2)
````

### 抽出：pd.isin
- 同じ列からいろいろ抽出したいときはisinを使用、

````python
t1 = air_con_use_df[(air_con_use_df['para']=='El') | (air_con_use_df['para']=='Cal') |
                         (air_con_use_df['para']=='Inte') | (air_con_use_df['para']=='Flow')].index
t2 = air_con_use_df[air_con_use_df['para'].isin(['El', 'Cal', 'Inte', 'Flow'])].index
(t1==t2).all()　# True
````

### データロード：pd.read_csv
- read_csvをするときは型の推論がかかるので保存したときの型とあっているか注意
- pickleのほうが型が保存されるし、読み込みが早いという話がある（要確認）
- encodingしないと文字化けするときがある、excelでcsvを編集した場合は注意が必要
- 最近はparquet形式が圧縮も速度も良くてお気に入り

````python
pd.read_csv('xxx.csv')
pd.read_csv('xxx.csv', encoding='cp932')  # excelからcsvを作った場合はこれで動くはず

# 以下、https://qiita.com/ctn15/items/501d8f838234f2308490　より
%time df1 = pd.read_pickle('/Users/data/alldata.pickle')
CPU times: user 3.36 s, sys: 2.86 s, total: 6.22 s
Wall time: 6.82 s

%time df2 = pd.read_csv('/Users/data/alldata.csv')
CPU times: user 29.2 s, sys: 8.65 s, total: 37.8 s
Wall time: 42 s
````

### 結合：merge, join, concat
- それぞれちょっとずつ違う、一番使うのはmergeかな
- mergeはpd.meage(df, df2)もできる

````python
df_ab = pd.DataFrame({'a': ['a_1', 'a_5', 'a_2', 'a_3'], 'b': ['b_1', 'b_2', 'b_3', 'b_4']})
df_ac = pd.DataFrame({'a': ['a_1', 'a_2', 'a_5', 'a_4'], 'c': ['c_1', 'c_2', 'c_5', 'c_4']})

df_ab.merge(df_ac, on='a', how='right')　#rightの順で並ぶ
df_ab.merge(df_ac, left_index=True, right_index=True, how='inner') # 正しい結合ではないけど参考として
df_ab.join(df_ac[['c']]) # 正しい結合ではないけど参考として
# そのうち追加予定
````

### カテゴリタイプ：Categorical
- objectよりもcategoryのほうが==が早いので多用する場合はカテゴリ化したほうが快適
````python
df = pd.DataFrame()
df['a'] = [f'x_{np.random.randint(10000)}' for i in range(1000000)]
df['b'] = np.random.rand(1000000)
df2 = df.copy()
df2['a'] = pd.Categorical(df2['a'], categories=df2['a'].unique())
df[df['a']=='x_1000']['b'].median()　#Wall time: 60.8 ms
df2[df2['a']=='x_1000']['b'].median() #Wall time: 4 ms
````