多变量时间序列数据集整理和对应标准化处理代码合集

简介: # 前言最近在做多变量时间序列异常检测相关的工作,顺带也整理了目前市面上比较常用的五个多变量时间序列异常检测数据集,测试集都有标好的label,这五个数据集应该是在这个领域最为常用benchmark的数据集,整理主要来自于很多顶会的对比实验。本文主要介绍五个数据集的具体信息和对应的标准化处理,并给出处理的代码和最终标准化的格式。tips:作为一个写了四五年博客的职场小白来说,准备从今天开

前言

最近在做多变量时间序列异常检测相关的工作,顺带也整理了目前市面上比较常用的五个多变量时间序列异常检测数据集,测试集都有标好的label,这五个数据集应该是在这个领域最为常用benchmark的数据集,整理主要来自于很多顶会的对比实验。

本文主要介绍五个数据集的具体信息和对应的标准化处理,并给出处理的代码和最终标准化的格式。

tips:作为一个写了四五年博客的职场小白来说,准备从今天开始好好把ATA写下去,多积累,干!

数据集下载

wget https://s3-us-west-2.amazonaws.com/telemanom/data.zip && unzip data.zip && rm data.zip

cd data && wget https://raw.githubusercontent.com/khundman/telemanom/master/labeled_anomalies.csv

数据集详情

  • SMD数据集(Server Machine Dataset)

    • 相关链接:https://github.com/NetManAIOps/OmniAnomaly
    • 出自论文:Robust Anomaly Detection for Multivariate Time Series through Stochastic RNN
    • Netman收集
    • 信息

      • 5周数据(时间粒度是1min,数据中时间index省略)
      • 28个不同的机器对应28个实体,每个实体有38维度的数据(每个维度都是机器的metric)
      • 数据量=57246028=1411200
      • 训练集和测试集=1:1,训练集无label,测试集有label
    • 原始文件说明

      • train: The former half part of the dataset.

        • machine-x-y.txt 其中x代表组,y是组里的index,每一个machine-x-y代表一个具体的机器即实体。
      • test: The latter half part of the dataset.
      • test_label: The label of the test set. It denotes whether a point is an anomaly.
      • interpretation_label: The lists of dimensions contribute to each anomaly. (该数据集给出具体哪几个维度造成了最终的异常)
  • SMAP(Soil Moisture Active Passive satellite)/MSL(Mars Science Laboratory rover)

    • 相关链接:https://github.com/khundman/telemanom
    • 出自论文:A framework for using LSTMs to detect anomalies in multivariate time series data
    • NASA收集
    • 信息

      • 时间信息匿名(时间粒度也是1min),数据已全被scale到0-1
      • SMAP和MSL代表两个不同的产生遥测流的航天器,两个数据集分开
      • SMAP有55个channel(实体),每个channel有25个维度
      • MSL有27个channel(实体),每个channel有55个维度
      • 只有telemetry value是连续变量,其他变量都是相关命令(command)是否发送,实际就是0或者1(0为没发送,而1代表已发送)
      • 训练集无label,测试集有label
    • 原始文件说明

      • labeled_anomalies:数据处理和两个航天器数据分离依靠此文件

        • chan_id: 对应于train和test中对应名字的numpy文件。(对应实体)
        • spacecraft:chan_id所归属的SMAP或MSL
        • anomaly_sequence:对应test中chan_id文件中的index为此范围的为异常序列
        • class:有两种异常,point和contextual,前者点异常,后者是整体变化趋势异常
        • num_values: 对应chan_id的test文件的时间戳数量
      • train
      • test
  • SWat(Secure Water Treatment)

    • https://itrust.sutd.edu.sg/itrust-labs_datasets/dataset_info
    • Singapore University of Technology and Design收集
    • 信息

      • 51 个feature, 来自于 51 个传感器和执行器。
      • 时间信息给出,train(2015.12.22 16:30:00 -> 2015.12.28 10:00:00). test(2015.12.28 10:00:00->2016.1.2 15:00:00).
      • 一共连续11天数据, 7天正常操作下,4天阶段性受到attack,一共41次袭击。
      • 时间粒度为1s
    • normal有v0和v1两个版本,v1是剔除掉无用的前30分钟数据,故采用normalv1和attackv0
    • 原数据中label是normal和attack,统一替换为0和1
  • WADI(Water Distribution)

    • 相关链接:https://itrust.sutd.edu.sg/itrust-labs_datasets/dataset_info/
    • Singapore University of Technology and Design收集
    • 信息

      • 127个feature, 来自123个传感器和执行器的数据
      • 时间信息给出,train(2017.9.25 18:00:00 -> 2017.10.9 18:00:00). test(2017.10.9 18.:00:00->2017.10.11 18:00:00). (第一版本数据信息)(目前用的数据是第二版本,train的不稳定数据已删除,故train的数据时间范围不是一直连续的,测试集时间范围不变)
      • 一共连续16天数据, 14天正常操作下,2天阶段性受到attack,在两天内15次袭击。
      • 时间粒度是1s
      • 有两个版本,由于核电站在运行期间的某些时期不稳定,受影响的读数已被删除,故第二个版本的数据训练集时间是有几个区段被删除掉的,而测试集时间是完全的完整的,且只有第二个版本的数据存在label,故采用第二个版本数据。

        • 新版本中的数据时间格式存在错误,因为两个版本都存在row这行,所以利用row将时间格式替换成第一个版本中的时间数据即可。
    • 原数据是1为 no attack 而-1为attack,为了保证数据的一致性则attack为1 而no attack为0

