将系列四舍五入到 N 个有效数字
Round a series to N number of significant figures
我有一个浮点数数据框,我需要创建一个函数,它将接受一列并将所有值四舍五入为 N 个有效数字
所以该列可能类似于:
123.949
23.87
1.9865
0.0129500
如果我想四舍五入到 3 位有效数字,我会将列和 3 传递给函数以获得此
124.0
23.9
1.99
0.013
如何在不循环遍历该列的情况下高效地执行此操作?
我有一个方程式可以计算数字的有效数字
round(x, N-int(floor(log10(abs(x))))
但它不适用于系列或数据框
您可以使用,pandas.Series.apply
,它在轴(列或行)上明智地实现功能元素:
df.col.apply(lambda x: round(x, N - int(floor(log10(abs(x))))))
请注意,您不能真正在此处使用 pandas.DataFrame.apply
,因为圆形函数应该是元素明智的,而不是在整个轴上。
区别在于您的函数输入是 float
而不是 array
.
另一个选项是 applymap
,它在整个 pandas.DataFrame
.
上按元素实现函数
df.applymap(lambda x: round(x, N - int(floor(log10(abs(x))))))
这是对数据框中的系列应用自定义函数的另一种方法。但是,内置的 round() 似乎在最后一位数字为 5 时将小数部分向下舍入,因此在您的示例中,您实际上得到的是 0.0129 而不是 0.013。我试图解决这个问题。还添加了将有效数字的数量设置为参数的功能,以获得您要应用的舍入器。
import pandas as pd
from math import floor, log10
df = pd.DataFrame({'floats':[123.949, 23.87, 1.9865, 0.0129500]})
def smarter_round(sig):
def rounder(x):
offset = sig - floor(log10(abs(x)))
initial_result = round(x, offset)
if str(initial_result)[-1] == '5' and initial_result == x:
return round(x, offset - 2)
else:
return round(x, offset - 1)
return rounder
print(df['floats'].apply(smarter_round(3)))
Out:
0 124.000
1 23.900
2 1.990
3 0.013
Name: floats, dtype: float64
对于大型数据帧,.apply 可能会很慢。我见过的最好的解决方案来自 Scott Gigante 直接为 numpy 解决相同的问题。
这是他的答案的一个轻微修改版本,只是添加了一些 pandas 包装。该解决方案快速且稳健。
from typing import Union
import pandas as pd
import numpy as np
def significant_digits(df: Union[pd.DataFrame, pd.Series],
significance: int,
inplace: bool = False) -> Union[pd.DataFrame, pd.Series, None]:
# Create a positive data vector with a place holder for NaN / inf data
data = df.values
data_positive = np.where(np.isfinite(data) & (data != 0), np.abs(data), 10**(significance-1))
# Align data by magnitude, round, and scale back to original
magnitude = 10 ** (significance - 1 - np.floor(np.log10(data_positive)))
data_rounded = np.round(data * magnitude) / magnitude
# Place back into Series or DataFrame
if inplace:
df.loc[:] = data_rounded
else:
if isinstance(df, pd.DataFrame):
return pd.DataFrame(data=data_rounded, index=df.index, columns=df.columns)
else:
return pd.Series(data=data_rounded, index=df.index)
我有一个浮点数数据框,我需要创建一个函数,它将接受一列并将所有值四舍五入为 N 个有效数字
所以该列可能类似于:
123.949
23.87
1.9865
0.0129500
如果我想四舍五入到 3 位有效数字,我会将列和 3 传递给函数以获得此
124.0
23.9
1.99
0.013
如何在不循环遍历该列的情况下高效地执行此操作?
我有一个方程式可以计算数字的有效数字
round(x, N-int(floor(log10(abs(x))))
但它不适用于系列或数据框
您可以使用,pandas.Series.apply
,它在轴(列或行)上明智地实现功能元素:
df.col.apply(lambda x: round(x, N - int(floor(log10(abs(x))))))
请注意,您不能真正在此处使用 pandas.DataFrame.apply
,因为圆形函数应该是元素明智的,而不是在整个轴上。
区别在于您的函数输入是 float
而不是 array
.
另一个选项是 applymap
,它在整个 pandas.DataFrame
.
df.applymap(lambda x: round(x, N - int(floor(log10(abs(x))))))
这是对数据框中的系列应用自定义函数的另一种方法。但是,内置的 round() 似乎在最后一位数字为 5 时将小数部分向下舍入,因此在您的示例中,您实际上得到的是 0.0129 而不是 0.013。我试图解决这个问题。还添加了将有效数字的数量设置为参数的功能,以获得您要应用的舍入器。
import pandas as pd
from math import floor, log10
df = pd.DataFrame({'floats':[123.949, 23.87, 1.9865, 0.0129500]})
def smarter_round(sig):
def rounder(x):
offset = sig - floor(log10(abs(x)))
initial_result = round(x, offset)
if str(initial_result)[-1] == '5' and initial_result == x:
return round(x, offset - 2)
else:
return round(x, offset - 1)
return rounder
print(df['floats'].apply(smarter_round(3)))
Out:
0 124.000
1 23.900
2 1.990
3 0.013
Name: floats, dtype: float64
对于大型数据帧,.apply 可能会很慢。我见过的最好的解决方案来自 Scott Gigante 直接为 numpy 解决相同的问题。
这是他的答案的一个轻微修改版本,只是添加了一些 pandas 包装。该解决方案快速且稳健。
from typing import Union
import pandas as pd
import numpy as np
def significant_digits(df: Union[pd.DataFrame, pd.Series],
significance: int,
inplace: bool = False) -> Union[pd.DataFrame, pd.Series, None]:
# Create a positive data vector with a place holder for NaN / inf data
data = df.values
data_positive = np.where(np.isfinite(data) & (data != 0), np.abs(data), 10**(significance-1))
# Align data by magnitude, round, and scale back to original
magnitude = 10 ** (significance - 1 - np.floor(np.log10(data_positive)))
data_rounded = np.round(data * magnitude) / magnitude
# Place back into Series or DataFrame
if inplace:
df.loc[:] = data_rounded
else:
if isinstance(df, pd.DataFrame):
return pd.DataFrame(data=data_rounded, index=df.index, columns=df.columns)
else:
return pd.Series(data=data_rounded, index=df.index)