【问题标题】:Finding anomalous values from sinusoidal data从正弦数据中查找异常值
【发布时间】:2018-12-22 11:57:22
【问题描述】:

如何从以下数据中找到异常值。我正在模拟正弦模式。虽然我可以绘制数据并发现数据中的任何异常或噪音,但我如何在不绘制数据的情况下做到这一点。我正在寻找机器学习方法以外的简单方法。

import random 
import numpy as np 
import matplotlib.pyplot as plt 

N = 10                  # Set signal sample length
t1 = -np.pi             # Simulation begins at t1
t2 =  np.pi;            # Simulation  ends  at t2

in_array = np.linspace(t1, t2, N)
print("in_array : ", in_array)
out_array = np.sin(in_array)

plt.plot(in_array, out_array, color = 'red', marker = "o") ; plt.title("numpy.sin()")

注入随机噪声

noise_input = random.uniform(-.5, .5); print("Noise : ",noise_input)

in_array[random.randint(0,len(in_array)-1)] = noise_input
print(in_array)

plt.plot(in_array, out_array, color = 'red', marker = "o") ; plt.title("numpy.sin()")

有噪声的数据

【问题讨论】:

  • 您是否考虑过使用 FFT 来查找数据中的主频率?
  • @joel 我的第二个回答有帮助吗?
  • @MadPhysicist 我在嘈杂的数据上尝试了 FFT。这里是图片链接:imgur.com/a/i7a6bS8。由于存在的噪声较少,因此频域中只有一个峰值。
  • @joel。该峰值比正弦曲线本身更容易分离,但包含所有相同的信息。

标签: python signal-processing anomaly-detection


【解决方案1】:

我已经想到了以下方法来解决您的问题,因为您只有一些值在时间向量中是异常的,这意味着其余的值有规律的进展,这意味着如果我们收集所有的集群下向量中的数据点并计算最大集群的平均步长(本质上是代表真实交易的值池),然后我们可以使用该平均值在给定阈值下进行三元组检测,超过矢量并检测哪些元素是异常的。

为此,我们需要两个函数:calculate_average_step,它将计算接近值的最大集群的平均值,然后我们需要 detect_anomalous_values,它将根据该平均值生成向量中异常值的索引较早计算。

在我们检测到异常值后,我们可以继续用估计值替换它们,我们可以根据我们的平均步长值和使用向量中的相邻点来确定。

import random 
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt 


def calculate_average_step(array, threshold=5):
    """
    Determine the average step by doing a weighted average based on clustering of averages.
    array: our array
    threshold: the +/- offset for grouping clusters. Aplicable on all elements in the array. 
    """

    # determine all the steps
    steps = []
    for i in range(0, len(array) - 1):
        steps.append(abs(array[i] - array[i+1]))

    # determine the steps clusters
    clusters = []
    skip_indexes = []
    cluster_index = 0

    for i in range(len(steps)):
        if i in skip_indexes:
            continue

        # determine the cluster band (based on threshold)
        cluster_lower = steps[i] - (steps[i]/100) * threshold
        cluster_upper = steps[i] + (steps[i]/100) * threshold

        # create the new cluster
        clusters.append([])
        clusters[cluster_index].append(steps[i])

        # try to match elements from the rest of the array
        for j in range(i + 1, len(steps)):

            if not (cluster_lower <= steps[j] <= cluster_upper):
                continue

            clusters[cluster_index].append(steps[j])
            skip_indexes.append(j)

        cluster_index += 1  # increment the cluster id

    clusters = sorted(clusters, key=lambda x: len(x), reverse=True)
    biggest_cluster = clusters[0] if len(clusters) > 0 else None

    if biggest_cluster is None:
        return None

    return sum(biggest_cluster) / len(biggest_cluster)  # return our most common average


def detect_anomalous_values(array, regular_step, threshold=5):
    """
    Will scan every triad (3 points) in the array to detect anomalies.
    array: the array to iterate over.
    regular_step: the step around which we form the upper/lower band for filtering
    treshold: +/- variation between the steps of the first and median element and median and third element.
    """
    assert(len(array) >= 3)  # must have at least 3 elements

    anomalous_indexes = []

    step_lower = regular_step - (regular_step / 100) * threshold
    step_upper = regular_step + (regular_step / 100) * threshold

    # detection will be forward from i (hence 3 elements must be available for the d)
    for i in range(0, len(array) - 2):
        a = array[i]
        b = array[i+1]
        c = array[i+2]

        first_step = abs(a-b)
        second_step = abs(b-c)

        first_belonging = step_lower <= first_step <= step_upper
        second_belonging = step_lower <= second_step <= step_upper

        # detect that both steps are alright
        if first_belonging and second_belonging:
            continue  # all is good here, nothing to do

        # detect if the first point in the triad is bad
        if not first_belonging and second_belonging:
            anomalous_indexes.append(i)

        # detect the last point in the triad is bad
        if first_belonging and not second_belonging:
            anomalous_indexes.append(i+2)

        # detect the mid point in triad is bad (or everything is bad)
        if not first_belonging and not second_belonging:
            anomalous_indexes.append(i+1)
            # we won't add here the others because they will be detected by
            # the rest of the triad scans

    return sorted(set(anomalous_indexes))  # return unique indexes

