pandasで四捨五入・偶数丸め・切り捨て・切り上げ
pandas.DataFrame
, pandas.Series
の数値を丸めるにはround()
メソッドを使う。一般的な四捨五入ではなく偶数への丸めとなるので注意。
- pandas.DataFrame.round — pandas 2.1.3 documentation
- pandas.Series.round — pandas 2.1.3 documentation
一般的な四捨五入や小数点以下の切り捨て・切り上げを行うには、NumPyの関数を使うか、Python標準ライブラリのdecimalモジュールを使う。
Pythonの組み込み関数round()
やNumPyのnp.round()
については以下の記事を参照。
pandasで数値を文字列に変換する方法、表示設定を変更する方法については以下の記事を参照。
本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。
import pandas as pd
print(pd.__version__)
# 2.1.2
pandas.Seriesのround()メソッドの使い方
浮動小数点数float
のSeries
を例とする。
s_f = pd.Series([123.456, 987.654])
print(s_f)
# 0 123.456
# 1 987.654
# dtype: float64
round()
の第一引数decimals
に何桁に丸めるかを整数値で指定する。デフォルトはdecimals=0
で整数に丸められるがデータ型はfloat
のまま。整数int
に変換するにはastype()
メソッドを使う。
print(s_f.round())
# 0 123.0
# 1 988.0
# dtype: float64
print(s_f.round().astype(int))
# 0 123
# 1 988
# dtype: int64
第一引数decimals
が正だと小数点以下の桁数指定、負だと整数部分の桁数指定になる。-1
は十の位、-2
は百の位に丸められる。
print(s_f.round(2))
# 0 123.46
# 1 987.65
# dtype: float64
print(s_f.round(-2))
# 0 100.0
# 1 1000.0
# dtype: float64
整数int
のSeries
の場合、round()
の第一引数decimals
が0
(省略した場合も含む)や正の場合は変化なし。負の場合は対応する位に丸められる。
s_i = pd.Series([123, 987])
print(s_i)
# 0 123
# 1 987
# dtype: int64
print(s_i.round())
# 0 123
# 1 987
# dtype: int64
print(s_i.round(2))
# 0 123
# 1 987
# dtype: int64
print(s_i.round(-2))
# 0 100
# 1 1000
# dtype: int64
round()
メソッドは数値が丸められた新しいオブジェクトを返し、元のオブジェクトは変更されない。DataFrame
のround()
メソッドでも同様。
s_i_round = s_i.round(-2)
print(s_i_round)
# 0 100
# 1 1000
# dtype: int64
print(s_i)
# 0 123
# 1 987
# dtype: int64
なお、round()
メソッドによる数値の丸めは四捨五入ではなく偶数への丸めとなる。詳しくは後述。
pandas.DataFrameのround()メソッドの使い方
浮動小数点数float
、整数int
、文字列str
(列のデータ型はobject
)の列を含むDataFrame
を例とする。
df = pd.DataFrame({'f': [123.456, 987.654], 'i': [123, 987], 's': ['abc', 'xyz']})
print(df)
# f i s
# 0 123.456 123 abc
# 1 987.654 987 xyz
print(df.dtypes)
# f float64
# i int64
# s object
# dtype: object
全ての列に一律で桁数指定
round()
の引数を省略した場合および整数を指定した場合は、全ての列が対応する桁数に丸められる。文字列は変化なし。
print(df.round())
# f i s
# 0 123.0 123 abc
# 1 988.0 987 xyz
print(df.round(2))
# f i s
# 0 123.46 123 abc
# 1 987.65 987 xyz
print(df.round(-2))
# f i s
# 0 100.0 100 abc
# 1 1000.0 1000 xyz
各列に個別に桁数指定
round()
の引数にキーを列名、値を桁数とする辞書を指定すると、各列に個別に桁数を指定できる。文字列に指定しても変化なし。指定しない列も変化なし。
print(df.round({'f': 2, 'i': -1, 's': 2}))
# f i s
# 0 123.46 120 abc
# 1 987.65 990 xyz
print(df.round({'i': -2}))
# f i s
# 0 123.456 100 abc
# 1 987.654 1000 xyz
pandasのround()メソッドは偶数への丸め
pandasのround()
メソッドによる丸めは一般的な四捨五入ではなく偶数への丸め。
「偶数への丸め」(round to even)は、端数が0.5より小さいなら「切り捨て」、端数が0.5より大きいならば「切り上げ」、端数がちょうど0.5なら「切り捨て」と「切り上げ」のうち結果が偶数となる方へ丸める(つまり偶数+0.5なら「切り捨て」、奇数+0.5ならば「切り上げ」となる)。 端数処理 - 偶数への丸め(round to even) - Wikipedia
0.5
が0.0
に丸められたり、2.5
が2.0
に丸められたりする。
s = pd.Series([0.5, 1.5, 2.5, 3.5, 4.5])
print(s)
# 0 0.5
# 1 1.5
# 2 2.5
# 3 3.5
# 4 4.5
# dtype: float64
print(s.round())
# 0 0.0
# 1 2.0
# 2 2.0
# 3 4.0
# 4 4.0
# dtype: float64
偶数へ丸められるのは端数がちょうど0.5
のときなので、例えば2.5
は2.0
に丸められるが2.51
は3.0
に丸められる。
s = pd.Series([2.49, 2.5, 2.51])
print(s)
# 0 2.49
# 1 2.50
# 2 2.51
# dtype: float64
print(s.round())
# 0 2.0
# 1 2.0
# 2 3.0
# dtype: float64
桁数を指定する場合も同様。
s = pd.Series([249, 250, 251])
print(s)
# 0 249
# 1 250
# 2 251
# dtype: int64
print(s.round(-2))
# 0 200
# 1 200
# 2 300
# dtype: int64
NumPyの関数で四捨五入・小数点以下の切り捨て・切り上げ
バージョン2.1
時点で、pandasには一般的な四捨五入や小数点以下の切り捨て・切り上げを行うメソッドは提供されていない。
NumPyの関数でSeries
やDataFrame
を処理できるので、該当の関数を使えばよい。
import numpy as np
s = pd.Series([-0.5, -0.25, 0.25, 0.5])
print(s)
# 0 -0.50
# 1 -0.25
# 2 0.25
# 3 0.50
# dtype: float64
print(np.floor(s))
# 0 -1.0
# 1 -1.0
# 2 0.0
# 3 0.0
# dtype: float64
print(np.trunc(s))
# 0 -0.0
# 1 -0.0
# 2 0.0
# 3 0.0
# dtype: float64
print(np.ceil(s))
# 0 -0.0
# 1 -0.0
# 2 1.0
# 3 1.0
# dtype: float64
np.floor()
は負の無限大への丸め、np.trunc()
はゼロへの丸め、np.ceil()
は正の無限大への丸め。詳細は以下の記事を参照。
NumPyにも一般的な四捨五入をする関数はないが、例えば以下のように実現できる。
def my_round(x, decimals=0):
return np.floor(x * 10**decimals + 0.5) / 10**decimals
s = pd.Series([0.5, 1.5, 2.5, 3.5, 4.5])
print(s)
# 0 0.5
# 1 1.5
# 2 2.5
# 3 3.5
# 4 4.5
# dtype: float64
print(my_round(s))
# 0 1.0
# 1 2.0
# 2 3.0
# 3 4.0
# 4 5.0
# dtype: float64
print(my_round(s * 10, -1))
# 0 10.0
# 1 20.0
# 2 30.0
# 3 40.0
# 4 50.0
# dtype: float64
詳細は以下の記事を参照。
文字列など数値以外の列を含むDataFrame
に対してNumPyの関数を適用するとエラーになるので注意。select_dtypes()
を使うと数値列のみ抽出できる。
df = pd.DataFrame({'f': [123.456, 987.654], 'i': [123, 987], 's': ['abc', 'xyz']})
print(df)
# f i s
# 0 123.456 123 abc
# 1 987.654 987 xyz
# print(np.floor(df))
# TypeError: must be real number, not str
print(np.floor(df.select_dtypes('number')))
# f i
# 0 123.0 123.0
# 1 987.0 987.0
所望の列を処理して新たな列として追加することもできる。
df['f_floor'] = np.floor(df['f'])
print(df)
# f i s f_floor
# 0 123.456 123 abc 123.0
# 1 987.654 987 xyz 987.0
decimalモジュールで四捨五入・小数点以下の切り捨て・切り上げ
Pythonの標準ライブラリdecimalモジュールを使うと、正確な十進浮動小数点数を扱うことができる。
当然ながらpandasやNumPyのメソッド・関数より低速だが、厳密に小数を処理したい場合はSeries
やDataFrame
の要素としてDecimal
オブジェクトを使うこともできる。
ここでは以下のようにインポートする。
from decimal import Decimal, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING
astype()
で文字列に変換してからmap()
で各要素にDecimal()
を適用する。
s = pd.Series([0.123, 0.456, 0.789])
print(s)
# 0 0.123
# 1 0.456
# 2 0.789
# dtype: float64
s_decimal = s.astype(str).map(Decimal)
print(s_decimal)
# 0 0.123
# 1 0.456
# 2 0.789
# dtype: object
print(type(s_decimal[0]))
# <class 'decimal.Decimal'>
文字列に変換しないと浮動小数点数float
で表現できる値として扱われてしまうので注意。
print(s.map(Decimal))
# 0 0.12299999999999999822364316059974953532218933...
# 1 0.45600000000000001643130076445231679826974868...
# 2 0.78900000000000003463895836830488406121730804...
# dtype: object
Decimal
オブジェクトのquantize()
メソッドで任意の桁数・丸めモードで丸めることが可能。ラムダ式で各要素に適用する。
print(s_decimal.map(lambda x: x.quantize(Decimal('0.1'), ROUND_HALF_UP)))
# 0 0.1
# 1 0.5
# 2 0.8
# dtype: object
print(s_decimal.map(lambda x: x.quantize(Decimal('0.1'), ROUND_FLOOR)))
# 0 0.1
# 1 0.4
# 2 0.7
# dtype: object
print(s_decimal.map(lambda x: x.quantize(Decimal('0.1'), ROUND_CEILING)))
# 0 0.2
# 1 0.5
# 2 0.8
# dtype: object
第一引数に求めたい桁数と同じ桁数のDecimal
を指定する。Decimal()
に文字列で'0.1'
や'0.01'
などを指定すればよい。整数部分を丸めたい場合は'1E1'
のように指数表記を使う。詳細は以下の記事を参照。
第二引数に丸めモードを指定する。上の例のROUND_HALF_UP
は四捨五入、ROUND_FLOOR
は小数点以下切り捨て(負の無限大への丸め)、ROUND_CEILING
は小数点以下切り上げ(正の無限大への丸め)。その他の丸めモードについては以下の公式ドキュメントを参照。偶数丸めやゼロへの丸めなどもある。
Decimal
オブジェクトを要素とするSeries
やDataFrame
をastype()
で浮動小数点数float
に戻すことも可能。ただし、もちろんfloat
で表現可能な値に変換されるので誤差を含む。
print(s_decimal.astype(float))
# 0 0.123
# 1 0.456
# 2 0.789
# dtype: float64
print(s_decimal.astype(float).map(Decimal))
# 0 0.12299999999999999822364316059974953532218933...
# 1 0.45600000000000001643130076445231679826974868...
# 2 0.78900000000000003463895836830488406121730804...
# dtype: object