如何在熊猫蟒蛇中得到最接近的除以100的数字



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

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

在熊猫身上做这件事最优雅的方式是什么?

据我所知,这里没有一种不涉及显式迭代的直观方法,这对于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)

无论如何都不优雅,但我想没有办法绕过这个循环(可能是错误的!(:

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。

我提出的解决方案:

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

最新更新