数值误差
(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()
检测连续跨距
如您所述,要检测线性数据的跨度,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
数据帧:
还可以绘制要验证的跨距。
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')