matlab 时间轴对齐,时间轴对齐算法(附单元测试)

要求

时间粒度

时间粒度根据查询跨度自适应

时间粒度是指趋势图中,一个数据系列的数据点间时间跨度,或是数据点采样时间。

用户查询的跨度变化较大,从1小时到30天,不同的跨度趋势图中应有不同的时间粒度。如果用户查询24小时,而时间粒度为1小时,则一根趋势线只有24个点,不实用且很丑。

以下表格,说明了大部份情况下,查询时间跨度与时间粒度的合理值:

查询时间跨度

时间粒度

数据点数

原始数据点数(30秒)

30天

4小时

180

86400

30分钟

10秒

180

60

12小时

5分钟

144

1440

7天

1小时

168

20160

6小时

2分钟

180

720

3天

30分钟

144

8640

1小时

20秒

180

120

1天

10分钟

144

2880

注意:

一个数据系列,数据点个数在120~200之间为合适;

时间粒度以2或5的倍数为佳,方便与自然时间对齐

当 时间粒度 <= 采集周期 时,即是原始数据粒度了

当 时间粒度 > 采集周期 时,则往往需要进行数据的时间聚合(即rollup或downsample)。

时间粒度与自然习惯对齐

在趋势图中,采用了某种时间粒度后,数据点的时间轴刻度,应该是与自然语言对齐,方便人阅读理解。

时间粒度

生成时间轴刻度举例

30分钟

1时0分,1时30分,2时0分...

5分钟

1时0分,1时5分,1时10分...

4小时

0时0分,4时0分,8时0分...

2分钟

1时0分,1时2分,1时4分...

每一个时间粒度,都代表了其时间周期内的所有数据聚合后的结果。如30分钟粒度下的,1时30分代表[1时30分,2时0分)的所有数据。

输入参数:开始时间,结束时间

输出结果:时间切割后的时间数组(大小在120~200范围内)

目前算法的缺陷在于临界点周围过度不够平滑,待后来者改进

import java.text.ParseException;

import java.util.ArrayList;

import java.util.List;

/**

* network设备概况时间对齐算法

*/

public class NetworkTimeAlineUtil {

/**

* 根据开始时间和结束时间来计算得到时间数组

* 使得时间数组size位于120~200区间内

* 具体参考KB:http://kb.uyunsoft.cn/kb/pages/viewpage.action?pageId=37199282

*

* @param beginTime

* @param endTime

* @return

*/

public static List getSearchTime(long beginTime, long endTime) throws ParseException {

List outputList = new ArrayList();

if (endTime < beginTime) {

return new ArrayList();

}

long totalInterval = endTime - beginTime;

beginTime = resetTime(beginTime, totalInterval);

endTime = resetTime(endTime, totalInterval);

if (totalInterval > 30 * 24 * 60 * 60 * 1000l) { //大于30天,则按照200来进行切割

long interval = totalInterval / 200;

for (int i = 0; i < 200; i++) {

outputList.add(beginTime + interval * i);

}

outputList.add(endTime);

} else if (totalInterval > 7 * 24 * 60 * 60 * 1000l) { //大于7天,小于等于30天(169~720小时),则按照1小时数量进行切割

if (totalInterval >= 601 * 60 * 60 * 1000l) {//601~720小时,按照4小时进行截取

long size = totalInterval / (4 * 60 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 4 * 60 * 60 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 401 * 60 * 60 * 1000l) {//401~600小时,按照3小时截取

long size = totalInterval / (3 * 60 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 3 * 60 * 60 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 240 * 60 * 60 * 1000l) {//240~400小时,按照2小时截取

long size = totalInterval / (2 * 60 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 2 * 60 * 60 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 201 * 60 * 60 * 1000l) {//201~239小时,按1.5小时截取

long size = totalInterval / (90 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 90 * 60 * 1000l * i);

}

outputList.add(endTime);

} else {//169~200小时,按1小时截取

long size = totalInterval / (1 * 60 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 1 * 60 * 60 * 1000l * i);

}

outputList.add(endTime);

}

} else if (totalInterval > 3 * 24 * 60 * 60 * 1000l) { //大于3天,小于等于7天(73~168小时),则按照30分钟为最小数量进行切割

if (totalInterval >= 120 * 60 * 60 * 1000l) {//120~168小时,按60分钟截取

long size = totalInterval / (60 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 60 * 60 * 1000l * i);

}

outputList.add(endTime);

} else {//73~119小时,按30分钟截取

long size = totalInterval / (30 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 30 * 60 * 1000l * i);

}

outputList.add(endTime);

}

} else if (totalInterval > 1 * 24 * 60 * 60 * 1000l) { //大于1天,小于等于3天(25~72小时),则按照10分钟为最小数量进行切割

if (totalInterval >= 60 * 60 * 60 * 1000l) {//60~72小时,按30分钟截取

long size = totalInterval / (30 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 30 * 60 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 40 * 60 * 60 * 1000l) {//40~60小时,按20分钟截取

long size = totalInterval / 20 * 60 * 1000l;

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 20 * 60 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 30 * 60 * 60 * 1000l) {//30~39小时,按15分钟截取

long size = totalInterval / (15 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 15 * 60 * 1000l * i);

}

outputList.add(endTime);

} else {//25~29小时,按10分钟截取

long size = totalInterval / (10 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 10 * 60 * 1000l * i);

}

outputList.add(endTime);

}

} else if (totalInterval > 12 * 60 * 60 * 1000l) { //大于12小时,小于等于24小时,则按照5分钟为最小数量进行切割

if (totalInterval >= 20 * 60 * 60 * 1000l) {//20~24小时,按10分钟截取

long size = totalInterval / (10 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 10 * 60 * 1000l * i);

}

outputList.add(endTime);

} else {//13~20小时,按5分钟截取

long size = totalInterval / (5 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 5 * 60 * 1000l * i);

}