数据集标准化处理

最终的格式为:

  • datasets

    • processed_datasets(处理后数据格式)

      • dataset1(数据集文件夹名字)

        • entity1 (实例名称,一个实例一个文件夹)

          • train数据集

            • timestamp/datetime
            • fea1(多维度特征)
            • fea2
          • test数据集

            • timestamp/datetime
            • fea1
            • fea2
            • label (0为正常 1为异常)
        • entity2

          • train
          • test
      • dataset2

具体格式

时间统一为datetime格式,很多时间信息匿名的,给予0-len(dataset),label列名统一用label,0为正常,1为异常。

处理代码

MSL SMAP SMD处理代码

import ast
import csv
import os
import sys
from pickle import dump
import pandas as pd

import numpy as np

output_folder = 'processed_csv'
os.makedirs(output_folder, exist_ok=True)


def load_and_save(category, filename, dataset, dataset_folder):
    os.makedirs(os.path.join(output_folder, filename.split('.')[0]), exist_ok=True)
    temp = np.genfromtxt(os.path.join(dataset_folder, category, filename),
                         dtype=np.float32,
                         delimiter=',')
    # print(dataset, category, filename, temp.shape)
    fea_len = len(temp[0, :])
    header_list = []
    for i in range(fea_len):
        header_list.append("col_%d"%i)
    data = pd.DataFrame(temp, columns=header_list).reset_index()
    data.rename(columns={'index': 'timestamp'}, inplace=True)
    if category == "test":
        temp1 = np.genfromtxt(os.path.join(dataset_folder, "test_label", filename),
                         dtype=np.float32,
                         delimiter=',')
        data1 = pd.DataFrame(temp1, columns=["label"]).reset_index()
        data1.rename(columns={'index': 'timestamp'}, inplace=True)
        data = pd.merge(data, data1, how="left", on='timestamp')

    print(dataset, category, filename, temp.shape)
    data.to_csv(os.path.join(output_folder,  filename.split('.')[0], dataset + "_" + category + ".csv"), index=False)

def load_data(dataset):
    if dataset == 'SMD':
        dataset_folder = 'ServerMachineDataset'
        file_list = os.listdir(os.path.join(dataset_folder, "train"))
        for filename in file_list:
            if filename.endswith('.txt'):
                load_and_save('train', filename, filename.strip('.txt'), dataset_folder)
                load_and_save('test', filename, filename.strip('.txt'), dataset_folder)
    elif dataset == 'SMAP' or dataset == 'MSL':
        dataset_folder = 'data'
        with open(os.path.join(dataset_folder, 'labeled_anomalies.csv'), 'r') as file:
            csv_reader = csv.reader(file, delimiter=',')
            res = [row for row in csv_reader][1:]
        res = sorted(res, key=lambda k: k[0])
        label_folder = os.path.join(dataset_folder, 'test_label')
        os.makedirs(label_folder, exist_ok=True)
        data_info = [row for row in res if row[1] == dataset and row[0] != 'P-2']
        labels = []
        for row in data_info:
            anomalies = ast.literal_eval(row[2])
            length = int(row[-1])
            label = np.zeros([length], dtype=np.int)
            for anomaly in anomalies:
                label[anomaly[0]:anomaly[1] + 1] = 1
            labels.extend(label)
        labels = np.asarray(labels)
        print(dataset, 'test_label', labels.shape)

        labels = pd.DataFrame(labels, columns=["label"]).reset_index()
        labels.rename(columns={'index': 'timestamp'}, inplace=True)

        def concatenate_and_save(category):
            data = []
            for row in data_info:
                filename = row[0]
                print(os.path.join(dataset_folder, category, filename + '.npy'))
                temp = np.load(os.path.join(dataset_folder, category, filename + '.npy'))
                data.extend(temp)
            data = np.asarray(data)
            print(dataset, category, data.shape)

            fea_len = len(data[0, :])
            header_list = []
            for i in range(fea_len):
                header_list.append("col_%d" % i)
            data = pd.DataFrame(data, columns=header_list).reset_index()
            data.rename(columns={'index': 'timestamp'}, inplace=True)

            if category == "test":
                data = pd.merge(data, labels, how="left", on='timestamp')
            print(dataset, category, filename, temp.shape)
            data.to_csv(os.path.join(output_folder,  dataset + "_" + category + ".csv"),
                        index=False)

        for c in ['train', 'test']:
            concatenate_and_save(c)


