要对 pandas.DataFrame、pandas.Series 的数字进行四舍五入,请使用 round() 方法。 round() 方法舍入为偶数而不是四舍五入。
如果要四舍五入,请将标准库十进制模块的 quantize() 应用于每个元素。
本示例代码中的各个版本如下。
import pandas as pd
import numpy as np
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN
import platform
print(platform.python_version())
print(pd.__version__)
print(np.__version__)
# 3.6.5
# 0.23.0
# 1.14.3
下面对内容进行说明。
以浮点float类型的pandas.Series为例。
s_f = pd.Series([123.456, 654.123])
print(s_f)
# 0 123.456
# 1 654.123
# dtype: float64
如果将整数 n 传递给 round() 的参数,它将四舍五入到小数点后 n 位。
如果省略参数,则 n=0 并舍入为整数。数据类型仍然是浮点型。如果要转换为整型 int 类型,请使用 astype() 方法。
print(s_f.round())
# 0 123.0
# 1 654.0
# dtype: float64
print(s_f.round().astype(int))
# 0 123
# 1 654
# dtype: int64
正整数指定小数点后的位数,负整数指定整数位数。 -1 舍入到第 10 位,-2 舍入到第 100 位。
print(s_f.round(2))
# 0 123.46
# 1 654.12
# dtype: float64
print(s_f.round(-2))
# 0 100.0
# 1 700.0
# dtype: float64
接下来以整数int类型的pandas.Series为例。
s_i = pd.Series([123, 654])
print(s_i)
# 0 123
# 1 654
# dtype: int64
如果 round() 的参数为 0(即使省略)或正整数,则不会发生任何变化。负整数四舍五入到相应的数字。
print(s_i.round())
# 0 123
# 1 654
# dtype: int64
print(s_i.round(2))
# 0 123
# 1 654
# dtype: int64
print(s_i.round(-2))
# 0 100
# 1 700
# dtype: int64
round() 方法返回一个数字四舍五入的新对象,保持原始对象不变。与 pandas.DataFrame 的 round() 方法相同。
s_i_round = s_i.round(-2)
print(s_i_round)
# 0 100
# 1 700
# dtype: int64
print(s_i)
# 0 123
# 1 654
# dtype: int64
以下面的 pandas.DataFrame 为例,其中列有整数 int 类型、浮点 float 类型和字符串 str 类型(列数据类型为对象类型)。
df = pd.DataFrame({'f': [123.456, 654.321], 'i': [123, 654], 's': ['abc', 'xyz']})
print(df)
# f i s
# 0 123.456 123 abc
# 1 654.321 654 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 654.0 654 xyz
print(df.round(2))
# f i s
# 0 123.46 123 abc
# 1 654.32 654 xyz
print(df.round(-2))
# f i s
# 0 100.0 100 abc
# 1 700.0 700 xyz
通过指定一个字典,其中键是列名,值是位数,可以为每一列单独指定位数。未指定的列保持不变。
print(df.round({'f': 2, 'i': -1}))
# f i s
# 0 123.46 120 abc
# 1 654.32 650 xyz
print(df.round({'i': -2}))
# f i s
# 0 123.456 100 abc
# 1 654.321 700 xyz
pandas的round()方法的四舍五入不是四舍五入,而是四舍五入到偶数。 0.5 舍入为 0,2.5 舍入为 2。
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,则向下舍入;如果分数大于 0.5,则向上舍入;如果分数恰好为 0.5,则向下舍入或向上舍入,以偶数为准。”25四舍五入到 20,但 25.1 四舍五入到 30。
s = pd.Series([5, 15, 25, 5.1, 15.1, 25.1])
print(s)
# 0 5.0
# 1 15.0
# 2 25.0
# 3 5.1
# 4 15.1
# 5 25.1
# dtype: float64
print(s.round(-1))
# 0 0.0
# 1 20.0
# 2 20.0
# 3 10.0
# 4 20.0
# 5 30.0
# dtype: float64
Python 的内置函数 round() 和 NumPy 的 around() 函数(= round() 函数)根据值的不同,结果不同,但 pandas 的 round() 方法与 NumPy 的 around() 函数结果相同。
print('Python round')
print('0.005 => ', round(0.005, 2))
print('0.015 => ', round(0.015, 2))
print('0.025 => ', round(0.025, 2))
print('0.035 => ', round(0.035, 2))
print('0.045 => ', round(0.045, 2))
print('2.675 => ', round(2.675, 2))
# Python round
# 0.005 => 0.01
# 0.015 => 0.01
# 0.025 => 0.03
# 0.035 => 0.04
# 0.045 => 0.04
# 2.675 => 2.67
print('NumPy np.around (np.round)')
print('0.005 => ', np.around(0.005, 2))
print('0.015 => ', np.around(0.015, 2))
print('0.025 => ', np.around(0.025, 2))
print('0.035 => ', np.around(0.035, 2))
print('0.045 => ', np.around(0.045, 2))
print('2.675 => ', np.around(2.675, 2))
# NumPy np.around (np.round)
# 0.005 => 0.0
# 0.015 => 0.02
# 0.025 => 0.02
# 0.035 => 0.04
# 0.045 => 0.04
# 2.675 => 2.68
s = pd.Series([0.005, 0.015, 0.025, 0.035, 0.045, 2.675])
print(s)
# 0 0.005
# 1 0.015
# 2 0.025
# 3 0.035
# 4 0.045
# 5 2.675
# dtype: float64
print(s.round(2))
# 0 0.00
# 1 0.02
# 2 0.02
# 3 0.04
# 4 0.04
# 5 2.68
# dtype: float64
Python 内置函数 round() 的结果看起来与舍入为偶数的定义不同的原因是,如官方文档所述,十进制数无法用浮点数精确表示。
如果您使用接下来解释的十进制模块进行检查,您可以看到实际上被视为什么样的数字,例如 2.675。
print(Decimal(2.675))
# 2.67499999999999982236431605997495353221893310546875
由于 2.675 实际上被视为值 2.674999…,因此内置函数 round() 根据舍入到偶数的定义舍入到 2.67。 我不确定 NumPy 的 around() 函数和 pandas 的 round() 方法如何解决这个问题(还没有阅读代码)。 为了准确性,使用如下所示的十进制模块更安全。
标准库十进制模块允许您使用精确的十进制浮点数。
当使用 Decimal() 创建 Decimal 类型对象时,通过传递字符串 str 类型而不是 float 类型将其视为所需值。
print(Decimal(2.675))
# 2.67499999999999982236431605997495353221893310546875
print(Decimal('2.675'))
# 2.675
使用 Decimal() 生成的 Decimal 类型对象的 quantize() 方法对任意位数和舍入模式进行舍入。
在 quantize() 方法中,使用“0.1”或“0.01”等字符串指定与第一个参数中所需位数相同的位数,并在参数舍入中指定舍入模式。 ROUND_HALF_UP 四舍五入,ROUND_HALF_EVEN 四舍五入为偶数。
print(Decimal('2.675').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.68
print(Decimal('2.675').quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.68
print(Decimal('0.5').quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 1
print(Decimal('0.5').quantize(Decimal('0'), rounding=ROUND_HALF_EVEN))
# 0
由于quantize()返回的是Decimal类型对象,因此使用int()和float()可以转换为整数int和浮点float。
这些过程被定义为匿名函数(lambda 表达式),并使用 map() 和 applymap() 方法应用于每个元素。使用 map() 应用于 pandas.Series 的每个元素,并使用 applymap() 应用于 pandas.DataFrame 的每个元素,如示例中所示。有关lambda表达式等详细信息,请参阅以下文章。
s = pd.Series([0.5, 1.5, 2.5, 3.5, 4.5])
print(s.map(lambda x: float(Decimal(str(x)).
quantize(Decimal('0'), rounding=ROUND_HALF_UP))))
# 0 1.0
# 1 2.0
# 2 3.0
# 3 4.0
# 4 5.0
# dtype: float64
s = pd.Series([0.005, 0.015, 0.025, 0.035, 0.045, 2.675])
print(s.map(lambda x: float(Decimal(str(x))
.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))))
# 0 0.01
# 1 0.02
# 2 0.03
# 3 0.04
# 4 0.05
# 5 2.68
# dtype: float64
s = pd.Series([5, 15, 25, 5.1, 15.1, 25.1])
print(s.map(lambda x: int(Decimal(str(x))
.quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))))
# 0 10
# 1 20
# 2 30
# 3 10
# 4 20
# 5 30
# dtype: int64
s = pd.Series([0.005, 0.015, 0.025, 0.035, 0.045, 2.675])
print(s.map(lambda x: float(Decimal(str(x))
.quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))))
# 0 0.00
# 1 0.02
# 2 0.02
# 3 0.04
# 4 0.04
# 5 2.68
# dtype: float64