gpt4 book ai didi

python - 如何找到系列中的异常值,矢量化?

转载 作者:太空狗 更新时间:2023-10-29 18:30:39 26 4
gpt4 key购买 nike

我有一个 pandas.Series 的正数。我需要找到“异常值”的索引,其值与之前的“规范”相差 3 或更多。

如何向量化这个函数:

def baseline(s):
values = []
indexes = []
last_valid = s.iloc[0]
for idx, val in s.iteritems():
if abs(val - last_valid) >= 3:
values.append(val)
indexes.append(idx)
else:
last_valid = val
return pd.Series(values, index=indexes)

例如,如果输入是:

import pandas as pd
s = pd.Series([7,8,9,10,14,10,10,14,100,14,10])
print baseline(s)

期望的输出是:

4     14
7 14
8 100
9 14

请注意,14 之后的 10 值不会返回,因为它们是“恢复正常”的值。

编辑:

  • 在代码中添加了 abs()。这些数字是积极的。
  • 这里的目的是加速代码。
  • 不完全模仿代码的答案可能可以接受。
  • 更改示例以包含另一个边缘情况,其中值缓慢变化 3。

最佳答案

这是我最初的“矢量化”解决方案:

您可以使用 shift 获取 last_valid和 numpy 的 where :

In [1]: s = pd.Series([10, 10, 10, 14, 10, 10, 10, 14, 100, 14, 10])

In [2]: last_valid = pd.Series(np.where((s - s.shift()).abs() < 3, s, np.nan))
last_valid.iloc[0] = s.iloc[0] # initialize with first value of s
last_valid.ffill(inplace=True)

In [3]: last_valid
Out[3]:
0 7
1 8
2 9
3 10
4 10
5 10
6 10
7 10
8 10
9 10
10 10
dtype: float64

这样问题就简单多了。您可以将其与 s 进行比较:

In [4]: s - last_valid  # alternatively use (s - last_valid).abs()
Out[4]:
0 0
1 0
2 0
3 0
4 4
5 0
6 0
7 4
8 90
9 4
10 0
dtype: float64

那些相差超过 +3 的元素:

In [5]: (s - last_valid).abs() >= 3
Out[5]:
0 False
1 False
2 False
3 False
4 True
5 False
6 False
7 True
8 True
9 True
10 False
dtype: bool

In [6]: s[(s - last_valid).abs() >= 3]
Out[6]:
4 14
7 14
8 100
9 14
dtype: int64

根据需要。 ...或者看起来,@alko 的示例表明这不太正确。

更新

正如@alko 所指出的,下面的矢量化方法不太正确,特别是对于示例 s = pd.Series([10, 14, 11, 10, 10, 12, 14, 100, 100 , 14, 10]),我的“矢量化”方法将第二个 100 作为“非异常值”,即使它处于基线。

这让我(和@alko)认为这不能被矢量化。作为替代方案,我包含了一个简单的 cython 实现(参见 cython section of pandas docs),它比原生 python 快得多:

%%cython
cimport numpy as np
import numpy as np
cimport cython
@cython.wraparound(False)
@cython.boundscheck(False)
cpdef _outliers(np.ndarray[double] s):
cdef np.ndarray[Py_ssize_t] indexes
cdef np.ndarray[double] vals
cdef double last, val
cdef Py_ssize_t count
indexes = np.empty(len(s), dtype='int')
vals = np.empty(len(s))
last = s[0]
count = 0
for idx, val in enumerate(s):
if abs(val - last) >= 3:
indexes[count] = idx
vals[count] = val
count += 1
else:
last = val
return vals[:count], indexes[:count]

def outliers(s):
return pd.Series(*_outliers(s.values.astype('float')))

一些时间指示:

In [11]: s = pd.Series([10,10,12,14,100,100,14,10])

In [12]: %timeit baseline(s)
10000 loops, best of 3: 132 µs per loop

In [13]: %timeit outliers(s)
10000 loops, best of 3: 46.8 µs per loop

In [21]: s = pd.Series(np.random.randint(0, 100, 100000))

In [22]: %timeit baseline(s)
10 loops, best of 3: 161 ms per loop

In [23]: %timeit outliers(s)
100 loops, best of 3: 9.43 ms per loop

有关更多信息,请参阅 cython (enhancing performance) section Pandas 文档。

关于python - 如何找到系列中的异常值,矢量化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20539777/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com