自动驾驶数据仓库:时间片合并算法。

在自动驾驶数据仓库,数据都是以时间片的方式进行保存、检索和流转的,但是这些时间片数据的自然时长可能在很大程度上无法直接满足模型的训练和评测,因此时间片的切分、交集计算、合并,前后扩等,就成了对数据进行二次加工、处理的常态化需求,这篇文章给小伙伴们分享一个对连续时间片进行合并的算法

既然是时间片段,那么数据就天然的具备一个属性:时间顺序,因此实现时间片的合并,需要两个步骤:

  • 按时间先后顺序对数据进行排序。

  • 时间上连续的片段进行合并。

下面分别实现这两个步骤。

1、对时间片进行排序

通常情况,数仓数据的获取都来源于检索,根据用户设定的检索条件,比如最近一周内、左转、路口直行、自动驾驶,这些条件,从检索库获取到基础数据,这些基础数据通常都特定的格式,比如:

复制代码
{
    "car_id": "京A88888",
    "start_time": 1750859435000,
    "end_time": 1750859439000,
    "map_region": "beijing_houchangcun",
    "hardware_version": 2
}

当我们拿到这些数据后,需要通过代码对其进行排序,如上的数据结构,golang无法直接对其进行处理,需要我们自己来实现,首先看一下golang支持的排序,比如如下代码,实现对整型、字符串、浮点型的升序排列

Go 复制代码
package main
import (
"fmt"
"sort"
)
func main() {
// 对整数切片排序
	nums := []int{4, 2, 7, 1, 3}
	sort.Ints(nums)
	fmt.Println(nums) // 输出: [1 2 3 4 7]
// 对字符串切片排序
	names := []string{"Zoe", "Alice", "Bob", "John"}
	sort.Strings(names)
	fmt.Println(names) // 输出: [Alice Bob John Zoe]
// 对浮点数切片排序
	floats := []float64{3.2, 1.5, 4.7, 2.1}
	sort.Float64s(floats)
	fmt.Println(floats) // 输出: [1.5 2.1 3.2 4.7]
}

降序排列:

Go 复制代码
// 对整数切片降序排序
nums := []int{4, 2, 7, 1, 3}
sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println(nums) // 输出: [7 4 3 2 1]

以上两种方式实现的是对基本数据类型的排序,对于自定义的数据类型无法支持,我们需要自己来实现。

首先定义一个结构体用于存储以上数据:

Go 复制代码
type DwData struct {
	CarID string `json:"car_id"`
	StartTime int64 `json:"start_time"`
	EndTime int64 `json:"end_time"`
	MapRegion string `json:"map_region"`
	HardwareVersion int `json:"hardware_version"`
}

第二,实现sort.Interface接口:

Go 复制代码
// 按起始时间排序的 ByStartTime 切片类型
type ByStartTime []DwData

func (a ByStartTime) Len() int           { return len(a) }
func (a ByStartTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByStartTime) Less(i, j int) bool { return a[i].StartTime < a[j].StartTime }

第三,进行排序:

Go 复制代码
type DwData struct {
    CarID           string `json:"car_id"`
    StartTime       int64  `json:"start_time"`
    EndTime         int64  `json:"end_time"`
    MapRegion       string `json:"map_region"`
    HardwareVersion int    `json:"hardware_version"`
}
// 按起始时间排序的 ByStartTime 切片类型
type ByStartTime []DwData
func (a ByStartTime) Len() int           { return len(a) }
func (a ByStartTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByStartTime) Less(i, j int) bool { return a[i].StartTime < a[j].StartTime }
func main() {
    clips := []DwData{
        {
            CarID:           "1234567890",
            StartTime:       1200,
            EndTime:         1205,
            MapRegion:       "region_a",
            HardwareVersion: 1,
        },
        {
            CarID:           "1234567890",
            StartTime:       1100,
            EndTime:         1105,
            MapRegion:       "region_a",
            HardwareVersion: 1,
        },
    }
    sort.Sort(ByStartTime(clips))
    fmt.Println(clips) // 按起始时间排序后的切片,先打印出StartTime=1100的记录
}

排序问题解决后,接下来就可以对相连的时间片进行合并,从而获取到更长的时间片。

2、对连续的时间片进行合并

对于以下时间片段,连续的时间片合并后,会新生成四个片段,分别是:

蓝色一组为一个连续片段、红色一个、绿色一个、黄色一个。

合并的思路,由于StartTime与EndTime都是单位为毫秒的时间戳,所以只要第一个时间片的EndTime+1000大于等于下一个时间片的StartTime(前提:已排好序),那么这两个时间片段就是连续的,就可以将其合并在一起。