if __name__ == '__main__':
    datasets = ['SMD', 'SMAP', 'MSL']
    load_data('MSL')

改于:https://github.com/NetManAIOps/OmniAnomaly/blob/master/data_preprocess.py

WADI处理

import pandas as pd

train_new = pd.read_csv('./WADI.A2_19 Nov 2019/WADI_14days_new.csv')
test_new = pd.read_csv('./WADI.A2_19 Nov 2019/WADI_attackdataLABLE.csv', skiprows=1)

test = pd.read_csv('./WADI.A1_9 Oct 2017/WADI_attackdata.csv')
train = pd.read_csv('./WADI.A1_9 Oct 2017/WADI_14days.csv', skiprows=4)

def recover_date(str1, str2):
    return str1+" "+str2
train["datetime"] = train.apply(lambda x : recover_date(x['Date'], x['Time']), axis=1)
train["datetime"] = pd.to_datetime(train['datetime'])

train_time = train[['Row', 'datetime']]
train_new_time = pd.merge(train_new, train_time, how='left', on='Row')
del train_new_time['Row']
del train_new_time['Date']
del train_new_time['Time']
train_new_time.to_csv('./processing/WADI_train.csv', index=False)

test["datetime"] = test.apply(lambda x : recover_date(x['Date'], x['Time']), axis=1)
test["datetime"] = pd.to_datetime(test['datetime'])
test = test.loc[-2:, :]
test_new = test_new.rename(columns={'Row ':'Row'})

test_time = test[['Row', 'datetime']]
test_new_time = pd.merge(test_new, test_time, how='left', on='Row')

del test_new_time['Row']
del test_new_time['Date ']
del test_new_time['Time']

test_new_time = test_new_time.rename(columns={'Attack LABLE (1:No Attack, -1:Attack)':'label'})
test_new_time.loc[test_new_time['label'] == 1, 'label'] = 0
test_new_time.loc[test_new_time['label'] == -1, 'label'] = 1

test_new_time.to_csv('./processing/WADI_test.csv', index=False)

SWaT处理

import numpy as np
import pandas as pd

normal = pd.read_csv("input/SWaT_Dataset_Normal_v1.csv")
attack = pd.read_csv("input/SWaT_Dataset_Attack_v0.csv",sep=";")

normal['Timestamp'] = pd.to_datetime(normal['Timestamp'])
del normal['Normal/Attack']

normal = normal.rename(columns={'Timestamp':'datetime'})

datetime = normal['datetime']
del normal['datetime']

for i in list(normal): 
    normal[i]=normal[i].apply(lambda x: str(x).replace("," , "."))
normal = normal.astype(float)
normal['datetime']= datetime

normal.to_csv('SWaT_train.csv', index=False)

attack['Timestamp'] = pd.to_datetime(attack['Timestamp'])
attack = attack.rename(columns={'Timestamp':'datetime'})
datetime = attack['datetime']
del attack['datetime']

labels = [ float(label!= 'Normal' ) for label  in attack["Normal/Attack"].values]
del attack['Normal/Attack']

for i in list(attack):
    attack[i]=attack[i].apply(lambda x: str(x).replace("," , "."))
attack = attack.astype(float)

