如何在一系列中找到异常值,进行矢量化?

 陨落星辰W_955 发布于 2023-02-11 12:30

我有一只大熊猫.正数系列.我需要找到"异常值"的索引,其值离开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

请注意,s 10之后的值14不会返回,因为它们是"恢复正常"值.

编辑:

添加abs()到代码中.数字是积极的.

这里的目的是加速代码.

一个不完全模仿代码的答案可能是可以接受的.

更改示例以包含另一个边缘大小写,其中值缓慢变化3.

Andy Hayden.. 7

这是我原来的"矢量化"解决方案:

你可以获得last_valid使用shift和numpy的位置:

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实现(参见pandas docs的cython部分),它比原生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

有关更多信息,请参阅pandas docs 的cython(增强性能)部分.

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有