dynamicsoar's log

主に研究関係のメモ

pandas で各要素の有効数字を揃える(「小数点以下何桁」ではなく)

Introduction

df.round(n) だと「小数点以下n桁」で四捨五入される丸められるだけで、有効数字を揃えたい場合はちょっと違う、ということがあった。

たとえば df という DataFrame の中身として

  • 12.345
  • 9.8760

があったとき、df.round(2) すると、

  • 12.34
  • 9.88

になる*1

ところが今やりたいのが「有効数字3桁であらわしたい」であって、単に「小数点以下2桁で丸めたい」ではない、とするとどうか。つまり、

  • 12.3
  • 9.88

となってほしい場合。

Methods

まず

import numpy as np
from math import log10, floor
def round_sig(x, sig=2):
    return np.around(x, sig-int(floor(log10(abs(x))))-1)

という関数を作る。これはstackoverflowの回答の indgar 氏のコードをコピペし、x を abs(x) にしたもの。また、python デフォルトの round よりも numpy の around の方が良いようなので、そちらにした。

使うときは、まだこの辺がよくわかってないのだが、Series にしか直接は適用できないっぽい?ので、

df_orig = pd.Series([12.345, 9.8760]) # 適当な元データを用意する
sig_digit = 3 # 有効桁数を指定する

s_orig = df_orig[:] # DataFrame --> Series
s_round = s_orig.apply(round_sig, args=(sig_digit,)) # ここで apply
df_round = s_round.to_frame() # Series --> DataFrame

df_round # 結果の表示

みたいな感じで。あ、Series への変換と逆変換のところも関数に入れればいいのか…

参考にしたページ

*1:ここ訂正。「四捨五入」なら12.35になるはずだが、roundというもののIEEEだかの定義は「....5000000...のようにピッタリ半分のときは切り上げとは限らず、最も近い偶数へ丸める」らしいので、12.34で良い。12.34501なら、12.35になる。が、実際には内部的な2進数の表現によっても変わるので、np.around(12.3450000000000001, 2) とかしちゃうと 12.34 になる。頭がこんがらがる…