簡體   English   中英

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

[英]How to find outliers in a series, vectorized?

我有一只大熊貓。正數系列。 我需要找到“異常值”的索引,其值從前一個“范數”中偏離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和numpy獲取last_valid其中

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])的例子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(增強性能)部分

我試圖解決這個問題的方法就是將其表示為last_valid的循環表達式,然后按照Andy Hayden的說法進行操作。 last_valid的表達式為:

lv[i] = lv[i-1] if diff >= 3 else v[i]

哪里

diff = abs(v[i] - lv[i-1]))

其中i是迭代次數。 要在DataFrame中實現這一點,我使用的是來自ctype的指針。 通過在列lv(last_valid)的指針移位列上取消引用,可以在下一個迭代步驟中訪問一個迭代步驟計算的值。

from ctypes import *
import pandas as pd

df = pd.DataFrame({'s_num': [7,8,9,10,14,10,10,14,100,14,10]})
df['s'] = df['s_num'].apply(c_long)
df['lv'] = [c_long(0) for x in range(len(df))]
df.ix[0, 'lv'] = df.ix[0, 's']
df['p_lv'] = df['lv'].apply(pointer)
df['p_lv_m1'] = df['p_lv'].shift()

def ff(x):
    if not pd.isnull(x['p_lv_m1']):
        diff = abs(x['s'].value - x['p_lv_m1'].contents.value)
        x['p_lv'][0] = x['s'] if diff < 3 else x['p_lv_m1'][0]
    return None

df.apply(ff, 1)
df['lv_num'] = df['lv'].apply(lambda x: x.value)
df = df[['s_num', 'lv_num']]

print df.ix[(df['s_num'] - df['lv_num']).abs() >= 3, 's_num']

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM