fisher分割(python实现)--交通信控时段划分

本文介绍了Fisher最优分割法的概念和递推公式,用于对有序样本进行聚类。该方法在交通信控时段划分中,通过寻找最小组内差异和最大组间差异的分割,以优化交通信号控制。文章提供了Python代码实现,包括计算组内样本均值和直径,以及损失函数。此外,还讨论了交通信控的挑战和后续研究方向,如时段长度和相邻交叉口的协同考虑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

fisher分割(python实现)–交通信控时段划分

本文重点参考该篇博文:

【版权声明:本文为CSDN博主「Trisyp」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明】
【原文链接:https://blog.csdn.net/Trisyp/article/details/118492913

一、理论知识

1.1、Fisher最优分割法概念及重要指标

最优分割法

是对有序样本的一种聚类方法,例如 N N N个样品按顺序排列 { x 1 , x 2 , . . . x N } \{x_1,x_2,...x_N\} {x1,x2,...xN},在分类中不允许打破样本的顺序。即 N N N个有序样本进行分割,可能有 2 N − 1 2^{N-1} 2N1种划分方式:N=2时,有2种划分( 2 1 2^1 21,表示该样本是否加入前面的划分);N=3时,有4种划分( 2 2 2^2 22)种划分方法,这每一种分法称为一种分割。

在所有的这些分割中,找到一种分割法,这种分割法使得各组内样品之间的差异最小,而各组之间的差异最大。这种对 N N N个样品分组并使组内差异最小的分割方法,称为最优分割法。

对于第 k k k组样本 { x i , x i + 1 . . . x j } \{x_i,x_{i+1}...x_j\} {xi,xi+1...xj},组内样本差异计算公式如下(求组内样本均值,然后计算方差):
d i j = ∑ k s = i j [ x k s − x k ˉ ( i , j ) ] 2 d_{ij}=\sum_{k_s=i}^{j}[{x_{k_s}}-\bar{x_k}{(i,j)}]^2 dij=ks=ij[xksxkˉ(i,j)]2

x k ˉ ( i , j ) = 1 j − i + 1 ∑ k s = i j x k s \bar{x_k}{(i,j)}=\frac{1}{j-i+1}\sum_{k_s=i}^{j}x_{k_s} xkˉ(i,j)=ji+11ks=ijxks

Fisher最优分割法

Fisher最优分割法是用离差平方和来表示同类样本之间的差异程度(可以称为该组的直径 D ( i , j ) D{(i,j)} D(i,j),与 d i , j d_{i,j} di,j计算公式相同,也可以采用其他的距离度量方式,例如计算各个样本与样本中位数的差异),通过迭代计算,确定最优分类数,使同类样本间的差异最小,各类别样本间的差异最大。

1.2、Fisher最优分割递推公式及计算流程

b ( N , K ) b(N,K) b(N,K)表示将 N N N个有序样本分为 K K K类的一种方法,定义损失函数为 L [ b ( N , K ) ] = ∑ k = 1 K D k ( i , j ) L[b(N,K)]=\sum_{k=1}^{K}D_k(i,j) L[b(N,K)]=k=1KDk(i,j)

使损失函数最小的分类方法即为最优分割,记为 P ( N , K ) P(N,K) P(N,K)

计算流程

目标:对于 N N N个有序样本,分类数 K K K已知,求最优分割方法 P ( N , K ) P(N,K) P(N,K)

算法流程:

  1. 对于最优分割 L [ P ( N , K ) ] L[P(N,K)] L[P(N,K)],最右侧的类别 K K K的直径为 D ( 1 , N ) D(1,N) D(1,N),则最优分割的损失函数可以写成: L [ P ( N , K ) ] = m i n k ≤ j ≤ N { L [ P ( j − 1 , K − 1 ) ] + D ( j , N ) } L[P(N,K)]=min_{k{\leq}j{\leq}N}\{L[P(j-1,K-1)]+D(j,N)\} L[P(N,K)]=minkjN{L[P(j1,K1)]+D(j,N)},其中 L [ P ( j − 1 , K − 1 ) ] L[P(j-1,K-1)] L[P(j1,K1)]表示对前面 j − 1 j-1 j1个样本划分为 K − 1 K-1 K1类的最优分割;
  2. 采用该公式可依次往前递推,确定各个类别的最优分割;
  3. 确定 N N N个样本划分为一类( K = 1 K=1 K=1)的损失函数及最优分割: L [ P ( N , 1 ) ] = L [ b ( N , 1 ) ] = D ( 1 , N ) L[P(N,1)]=L[b(N,1)]=D(1,N) L[P(N,1)]=L[b(N,1)]=D(1,N)

举例说明:

L [ P ( 3 , 2 ) ] = m i n { L [ P ( 1 , 1 ) ] + D ( 2 , 3 ) , L [ P ( 2 , 1 ) ] + D ( 3 , 3 ) } L[P(3,2)]=min\{L[P(1,1)]+D(2,3),L[P(2,1)]+D(3,3)\} L[P(3,2)]=min{L[P(1,1)]+D(2,3),L[P(2,1)]+D(3,3)}

在这里插入图片描述

可参考LeetCode中的动态规划相关题目:

题目:剑指 Offer II 101. 分割等和子集

描述:给定一个非空的正整数数组 n u m s nums nums ,请判断能否将这些数字分成元素和相等的两部分。

题解链接(动态规划求解):https://leetcode.cn/problems/NUPfPr/solutions/1417796/fen-ge-deng-he-zi-ji-by-leetcode-solutio-re1t/

二、应用场景

2.1、交通信控时段划分

大多数交叉口全天交通流量是不断变化的,一般可主观划分为八个时段:早平峰、早高峰、上午平峰、中午闲散、下午平峰、晚高峰、晚平峰、夜间闲散。一般早晚高峰时期的交通流量大于其他时段,如果全天采用同一套定时配时方案很难适应路口交通量的变化,使交叉口在局部时间内出现拥堵或空放现象。因此,为保障交叉口的有效通行,应在不同时段应采用不同信号控制策略及配时方案。

在这里插入图片描述