attack['datetime'] = datetime
attack['label'] = labels

attack.to_csv('SWaT_test.csv', index=False)
目录
相关文章
|
机器学习/深度学习 数据采集 数据处理
掌握时间序列特征工程:常用特征总结与 Feature-engine 的应用
本文介绍了时间序列特征工程,包括滚动统计量、滞后特征、差分和变换等技术,用于提升机器学习模型性能。文章还推荐了Python库`feature-engine`,用于简化特征提取,如处理缺失值、编码分类变量和进行时间序列转换。示例代码展示了如何使用`feature-engine`提取时间戳信息、创建滞后特征和窗口特征。通过创建管道,可以高效地完成整个特征工程流程,优化数据预处理并提高模型效果。
2832 15
|
机器学习/深度学习 数据采集 算法
时间序列结构变化分析:Python实现时间序列变化点检测
在时间序列分析和预测中,准确检测结构变化至关重要。新出现的分布模式往往会导致历史数据失去代表性,进而影响基于这些数据训练的模型的有效性。
2112 1
|
11月前
|
人工智能 IDE 开发工具
CodeGPT AI代码狂潮来袭!个人完全免费使用谷歌Gemini大模型 超越DeepSeek几乎是地表最强
CodeGPT是一款基于AI的编程辅助插件,支持代码生成、优化、错误分析和单元测试,兼容多种大模型如Gemini 2.0和Qwen2.5 Coder。免费开放,适配PyCharm等IDE,助力开发者提升效率,新手友好,老手提效利器。(238字)
CodeGPT AI代码狂潮来袭!个人完全免费使用谷歌Gemini大模型 超越DeepSeek几乎是地表最强
|
IDE Java 程序员
学生邮箱白嫖/免费安装JetBrains全家桶(IDEA/pycharm等) —— 保姆级教程
本文提供了如何使用学生邮箱免费获取并安装JetBrains全家桶(包括IDEA、PyCharm等)的详细教程,涵盖了学生认证、软件下载、安装及常见问题的解决方法。
8431 0
学生邮箱白嫖/免费安装JetBrains全家桶(IDEA/pycharm等) —— 保姆级教程
|
机器学习/深度学习 资源调度 算法
图卷积网络入门:数学基础与架构设计
本文系统地阐述了图卷积网络的架构原理。通过简化数学表述并聚焦于矩阵运算的核心概念,详细解析了GCN的工作机制。
1329 3
图卷积网络入门:数学基础与架构设计
|
人工智能 API 开发者
用Qwen3+MCPs实现AI自动发布小红书笔记!支持图文和视频
魔搭自动发布小红书MCP,是魔搭开发者小伙伴实现的小红书笔记自动发布器,可以通过这个MCP自动完成小红书标题、内容和图片的发布。
4131 41
|
机器学习/深度学习 自然语言处理 PyTorch
深入剖析Transformer架构中的多头注意力机制
多头注意力机制(Multi-Head Attention)是Transformer模型中的核心组件,通过并行运行多个独立的注意力机制,捕捉输入序列中不同子空间的语义关联。每个“头”独立处理Query、Key和Value矩阵,经过缩放点积注意力运算后,所有头的输出被拼接并通过线性层融合,最终生成更全面的表示。多头注意力不仅增强了模型对复杂依赖关系的理解,还在自然语言处理任务如机器翻译和阅读理解中表现出色。通过多头自注意力机制,模型在同一序列内部进行多角度的注意力计算,进一步提升了表达能力和泛化性能。
11297 48
|
机器学习/深度学习 资源调度 自然语言处理
长短时记忆网络(LSTM)完整实战:从理论到PyTorch实战演示
长短时记忆网络(LSTM)完整实战:从理论到PyTorch实战演示
21213 0
|
编译器 Linux C++
本地LaTeX编写环境配置
LaTeX是一种高质量排版系统,适用于学术论文、书籍等文档。本地配置主要基于VS Code,通过安装LaTeX Workshop插件实现一键配置。还可通过Overleaf Workshop插件连接在线平台Overleaf,实现线上线下同步编辑与编译。
3305 1
本地LaTeX编写环境配置
|
人工智能 自然语言处理 PyTorch
InspireMusic:阿里通义实验室开源的音乐生成模型,支持文本或音频生成多种风格的音乐
阿里通义实验室开源的音乐生成技术,支持通过简单描述快速生成多种风格的高质量音乐作品。
3092 4