出力時の整形 - pandas

df.to_csv()などで出力する時、ちょっとした変換方法

UnixTimeをDateStringに変換

1514777696 → 2018/01/01 12:34:56 にする。int ⇒ numpy.datetime64 ⇒ str と変換する

# 目的: 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')

NaNを含むintカラム

np.nanはint型で表現できないため、それを1つでも含むカラムは自動的にfloat型になる。float型は出力時の記述に「1.0」のように小数点が付いてしまう。

   code                 code
1  1.0       →         1
2  1.0   整数にしたい   1
3  nan                   
4  3.0                  3
...                    ...

文字列に変換する

メモリや処理速度は多少効率悪くてもいい場合、文字列に変換するのがよい。

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') などとしてやれば、指定した文字列で表現される。

nanを示す整数値を定義

妥協として、'-1'など、nanであることを示す値を定義して、その値で代替する。

df['col'] = df['col'].fillna(-1).astype(int)

pandas.Int64Dtype型を使う

未検証、pandas ver.0.24から導入された新しめの手法。

numpyはnanを扱えないので、pandasのレイヤーでnanを扱えるint型を独自定義した感じ。

ただ、floatからのキャストでエラーが出て、イマイチよく使い方がわかっていない。

カラムの選択・並べ替え

# 元のカラムの並び順
print(df.columns.tolist())
# ['a', 'b', 'c', 'd']

cols = ['a', 'c', 'b']
# この順で新しいdfを作る

df = df[cols]

整列

これは別に出力時に限った話でも無いけど。

# 'col'カラムでソート
df.sort_values('col', inplace=True)

# 'primary' => 'secondary' の優先順位でソート
df.sort_values(['primary', 'secondary'], inplace=True)

インデックスを振り直す

インデックス(行ID)を、今の並び順で0からの連番にする。

#     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()を用いる。

#      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」型にキャストすると上手くいったので、メモとして残しておく。

#      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 を加算するみたいな方がいいかもしれん。

  • (*) 検証方法
  • $0.0~1.9$ の0.1刻みの $(a,b)$ の組400通りにつき、小数点1桁目を丸めての $a'-b'$ と、元の $a-b$ との誤差は、
    • 偶数丸めで処理した結果: MAE=0.335, RMSE=0.4123
    • 四捨五入で処理した結果: MAE=0.33, RMSE=0.4062
  • となり、四捨五入の方が僅かに誤差が少なくなる
programming/python/packages/pandas/format_output.txt · 最終更新: 2021/12/09 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0