代码之家  ›  专栏  ›  技术社区  ›  Cedric Zoppolo

如何检测数据帧中数据线性变化的连续范围?

  •  6
  • Cedric Zoppolo  · 技术社区  · 6 年前

    我试图检测数据帧中某些数据内相关变量线性变化的连续跨度。数据中可能有许多跨度满足这一要求。我开始我的aproach使用 ransac 基于 Robust linear model estimation using RANSAC

    客观的

    检测相关变量在数据中线性变化的连续范围。要检测的跨度由20多个连续数据点组成。所需的输出将是放置连续跨距的范围日期。

    玩具示例

    页然而,我知道我需要修改剩下的代码才能达到目标。

    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn import linear_model, datasets
    import numpy as np
    
    ## 1. Generate random data for toy sample
    times = pd.date_range('2016-08-10', periods=100, freq='15min')
    df = pd.DataFrame(np.random.randint(0,100,size=(100, 1)), index=times, columns=["data"])
    
    ## 2. Set line1 within random data
    date_range1_start = "2016-08-10 08:15"
    date_range1_end = "2016-08-10 15:00"
    line1 = df.data[date_range1_start:date_range1_end]
    value_start1 = 10
    values1 = range(value_start1,value_start1+len(line1))
    df.data[date_range1_start:date_range1_end] = values1
    
    ## 3. Set line2 within random data
    date_range2_start = "2016-08-10 17:00"
    date_range2_end = "2016-08-10 22:30"
    value_start2 = 90
    line2 = df.data[date_range2_start:date_range2_end]
    values2 = range(value_start2,value_start2-len(line2),-1)
    df.data[date_range2_start:date_range2_end] = values2
    
    ## 4. Plot data
    df.plot()
    plt.show()
    
    ## 5. Create arrays
    X = np.asarray(df.index)
    y = np.asarray(df.data.tolist())
    
    ## 6. Fit line using all data
    lr = linear_model.LinearRegression()
    lr.fit(X, y)
    

    对于这个玩具示例代码,所需的输出(我还无法编写)将是如下所示的数据帧:

    >>> out
                  start               end
    0  2016-08-10 08:15  2016-08-10 15:00
    1  2016-08-10 17:00  2016-08-10 22:30
    

    生成的图形如下所示: Data generated

    错误代码

    然而,当执行步骤6时,我得到以下错误:

    数据可以使用数组。如果数据具有单个

    在本例中,我希望能够检测相关变量线性变化的两个连续跨度( line1 line2 ransac code example .

    问题:

    我应该修改什么 在我的代码中可以继续吗?以及, 是否有更好的方法来检测相关变量线性变化的连续跨度 ?

    2 回复  |  直到 6 年前
        1
  •  2
  •   Cedric Zoppolo    6 年前

    要继续拟合线性回归,您必须执行以下操作:

    lr.fit(X.reshape(-1,1), y)
    

    sklearn 正在等待值的二维数组,每一行都是要素行。

    如果您正在寻找精确的线性范围(例如,在整数的情况下可以检测到,但在浮点数的情况下不能检测到),那么我将执行以下操作:

    dff = df.diff()
    dff['block'] = (dff.data.shift(1) != dff.data).astype(int).cumsum()
    out = pd.DataFrame(list(dff.reset_index().groupby('block')['index'].apply(lambda x: \
        [x.min(), x.max()] if len(x) > 20 else None).dropna()))
    

    产出将是:

    >>> out
                        0                   1
    0 2016-08-10 08:30:00 2016-08-10 15:00:00
    1 2016-08-10 17:15:00 2016-08-10 22:30:00
    

    如果您试图做类似的事情,但对于浮点数据,我会使用 diff

        2
  •  5
  •   A Kruger    6 年前

    数值误差

    (100,1) df.data.tolist() 它有一个形状 (100,) . 这可以通过重塑形状来修复 X X = X.reshape(-1,1) . 下一个错误是 X 值不能在中 datetime64 格式。这可以通过将时间转换为秒来解决。例如,要使用的标准纪元是 1970-01-01T00:00Z 然后所有的数据点都是从那个日期和时间算起的秒数。此转换可通过以下方式完成:

    X = (X - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
    

    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn import linear_model, datasets
    import numpy as np
    
    ## 1. Generate random data for toy sample
    times = pd.date_range('2016-08-10', periods=100, freq='15min')
    df = pd.DataFrame(np.random.randint(0,100,size=(100, 1)), index=times, columns=["data"])
    
    ## 2. Set line1 within random data
    date_range1_start = "2016-08-10 08:15"
    date_range1_end = "2016-08-10 15:00"
    line1 = df.data[date_range1_start:date_range1_end]
    value_start1 = 10
    values1 = range(value_start1,value_start1+len(line1))
    df.data[date_range1_start:date_range1_end] = values1
    
    ## 3. Set line2 within random data
    date_range2_start = "2016-08-10 17:00"
    date_range2_end = "2016-08-10 22:30"
    value_start2 = 90
    line2 = df.data[date_range2_start:date_range2_end]
    values2 = range(value_start2,value_start2-len(line2),-1)
    df.data[date_range2_start:date_range2_end] = values2
    
    
    ## 4. Create arrays
    X = np.asarray(df.index)
    X = ( X - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
    X = X.reshape(-1,1)
    y = np.asarray(df.data.tolist())
    
    ## 5. Fit line using all data
    lr = linear_model.LinearRegression()
    lr.fit(X, y)
    
    ## 6. Predict values
    z = lr.predict(X)
    df['linear fit'] = z
    
    ## 7. Plot
    df.plot()
    plt.show()
    

    enter image description here

    检测连续跨距

    如您所述,要检测线性数据的跨度,RANSAC是一种很好的方法。为此,线性模型将更改为 lr = linear_model.RANSACRegressor() . 但是,这将只返回一个跨度,而您需要检测所有跨度。这意味着您需要重复跨度检测,同时在每次检测后删除跨度,以便它们不会再次被检测到。应重复此操作,直到检测范围内的点数小于20。

    RANSAC拟合的剩余阈值需要非常小,以避免拾取范围外的点。这个 residual_threshold 如果真实数据中存在任何噪声,则可以更改。然而,这并不总是足够的,可能会发现错误的输入,这将影响记录的量程范围。

    假内窥镜

    由于RANSAC不检查跨度内点是否连续,因此可能会将异常值错误地包含在跨度内。为了防止这种情况,如果标记为范围内的点被异常值包围,则应将其更改为异常值。最快的方法是卷积 lr.inlier_mask_ [1,1,1] . 任何单独的“内联线”在卷积后的值为1(因此实际上是离群值),而作为跨度运行一部分的点为2或3。因此,以下将修复错误的内联线:

    lr.inlier_mask_ = np.convolve(lr.inlier_mask_.astype(int), [1,1,1], mode='same') > 1
    

    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn import linear_model, datasets
    import numpy as np
    
    ## 1. Generate random data for toy sample
    times = pd.date_range('2016-08-10', periods=100, freq='15min')
    df = pd.DataFrame(np.random.randint(0,100,size=(100, 1)), index=times, columns=["data"])
    
    ## 2. Set line1 within random data
    date_range1_start = "2016-08-10 08:15"
    date_range1_end = "2016-08-10 15:00"
    line1 = df.data[date_range1_start:date_range1_end]
    value_start1 = 10
    values1 = range(value_start1,value_start1+len(line1))
    df.data[date_range1_start:date_range1_end] = values1
    
    ## 3. Set line2 within random data
    date_range2_start = "2016-08-10 17:00"
    date_range2_end = "2016-08-10 22:30"
    value_start2 = 90
    line2 = df.data[date_range2_start:date_range2_end]
    values2 = range(value_start2,value_start2-len(line2),-1)
    df.data[date_range2_start:date_range2_end] = values2
    
    ## 4. Create arrays
    X = np.asarray(df.index)
    X = ( X - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
    X = X.reshape(-1,1)
    y = np.asarray(df.data.tolist())
    
    ## 5. Fit line using all data
    lr = linear_model.RANSACRegressor(residual_threshold=0.001)
    lr.fit(X, y)
    
    # Placeholders for start/end times
    start_times = []
    end_times = []
    
    # Repeat fit and check if number of span inliers is greater than 20
    while np.sum(lr.inlier_mask_) > 20:
    
        # Remove false inliers
        lr.inlier_mask_ = np.convolve(lr.inlier_mask_.astype(int), [1,1,1], mode='same') > 1
    
        # Store start/end times
        in_span = np.squeeze(np.where(lr.inlier_mask_))
        start_times.append(str(times[in_span[0]]))
        end_times.append(str(times[in_span[-1]]))
    
        # Get outlier and check for another span
        outliers = np.logical_not(lr.inlier_mask_)
        X = X[outliers]
        y = y[outliers]
        times = times[outliers]
    
        # Fit to remaining points
        lr.fit(X, y)
    
    out = pd.DataFrame({'start':start_times, 'end':end_times}, columns=['start','end'])
    out.sort_values('start')
    

    这是我的建议 out 数据帧:

    enter image description here

    还可以绘制要验证的跨距。

    plt.plot(df['data'],c='b')
    
    for idx,row in out.iterrows():
        x0 = np.datetime64(row['start'])
        y0 = df.loc[x0]['data']
        x1 = np.datetime64(row['end'])
        y1 = df.loc[x1]['data']
        plt.plot([x0,x1],[y0,y1],c='r')
    

    enter image description here