在自动驾驶数据仓库,数据都是以时间片的方式进行保存、检索和流转的,但是这些时间片数据的自然时长可能在很大程度上无法直接满足模型的训练和评测,因此时间片的切分、交集计算、合并,前后扩等,就成了对数据进行二次加工、处理的常态化需求,这篇文章给小伙伴们分享一个对连续时间片进行合并的算法。
既然是时间片段,那么数据就天然的具备一个属性:时间顺序,因此实现时间片的合并,需要两个步骤:
按时间先后顺序对数据进行排序。
时间上连续的片段进行合并。
下面分别实现这两个步骤。
1、对时间片进行排序
通常情况,数仓数据的获取都来源于检索,根据用户设定的检索条件,比如最近一周内、左转、路口直行、自动驾驶,这些条件,从检索库获取到基础数据,这些基础数据通常都特定的格式,比如:
{
"car_id": "京A88888",
"start_time": 1750859435000,
"end_time": 1750859439000,
"map_region": "beijing_houchangcun",
"hardware_version": 2
}
当我们拿到这些数据后,需要通过代码对其进行排序,如上的数据结构,golang无法直接对其进行处理,需要我们自己来实现,首先看一下golang支持的排序,比如如下代码,实现对整型、字符串、浮点型的升序排列:
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]
}
降序排列:
// 对整数切片降序排序
nums := []int{4, 2, 7, 1, 3}
sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println(nums) // 输出: [7 4 3 2 1]
以上两种方式实现的是对基本数据类型的排序,对于自定义的数据类型无法支持,我们需要自己来实现。
首先定义一个结构体用于存储以上数据:
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接口:
// 按起始时间排序的 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 }
第三,进行排序:
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(前提:已排好序),那么这两个时间片段就是连续的,就可以将其合并在一起。
合并代码如下:
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合并成了一个。
以上就是时间片合并的全部内容了,学习中有任何问题,可以留言讨论~~~~。
欢迎各位热爱技术的小伙伴们点个关注,聊聊技术、聊聊跑步、聊聊读书~~~。
往期推荐:
Elastic:索引生命周期管理(Index Lifecycle Management)-减轻ES存储压力
一个异步架构设计:批量消费RabbitMQ,批量写入Elasticsearch(golang实现)
一个优秀的rabbitmq消费者(consumer)设计,可直接上线使用。
Python web框架sanic+tortoise服务框架搭建(MVP版本)
命令行参数的艺术:Python、Golang、C++技术实现
借助tritonserver完成gpt2模型的本地私有化部署
一文揭秘:Golang+Elasticsearch轻松搭建AI时代的图片搜索服务
大同华严寺:受人民爱戴的耿市长还会回来吗?薄伽教藏的合掌漏齿菩萨你知道是谁吗?
云冈石窟:翻开这本距今1565年、与天地同久长的石头史书,感受北魏王朝雕刻艺术的巅峰之作。