合并代码如下:

Go 复制代码
func MergeClips(adcStatus []DwData) []DwData {
    adcTags := []DwData{}
    // 按起始时间排序
    sort.Sort(ByStartTime(adcStatus))
    if len(adcStatus) == 0 {
        return adcTags
    }
    // 初始化current变量,用于记录当前正在处理的片段
    current := adcStatus[0]
    for _, v := range adcStatus[1:] {
        if int64(current.EndTime*1000+1000) >= int64(v.StartTime*1000) {
            // 如果当前片段的结束时间大于等于下一个片段的开始时间,则合并这两个片段
            current.EndTime = v.EndTime
        } else {
            autoTag := DwData{
                CarID:     v.CarID,
                StartTime: int64(current.StartTime * 1000),
                EndTime:   int64(current.EndTime * 1000),
            }
            // 当前的时间片和上一个时间片不相邻,则将上一个片段加入到结果切片中
            adcTags = append(adcTags, autoTag)
            // 更新当前片段为下一个片段
            current = v
        }
    }
    autoTag := DwData{
        CarID:     current.CarID,
        StartTime: int64(current.StartTime * 1000),
        EndTime:   int64(current.EndTime * 1000),
    }
    adcTags = append(adcTags, autoTag)
    return adcTags
}

上图中的时间片,经过以上的代码处理后,将会变成如下的样子:

c1、c2、c3合并成为一个时间片,c4、c5由于不连续保持原状,c6、c7合并成了一个。

以上就是时间片合并的全部内容了,学习中有任何问题,可以留言讨论~~~~。

欢迎各位热爱技术的小伙伴们点个关注,聊聊技术、聊聊跑步、聊聊读书~~~。

往期推荐:

我在百度的这10年~~

一个有线上问题带来的知识点:Python日志打印

一个实际上线的项目:Elastic检索库历史数据清理

Elastic:索引生命周期管理(Index Lifecycle Management)-减轻ES存储压力

Python Tortoise ORM库的基础操作介绍

一个异步架构设计:批量消费RabbitMQ,批量写入Elasticsearch(golang实现)

一个优秀的rabbitmq消费者(consumer)设计,可直接上线使用。

Python web框架sanic+tortoise服务框架搭建(MVP版本)

命令行参数的艺术:Python、Golang、C++技术实现

supervisor,你理应知道。

借助tritonserver完成gpt2模型的本地私有化部署

微信小程序文章列表焕新颜:从丑小鸭到白天鹅的华丽蜕变

一文揭秘:Golang+Elasticsearch轻松搭建AI时代的图片搜索服务

protobuf c++开发快速上手指南

golang线程池ants-你会用了吗

tfrecord文件介绍、读取、写入介绍

大同华严寺:受人民爱戴的耿市长还会回来吗?薄伽教藏的合掌漏齿菩萨你知道是谁吗?

云冈石窟:翻开这本距今1565年、与天地同久长的石头史书,感受北魏王朝雕刻艺术的巅峰之作。

一个读写excel的简单程序(golang)

历经沧桑的应县木塔,在风雨中已等你969年。

从北京到大同,走过600里,跨越1000年。

跑步的第六年,才真正了解运动的意义

武汉抗疫英雄汪勇:平凡人的非凡之举。

李白:为何两次选择做了上门女婿?

2025年,我要做个自我介绍

相关推荐
_殊途1 小时前
《Java HashMap底层原理全解析(源码+性能+面试)》
java·数据结构·算法
珊瑚里的鱼5 小时前
LeetCode 692题解 | 前K个高频单词
开发语言·c++·算法·leetcode·职场和发展·学习方法
秋说6 小时前
【PTA数据结构 | C语言版】顺序队列的3个操作
c语言·数据结构·算法
lifallen6 小时前
Kafka 时间轮深度解析:如何O(1)处理定时任务
java·数据结构·分布式·后端·算法·kafka
野生技术架构师7 小时前
MySQL数据实时同步到Elasticsearch的高效解决方案
数据库·mysql·elasticsearch
python_tty7 小时前
排序算法(二):插入排序
算法·排序算法
然我8 小时前
面试官:如何判断元素是否出现过?我:三种哈希方法任你选
前端·javascript·算法
risc1234568 小时前
【Lucene/Elasticsearch】 数据类型(ES 字段类型) | 底层索引结构
elasticsearch
F_D_Z8 小时前
【EM算法】三硬币模型
算法·机器学习·概率论·em算法·极大似然估计