基于交通流量的时段划分,可以看成是有序序列的分割问题,核心是找到一组分割,使得组内方差最小,组间方差最大。Fisher最优分割法是常用的交通信控时段划分算法:一般以5分钟为间隔统计交通流量作为一个样本,即全天共有288个样本( N = 288 N=288 N=288),分类数 K K K未知,可以通过比较 K K K取不同值的损失函数值确定最优分类数(类似于K-means聚类通过“肘形图”确定聚类数:http://t.csdn.cn/eRMaj

在这里插入图片描述

三、代码精讲

计算组内样本均值及直径
def get_class_ave(samples, start, end):
    """
    计算某一类的均值
    :param samples: 所有输入数据
    :param start: 某一类的开始索引(包含)
    :param end: 某一类的结束索引(不包含)
    :return:
    """
    class_ave = 0.0
    for i in range(start, end):
        class_ave += samples[i]
    class_ave = class_ave / (end - start)
    return class_ave


def get_class_diameter(samples, start, end):
    """
    计算某一类的直径(类内各样本的差异)
    :param samples: 所有输入数据
    :param start: 某一类的开始索引(包含)
    :param end: 某一类的结束索引(不包含)
    :return:
    """
    class_diameter = 0.0
    class_ave = get_class_ave(samples, start, end)
    for i in range(start, end):
        class_diameter += (samples[i] - class_ave) ** 2
    return class_diameter
计算不同样本采取不同分割的损失函数值
def get_split_loss(samples, sample_num, split_class_num):
    """
    计算得到不同样本划分为不同分类的loss矩阵
    :param samples: 样本
    :param sample_num: 样本数
    :param split_class_num: 最大分类数
    :return: 不同样本划分为不同分类的loss矩阵
    """
    # 记录不同样本数(1~sample_num)分成不同类(1~split_class_num)的loss值
    split_loss_result = np.zeros((sample_num + 1, split_class_num + 1))

    # 对于第一列k=1
    for n in range(1, sample_num + 1):
        # 将所有样本分成1类,直接调用函数get_class_diameter计算
        # 递推公式L(P(n,1))=D(1,n),其中P(n,1)表示将n个样本分成1类的最优划分,D(1,n)表示所有样本的差异
        # 该式表示将n个样本分成1类的损失函数值L=该类的直径D(类内各样本的差异)
        split_loss_result[n, 1] = get_class_diameter(samples, 0, n)

    # 使用递推公式计算k>1时采取不同划分的损失函数值
    for k in range(2, split_class_num + 1):
        # n不能小于k
        for n in range(k, sample_num + 1):
            # 递推公式:L(P(n,k))=min{L(P(j-1,k-1))+D(j,n)}
            # 其中,k<=j<=n,要保证前面每一类都至少有一个样本(j>=k),最后一类至少有一个样本(j<=n)
            loss = []
            for j in range(k - 1, n):
                loss.append(split_loss_result[j, k - 1] + get_class_diameter(samples, j, n))
            split_loss_result[n, k] = min(loss)

    return split_loss_result
确定样本分类信息
def get_split_info(samples, split_loss_result):
    """
    确定最佳分类数、分类点、各个样本的类别
    :param samples: 样本
    :param split_loss_result: loss矩阵
    :return: 最佳分类数、分类点、各个样本的类别
    """
    # 首先确定最优分类数k_best
    # 以增加一个分类后loss下降不足30%(需多次尝试不同值比较分类效果)作为阈值确定k_best
    loss_n_k = split_loss_result[-1, 1:]
    k_best = 1
    for i in range(len(loss_n_k) - 1):
        # 对于loss为0的情况,直接确定k_best
        if loss_n_k[i] == 0:
            k_best = i + 1
            break
        else:
            desc_rate = (loss_n_k[i] - loss_n_k[i + 1]) / loss_n_k[i]
            if desc_rate > 0.05:
                k_best = i + 2

    # 确定各个样本所属类别
    split_point = []
    k = k_best - 1
    n = len(samples)
    split_loss = split_loss_result[n][k_best]
    while k > 0:

        while n > 0:
            # 寻找类别划分点
            if split_loss_result[n][k] < split_loss:
                split_point.insert(0, n)
                split_loss = split_loss_result[n][k]
                break
            n -= 1
        k -= 1

    # 确定样本类别
    sample_class = []
    class_index = 1
    point_index = 0

    # split_point中补充完整
    # 举例:split_point = [6],有9个样本,则补充完整的split_point = [1, 6, 9]
    split_point.insert(0, 1)
    split_point.append(len(samples))
    for i in range(len(samples)):
        if i < split_point[point_index + 1]:
            sample_class.append(class_index)
        else:
            point_index += 1
            class_index += 1
            sample_class.append(class_index)

    return k_best, split_point, sample_class
测试样例

测试样本:[5, 6, 7, 1, 2, 1, 10, 11, 12]

计算得到损失函数值矩阵为

在这里插入图片描述

可确定最佳分类数、分割点、各个样本所属类别

在这里插入图片描述

四、后续研究方向

针对交通信控时段划分算法提出以下几点研究方向:

  1. 时段过短则方案变化、过渡过于频繁会导致控制效果不佳:要求每个时段必须包含一定的样本量(例如要求时段至少为15分钟,则需要至少包含3个样本(以5分钟为统计间隔的话));
  2. 允许指定时段,例如7:00-7:30指定为一个时段,设置了专属的控制策略,则可以对于指定时段前后的时段进行划分,再合并时段;
  3. 时段划分考虑相邻交叉口,即对同一子区内的交叉口进行时段划分。

五、参考链接:

完整代码及数据可在此处下载:

[1] https://download.csdn.net/download/weixin_42639395/87929803

Fisher分割相关:

[2] https://blog.csdn.net/Trisyp/article/details/118492913

动态规划相关:

[3] https://leetcode.cn/problems/NUPfPr/solutions/1417796/fen-ge-deng-he-zi-ji-by-leetcode-solutio-re1t/

交通信控时段划分相关:

[4] https://zhuanlan.zhihu.com/p/619567481

[5] 袁淑芬. 基于过车数据的单点交叉口多时段信号配时优化[D].东南大学,2019.DOI:10.27014/d.cnki.gdnau.2019.003104.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值