时间序列分段:Top-Down算法python实现

算法原理:
自顶向下TD算法:时间序列的开始点和结束点是首先选中的分段点。然后,遍历两点之间的所有点,找出和这两点连成的直线距离最大的点,如果这个点到直线的距离“大于”预先给定的阀值,则将它作为第三个分段点。这样我们就有了两个线段,做了最初步的划分。
之后,这个新增点到左边相邻点和右边相邻点构成的两条线段,继续寻找距离最大的点,然后,找到的两个点,谁与相应的线段距离最大,且这个距离“大于”阀值,则该点作为第四个分段点….如此循环,直到再也找不到距离大于R的点,分段完成。
这个阀值,也就是点到线段的距离,可以使用正交距离(原始点和分段线段在该点的值的差的绝对值)、垂直距离(原始点到分段线段的直线的长度)和欧式距离,当然也可以设置其他的特性作为阀值,比如拟合误差、又比如弧度、角度、余弦等,由此可以引申很多种不同的算法。
参考文章:http://www.cnblogs.com/by1990/archive/2011/01/15/1936296.html##

具体代码

#计算点到直线的距离
def calculate_error(st, seq_range):
    x = np.arange(seq_range[0], seq_range[1] + 1)
    y = np.array(st[seq_range[0]:seq_range[1] + 1])
    A = np.ones((len(x), 2), float)
    A[:, 0] = x
    # 返回回归系数、残差平方和、自变量X的秩、X的奇异值
    (p, residuals, ranks, s) = np.linalg.lstsq(A, y, rcond=None)#最小二乘法回归
    try:
        error = residuals[0]
    except IndexError:
        error = 0.0
    return error
def improvement_splitting_here(T, i, seq_range):
    return calculate_error(T, (seq_range[0], i)) + calculate_error(T, (i + 1, seq_range[1]))
def Top_Down(T, max_error, seq_range=None):
    if not seq_range:
        seq_range = (0, len(T) - 1)
    best_so_far = float('inf')
    break_point = float('inf')
    for i in range(seq_range[0] + 1, seq_range[1]):
        improvement_in_approximation = improvement_splitting_here(T, i, seq_range)
        if improvement_in_approximation < best_so_far:
            break_point = i
            best_so_far = improvement_in_approximation
    left_error = calculate_error(T, (seq_range[0], break_point))
    left_seg = T[seq_range[0]:break_point + 1]
    right_error = calculate_error(T, (break_point+1, seq_range[1]))
    right_seg = T[break_point+1:seq_range[1]+1]
    if left_error > max_error:
        segleft = Top_Down(T, max_error, (seq_range[0], break_point))
    else:
        segleft = [left_seg]
    if right_error > max_error:
        segright = Top_Down(T, max_error, (break_point, seq_range[1]))
    else:
        segright = [right_seg]
    return segleft + segright
df_2001_split=Top_Down(df_2001['arousal'],0.02)
#df_2001为DataFrame,df_2001['arousal']为其中一列572点的series,想要对这一series进行分段;
#返回得到的df_2001_split是list,每一行为分成的一段;
#0.02为多次尝试后设置的阈值;
x=[]#x存储分界点的index
y=[]#根据index
for i in range(0,len(df_2001_split)):
    x.append(df_2001_split[i].index.start)
x.append(len(df_2001)-1)#记得再加上最后一个点的index
for i in x:
    y.append(df_2001['arousal'][(df_2001['arousal'].index==i)])
y=np.array(y)

输出x为:
[0, 31, 46, 73, 89, 104, 124, 145, 154, 194, 219, 232, 245, 254, 280, 290, 298, 329, 346, 347, 388, 392, 445, 449, 502, 551, 557, 560, 567, 571]
对应的y为:[[ 0.166] [ 0.076] [ 0.056] [ 0.112] [ 0.24 ] [ 0.36 ] 0.238] [ 0.012] [-0.218[-0.154] [-0.198] [-0.002] [ 0.138] [ 0.52 ] [ 0.52 ] [ 0.534] [ 0.546] [ 0.62 ] [ 0.512][ 0.414] 0.466] [ 0.588] [ 0.826] [ 0.788] [ 0.824] [ 0.84 ] [ 0.768] [ 0.592] [ 0.172] [ 0.208]]

#图形表示
plt.figure(figsize=(10,5))
plt.plot(df_2001['arousal'].index,df_2001['arousal'])
plt.scatter(x,y,color="r")
plt.show()

wenti时间序列分段:Top-Down算法python实现
问题记录:
1.list array 之间的转换:list里的变量既有数值型又有字符串类型时(即使只有一个元素是字符串型),转换成的array里就全都是字符串型。
2.如何根据索引找数据点:df_2001[‘arousal’][(df_2001[‘arousal’].index==i)]
3.df_2001_split[i].index.start 这句,胡乱写的还真行

上一篇:Immutable.js学习笔记(一)----- Immutable简介


下一篇:剑指offer打卡 week4