if __name__ == "__main__":

    N = 10                  # Set signal sample length
    t1 = -np.pi             # Simulation begins at t1
    t2 =  np.pi;            # Simulation  ends  at t2

    in_array = np.linspace(t1, t2, N)

    # add some noise
    noise_input = random.uniform(-.5, .5);
    in_array[random.randint(0, len(in_array)-1)] = noise_input
    noisy_out_array = np.sin(in_array)

    # display noisy sin
    plt.figure()
    plt.plot(in_array, noisy_out_array, color = 'red', marker = "o");
    plt.title("noisy numpy.sin()")

    # detect anomalous values
    average_step = calculate_average_step(in_array)
    anomalous_indexes = detect_anomalous_values(in_array, average_step)

    # replace anomalous points with an estimated value based on our calculated average
    for anomalous in anomalous_indexes:

        # try forward extrapolation
        try:
            in_array[anomalous] = in_array[anomalous-1] + average_step
        # else try backwward extrapolation
        except IndexError:
            in_array[anomalous] = in_array[anomalous+1] - average_step

    # generate sine wave
    out_array = np.sin(in_array)

    plt.figure()
    plt.plot(in_array, out_array, color = 'green', marker = "o");
    plt.title("cleaned numpy.sin()")

    plt.show()

嘈杂的正弦:

清正弦:

【讨论】:

  • 看起来我们在这里有解决方案。谢谢。您会推荐阅读您在此处使用的过滤噪声的任何博客/论文。三元组检测也在这里工作,二元组或四元组怎么样?
  • @joel 没有博客或文章,我只是自己想到了这个算法。我不确定是否可以使用二分体和四分体似乎有点矫枉过正。我选择三元组是因为您可以轻松地从 3 个值中识别出哪一个值大量偏离。如果您有diads,您知道它们之间的差异是错误的,但您不知道哪个值会造成步长的巨大差异。
  • @andreihondari 您是如何得出这两个函数的阈值参数值的??
  • @joel 这只是一个默认值,您可以将其更改为适合您的任何值。这实际上取决于输入数组、值的聚集程度以及您的阈值舒适度。
【解决方案2】:

您的问题依赖于时间向量(它是一维的)。您需要对该向量应用某种过滤器。

首先想到的是来自scipymedfilt(中值过滤器),它看起来像这样:

from scipy.signal import medfilt
l1 = [0, 10, 20, 30, 2, 50, 70, 15, 90, 100]
l2 = medfilt(l1)
print(l2)

这个输出将是:

[ 0. 10. 20. 20. 30. 50. 50. 70. 90. 90.]

这个过滤器的问题在于,如果我们将一些噪声值应用到向量的边缘,例如[200, 0, 10, 20, 30, 2, 50, 70, 15, 90, 100, -50],那么输出将类似于[ 0. 10. 10. 20. 20. 30. 50. 50. 70. 90. 90. 0.],显然这对于​​正弦图是不合适的,因为它会为正弦值数组生成相同的工件。

解决此问题的更好方法是将时间向量视为y 输出,并将其索引值视为x 输入,并对“时间线性函数”,不是引号,它只是意味着我们通过应用一个假的X 向量来伪造二维模型。代码暗示使用scipylinregress(线性回归)函数:

from scipy.stats import linregress
l1 = [5, 0, 10, 20, 30, -20, 50, 70, 15, 90, 100]
l1_x = range(0, len(l1))

slope, intercept, r_val, p_val, std_err = linregress(l1_x, l1)
l1 = intercept + slope * l1_x

print(l1)

其输出将是:

[-10.45454545  -1.63636364   7.18181818  16.          24.81818182
  33.63636364  42.45454545  51.27272727  60.09090909  68.90909091
  77.72727273]

现在让我们将其应用于您的时间向量。

import random 
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt 
from scipy.stats import linregress

N = 20
# N = 10                  # Set signal sample length
t1 = -np.pi             # Simulation begins at t1
t2 =  np.pi;            # Simulation  ends  at t2

in_array = np.linspace(t1, t2, N)

# add some noise
noise_input = random.uniform(-.5, .5);
in_array[random.randint(0, len(in_array)-1)] = noise_input

# apply filter on time array
in_array_x = range(0, len(in_array))

slope, intercept, r_val, p_val, std_err = linregress(in_array_x, in_array)
in_array = intercept + slope * in_array_x

# generate sine wave
out_array = np.sin(in_array)
print("OUT ARRAY")
print(out_array)

plt.plot(in_array, out_array, color = 'red', marker = "o") ; plt.title("numpy.sin()")

plt.show()

输出将是:

得到的信号将是原始信号的近似值,就像任何形式的外插/内插/回归滤波一样。

【讨论】:

  • 谢谢@anandreihondrari。虽然您的解决方案展示了如何使用回归过滤器去除噪音。我正在寻找在信号数据中找到噪声值的方法。
猜你喜欢
  • 2015-03-13
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 2021-01-13
  • 2016-08-17
  • 2010-11-25
  • 2020-02-16
相关资源
最近更新 更多