numpyとpandasの役立つ処理まとめ
実務で使う中で良いと思ったものをメモとして載せておきます。
numpy
数える:np.unique
- np.unique, return_countsが結構便利。
- pandasのvalue_counts、collectionsのCounterでもいいけど、numpyをimportしてない時はまずないので一番使いやすい。
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
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番目なのかを表示できる
- 同値の場合はインデックスが小さい順
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と同値となる
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で書く
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で時間ごとに集計可能
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()としたりもする。メモリに余裕あれば。
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は良くない。
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は文字と数字どちらかしか取れないので、ちょっと気を付ける
df.loc[df.index[num], col_name]
df.iloc[num, df.columns.get_loc(col_name)]
数える・区分:df.value_counts(), pd.cut
- value_countsどんなのが多いのか調べるときに便利
- cutはたまに使うかも
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とかも使える
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で追加してくのがやりやすかった
df = pd.DataFrame()
df = df.append(df2)
抽出:pd.isin
- 同じ列からいろいろ抽出したいときはisinを使用、
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形式が圧縮も速度も良くてお気に入り
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)もできる
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のほうが==が早いので多用する場合はカテゴリ化したほうが快適
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