df.to_csv()
などで出力する時、ちょっとした変換方法
1514777696 → 2018/01/01 12:34:56 にする。int ⇒ numpy.datetime64 ⇒ str と変換する
1 2 3 4 5 6 7 |
# 目的: df['unix'] のUnixTimeを変換して df['datetime'] に格納する # df['unix'] をdatetime64型に変換、時差9時間を足し日本時刻とする df[ 'datetime' ] = pd.to_datetime(df[ 'unix' ], unit = 's' ) + pd.Timedelta( 9 , unit = 'h' ) # フォーマット df[ 'datetime' ] = df[ 'datetime' ].dt.strftime( '%Y/%m/%d %H:%M:%S' ) |
np.nan
はint型で表現できないため、それを1つでも含むカラムは自動的にfloat型になる。float型は出力時の記述に「1.0」のように小数点が付いてしまう。
code code 1 1.0 → 1 2 1.0 整数にしたい 1 3 nan 4 3.0 3 ... ...
メモリや処理速度は多少効率悪くてもいい場合、文字列に変換するのがよい。
1 2 3 4 5 |
df[ 'col' ] = df[ 'col' ].dropna(). apply ( lambda x: str ( int (x))) # または df[ 'col' ] = df[ 'col' ].dropna(). apply ( int ). apply ( str ) |
上記の変換ではnp.nanはnp.nanのまま残っているが、通常は空文字列になる。
もし他の表現にしたい場合、df.to_csv(na_rep='NULL')
などとしてやれば、指定した文字列で表現される。
妥協として、'-1
'など、nanであることを示す値を定義して、その値で代替する。
1 |
df[ 'col' ] = df[ 'col' ].fillna( - 1 ).astype( int ) |
未検証、pandas ver.0.24から導入された新しめの手法。
numpyはnanを扱えないので、pandasのレイヤーでnanを扱えるint型を独自定義した感じ。
ただ、floatからのキャストでエラーが出て、イマイチよく使い方がわかっていない。
1 2 3 4 5 6 7 8 |
# 元のカラムの並び順 print (df.columns.tolist()) # ['a', 'b', 'c', 'd'] cols = [ 'a' , 'c' , 'b' ] # この順で新しいdfを作る df = df[cols] |
これは別に出力時に限った話でも無いけど。
1 2 3 4 5 |
# 'col'カラムでソート df.sort_values( 'col' , inplace = True ) # 'primary' => 'secondary' の優先順位でソート df.sort_values([ 'primary' , 'secondary' ], inplace = True ) |
インデックス(行ID)を、今の並び順で0からの連番にする。
1 2 3 4 5 6 7 8 9 10 11 |
# col1 col2 # 2 1 2 # 18 30 5 # 3 40 8 df.reset_index(drop = True , inplace = True ) # col1 col2 # 0 1 2 # 1 30 5 # 2 40 8 |
df.round()を使う。{'カラム名': 小数点以下桁数} の辞書を渡すと、カラム毎にその桁数に揃える。
ただし桁数に0を指定してもint型にはならず、「1.0」のようなfloat型のままとなる。int型への変換はastype()を用いる。
1 2 3 4 5 6 7 8 9 10 11 |
# col1 col2 # 0 3.141592653 3.141592653 # 1 2.718281828 2.718281828 # 2 1.414213562 1.414213562 df = df. round ({ 'col1' : 2 , 'col2' : 5 }) # col1 col2 # 0 3.14 3.14159 # 1 2.72 2.71828 # 2 1.41 1.41421 |
また、環境に依るのかも知れないが、メモリ節約で「np.float32」型などにしていると、roundが効かないことがあった。 効かない事態に遭遇したら、「np.float64」型にキャストすると上手くいったので、メモとして残しておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# col1 col2 # 0 3.141592653 3.141592653 # 1 2.718281828 2.718281828 # 2 1.414213562 1.414213562 # (float32) (float64) df. round ({ 'col1' : 2 , 'col2' : 2 }) # col1 col2 # 0 3.141592653 3.14 # 1 2.718281828 2.72 # 2 1.414213562 1.41 df[ 'col1' ] = df[ 'col1' ].astype(np.float64) df. round ({ 'col1' : 2 , 'col2' : 2 }) # col1 col2 # 0 3.14 3.14 # 1 2.72 2.72 # 2 1.41 1.41 |
注意点として、NumPyのroundは偶数丸めであること。(参考: 端数処理)
偶数丸めは、丸めた値が実際の値から上振れするか下振れするかの期待値を、なるべく均等にする目的で使われる。 よって、丸め結果の総和や平均を取る場合などは望ましい。 しかし、丸め結果の差分が重要なデータなどでは、四捨五入の方が誤差が少なくなる場合もある(*)ため、使い分ける必要がある。
だが、PythonにもNumPyにも本来の四捨五入(xx.5は常に切り上げ)に相当する関数が無いんだよなあ。 一応、標準モジュール Decimal.quantize() でDecimal型に変換することで、切り上げ方を選択できるが、変換が必要なことがネック。 1 で割ってあまりが 0.5 に等しいものは一律 0.01 を加算するみたいな方がいいかもしれん。