outputList.add(endTime);

}

} else if (totalInterval > 360 * 60 * 1000l) { //大于360分钟,小于等于720分钟,则按照2分钟为最小数量进行切割

if (totalInterval >= 480 * 60 * 1000l) {//480~720分钟,按4分钟截取

long size = totalInterval / (4 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 4 * 60 * 1000l * i);

}

outputList.add(endTime);

} else {//361~479分钟,按3分钟截取

long size = totalInterval / (3 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 3 * 60 * 1000l * i);

}

outputList.add(endTime);

}

} else if (totalInterval > 60 * 60 * 1000l) { //大于60分钟,小于等于360分钟,则按照20秒为最小数量进行切割

if (totalInterval >= 240 * 60 * 1000l) {//240~360分钟,按2分钟截取

long size = totalInterval / (2 * 60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 2 * 60 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 180 * 60 * 1000l) {//180~240分钟,按90秒截取

long size = totalInterval / (90 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 90 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 120 * 60 * 1000l) {//120~179分钟,按60秒截取

long size = totalInterval / (60 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 60 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 80 * 60 * 1000l) {//80~119分钟,按40秒截取

long size = totalInterval / (40 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 40 * 1000l * i);

}

outputList.add(endTime);

} else {//61~79分钟,按30秒截取

long size = totalInterval / (30 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 30 * 1000l * i);

}

outputList.add(endTime);

}

} else if (totalInterval > 30 * 60 * 1000l) { //大于30分钟,小于等于60分钟,则按照10秒为最小数量进行切割

if (totalInterval >= 40 * 60 * 1000l) {//40~60分钟,按20秒截取

long size = totalInterval / (20 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 20 * 1000l * i);

}

outputList.add(endTime);

} else if (totalInterval >= 180 * 60 * 1000l) {//31~39分钟,按10秒截取

long size = totalInterval / (90 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 90 * 1000l * i);

}

outputList.add(endTime);

}

} else { //小于等于30分钟

if (totalInterval >= 20 * 60 * 1000l) {//20~30分钟,按10秒截取

long size = totalInterval / (10 * 1000l);

for (int i = 0; i < size; i++) {

outputList.add(beginTime + 10 * 1000l * i);

}

outputList.add(endTime);

} else {//小于20分钟,则按照200切割

long interval = totalInterval / 200;

for (int i = 0; i < 200; i++) {

outputList.add(beginTime + interval * i);

}

outputList.add(endTime);

}

}

return outputList;

}

/**

* 自然习惯对齐

* 比如2018-10-01 17:59:59 ~ 2018-10-07 18:00:00 变成 2018-10-01 18:00:00 ~ 2018-10-07 18:00:00

* 比如2018-10-01 17:00:20 ~ 2018-10-01 18:00:00 变成 2018-10-01 17:00:00 ~ 2018-10-07 18:00:00

*

* @param inputTime

* @param totalInterval

* @return

*/

private static long resetTime(long inputTime, long totalInterval) throws ParseException {

if (totalInterval > 24 * 60 * 60 * 1000l) { //如果间隔在天级别以上,那么分钟级别以下可以四舍五入(1~30舍/31~59入)

if (inputTime % (24 * 60 * 1000l) > 30 * 60 * 1000l) { //大于30分钟的,入

inputTime = inputTime - inputTime % (24 * 60 * 1000l) + 1 * 60 * 60 * 1000l;

} else {//小于等于30分钟的,舍弃

inputTime = inputTime - inputTime % (24 * 60 * 1000l);

}

} else if (totalInterval > 1 * 60 * 60 * 1000l) {//如果间隔在小时级别以上,那么秒钟级别以下可以四舍五入(1~30舍/31~59入)

if (inputTime % (60 * 1000l) > 30 * 1000l) { //大于30秒钟的,入

inputTime = inputTime - inputTime % (60 * 1000l) + 1 * 60 * 1000l;

} else {//小于等于30分钟的,舍弃

inputTime = inputTime - inputTime % (60 * 1000l);

}

} else { //间隔在1个小时以内的,不进行自然习惯对齐

return inputTime;

}

return inputTime;

}

}

单元测试

import org.junit.Assert;

import org.junit.Test;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.List;

/**

*

*/

public class NetworkTimeAlineUtilTest {

private static final String ymdhmsFormat = "yyyy-MM-dd HH:mm:ss";

// date类型转换为String类型

// formatType格式为yyyy-MM-dd HH:mm:ss//yyyy年MM月dd日 HH时mm分ss秒

// data Date类型的时间

public static String dateToString(Date data, String formatType) {

return new SimpleDateFormat(formatType).format(data);

}

// long类型转换为String类型

// currentTime要转换的long类型的时间

// formatType要转换的string类型的时间格式

public static String longToString(long currentTime, String formatType)

throws ParseException {

Date date = longToDate(currentTime, formatType); // long类型转成Date类型

String strTime = dateToString(date, formatType); // date类型转成String

return strTime;

}

// string类型转换为date类型

// strTime要转换的string类型的时间,formatType要转换的格式yyyy-MM-dd HH:mm:ss//yyyy年MM月dd日

// HH时mm分ss秒,

// strTime的时间格式必须要与formatType的时间格式相同

public static Date stringToDate(String strTime, S

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值