将一系列舍入为 N 个有效数字

问题描述 投票:0回答:4

我有一个浮点数数据框,我需要创建一个函数,该函数将采用一列并将所有值四舍五入为 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))))

但它不适用于系列或数据框

python pandas math rounding
4个回答
4
投票

您可以使用

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))))))

3
投票

这是将自定义函数应用于数据框中的系列的另一种方法。但是,当最后一位数字为 5 时,内置的 round() 似乎会将小数部分向下舍入,因此在您的示例中,您实际上会得到 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

2
投票

对于大型数据帧,.apply 可能会很慢。我见过的最好的解决方案来自 Scott Gigante 直接为 numpy 解决相同的问题。

这是他的答案的轻微修改版本,只是添加了一些熊猫包装。该解决方案快速且稳健。

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)

0
投票

这是一个更简单的矢量化函数,应该接近您正在寻找的函数。它应该具有与 @RSamson78 的答案类似的性能,但更容易阅读。

def round(x, n_figs):
    power = 10 ** (np.floor(np.log10(np.abs(x).clip(1e-200))))
    rounded = np.round(x / power, n_figs-1) * power
    return rounded

这就是它的工作原理:

  1. 找出第一个数字的位置(任何小于1e-200的数字都将被视为从位置-200开始,以正确处理0)
  2. 将数字移动许多小数位,因此它的小数点左边恰好有 1 位数字
  3. 四舍五入到小数点后 n-1 位
  4. 将数字移回原来的位置
© www.soinside.com 2019 - 2024. All rights reserved.