如何在pandas python中获取最接近100的倍数的数字

3
我希望在pandas dataframe中新增一列,根据输入列进行计算。新增的列应按以下方式填充。
  1. first row must be populated with nearest divide by 100 number.
  2. from next row onwards output will be repeated until its difference with input value is more than or equal to 100.

    input       output
    11700.15    11700
    11695.20    11700
    11661.00    11700
    11630.40    11700
    11666.10    11700
    11600.30    11700
    11600.00    11600
    11555.40    11600
    11655.20    11600
    11699.00    11600
    11701.55    11700
    11799.44    11700
    11604.65    11700
    11600.33    11700
    11599.65    11600
    

在 Pandas 中,最优雅的方式是什么?


1
我认为pandas不能做到这一点,也许需要使用for循环。 - BENY
1
你的输出有误。最后两行应该是1600。11600.33比11701.55高100以上,该行出现1700的位置。 - user3483203
1
@user3483203 让我们等待 OP 的回复,但我理解问题的重要区别在于先前输出和当前输入之间的差异。因此,在最后两行中,它分别为(11700 - 11600.33) < 100(11700 - 11599.65 > 100) - filippo
1
@filippo 哦,我相信你是对的。我的答案只需要更改一个字符,所以我会添加两个选项。 - user3483203
@user3483203,是的,Filippo是正确的。差异在于先前输出和当前输入之间的差异。谢谢。 - Devesh Agrawal
3个回答

4
据我所知,没有一种直观的方法可以避免显式迭代,这对于numpypandas来说并不理想。然而,该问题的时间复杂度为O(n),使其成为numba库的一个很好的目标。这使我们能够得出一个非常高效的解决方案。
关于我的解决方案,我使用(a + threshold // 2) // threshold * threshold进行四舍五入,与使用np.round(a, decimals=-2)相比,看起来更冗长。这是由于使用numbanopython=True标志的特性,它与np.round函数不兼容。
from numba import jit

@jit(nopython=True)
def cumsum_with_threshold(arr, threshold):
       """
       Rounds values in an array, propogating the last value seen until
       a cumulative sum reaches a threshold
       :param arr: the array to round and sum
       :param threshold: the point at which to stop propogation
       :return: rounded output array
       """

       s = a.shape[0]
       o = np.empty(s)
       d = a[0]
       r = (a + threshold // 2) // threshold * threshold
       c = 0
       o[0] = r[0]

       for i in range(1, s):
           if np.abs(a[i] - d) > threshold:
               o[i] = r[i]
               d = a[i]
           else:
               o[i] = o[i - 1]

       return o

让我们来测试一下:

a = df['input'].values
pd.Series(cumsum_with_threshold(a, 100))

0     11700.0
1     11700.0
2     11700.0
3     11700.0
4     11700.0
5     11700.0
6     11600.0
7     11600.0
8     11600.0
9     11600.0
10    11700.0
11    11700.0
12    11700.0
13    11600.0
14    11600.0
dtype: float64

如果你想将“四舍五入”的值与输入进行比较,而不是与“实际”值进行比较,请在上面的循环函数中进行以下更改,这将给出你提问的输出。
for i in range(1, s):
   if np.abs(a[i] - d) > t:
       o[i] = r[i]
       # OLD d = a[i]
       d = r[i]
   else:
       o[i] = o[i - 1]

为了测试效率,让我们在一个更大的数据集上运行这个程序:

为了测试效率,请在一个更大的数据集上运行以下代码:

l = np.random.choice(df['input'].values, 10_000_000)

%timeit cumsum_with_threshold(l, 100)
1.54 µs ± 7.93 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

1
谢谢user3483203。它按预期工作。我对先前输出和当前输入之间的差异感兴趣。非常感谢您的时间和努力。 - Devesh Agrawal

2

并不是十分优雅,但我想没有其他方法可以避免使用循环(也许我错了!):

vals = df1['input'].values
anchor = vals[0]
ch = np.zeros(len(vals))
ch.fill(np.nan)
for i in range(len(vals)):
    if abs(vals[i] - anchor) >= 100:
        anchor = vals[i]
        ch[i] = 1
    else:
        continue
ch[0] = 1

df['out_check'] = pd.Series(100* np.round((df['input'] * ch)/100)).ffill()

输出:

       input  output  out_check
0   11700.15   11700    11700.0
1   11695.20   11700    11700.0
2   11661.00   11700    11700.0
3   11630.40   11700    11700.0
4   11666.10   11700    11700.0
5   11600.30   11700    11700.0
6   11600.00   11600    11600.0
7   11555.40   11600    11600.0
8   11655.20   11600    11600.0
9   11699.00   11600    11600.0
10  11701.55   11700    11700.0
11  11799.44   11700    11700.0
12  11604.65   11700    11700.0
13  11600.33   11700    11600.0
14  11599.65   11600    11600.0

我相信output中的最后两个值必须是1600。

1
使用 df['out_check'] = (df['input'] * ch).round(-2).ffill() 作为你的最后一行代码。非常简洁。 - user3483203
@VishnuKunchur,谢谢你。最后两个输出也是正确的。我对前一个输出和当前输入之间的差异感兴趣。你的解决方案帮了我一个大忙。 - Devesh Agrawal

0
我想到的解决方案是:
last = df.loc[0, 'input'].round(-2)
for ix in range(len(df)):
    inp = df.loc[ix, 'input']
    last = inp.round(-2) if abs(inp - last) >= 100 else last
    df.loc[ix, 'output'] = last

这将产生与 OP 给出的输出完全相同的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接