首页 文章

如何在pandas python中得到最近除以100的数字

提问于
浏览
3

我想基于输入列在pandas数据框中添加一个新列 . 必须像这样填充新添加的列 .

  • 第一行必须填入最近除以100的数字 .
    从下一行开始
  • 将重复输出,直到其与输入值的差异大于或等于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

在熊猫中最优雅的方式是什么?

3 回答

  • 4

    我提出的解决方案:

    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给出的输出 .

  • 0

    据我所知,没有't an intuitive approach here that doesn't涉及显式迭代,这对于 numpypandas 来说并不理想 . 但是,这个问题的时间复杂度是O(n),这使得它成为 numba 库的一个很好的目标 . 这使我们能够提出一个非常有效的解决方案 .

    关于我的解决方案的一个注意事项,我使用 (a + threshold // 2) // threshold * threshold ,与使用 np.round(a, decimals=-2) 相比看起来冗长 . 这是由于使用 numbanopython=True ,flag的性质,它与 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)
    
  • 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()
    

    OUTPUT:

    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 .

相关问题