查找时间序列中的开始和结束

时间:2018-12-24 11:46:36

标签: c# .net linq

我想了解是否可以使用linq exp查找给定时间序列的所有开始和结束窗口(由开始和结束值界定)。 我知道可以通过常规循环来实现,但是我希望尽可能地扩大自己的知识。

数据存储在“ TagData”(List<TagData>)的有序列表中

class TagData
{
    public DateTime Timestamp { get; set; }
    public string Tag { get; set; }
    public double Value { get; set; }
}

最简单的方法是将数据内容完美地交替显示,例如:

timestamp               | tag       | value
2018-12-01 00:10:00.000 | extrg_01  | 1
2018-12-01 00:15:02.000 | extrg_01  | 0
2018-12-01 00:25:50.000 | extrg_01  | 1
2018-12-01 00:45:11.000 | extrg_01  | 0

此时,给定初始值= 1,最终值= 0,结果将是以下几行:

timestamp_start         |timestamp_end              | tag       | nrOfSeconds
2018-12-01 00:10:00.000 |2018-12-01 00:15:02.000    | extrg_01  | 302
2018-12-01 00:25:50.000 |2018-12-01 00:45:01.000    | extrg_01  | 1161

但是也可能存在一些不需要考虑的“脏”数据:

timestamp               | tag       | value
2018-12-01 00:10:00.000 | extrg_01  | 1
2018-12-01 00:12:02.000 | extrg_01  | 1
2018-12-01 00:15:02.000 | extrg_01  | 0
2018-12-01 00:16:01.000 | extrg_01  | 0
2018-12-01 00:25:50.000 | extrg_01  | 1
2018-12-01 00:45:11.000 | extrg_01  | 0

在这种情况下,最终结果应与第一个示例相同,因为将不考虑定义的初始值(在这种情况下为= 1)之后的所有值,并且同样,仅将第一个最终值(在这种情况下为= 0)进行计算。

我忘记添加我要编辑的linq表达式以实现结果: 不幸的是,我无法理解如何在此.Zip中添加条件(如果可能的话)以查找特定值并遵守时间条件以始终查找下一个可用值。

var diffs = tagDataList.Skip(1)
            .Zip(tagDataList,
                (curr, prev) => new
                {
                    CurrTag = curr.Tag,
                    CurrValue = curr.Value,
                    CurrDate = curr.Timestamp,
                    PrevDate = prev.Timestamp,
                    DiffToPrev = Math.Abs((curr.Timestamp - prev.Timestamp).TotalSeconds)
                })
            .ToList();

3 个答案:

答案 0 :(得分:1)

可能有很多方法可以做到这一点。我会试一试:

我假设您希望单独处理不同的标签。这是我的方法:

  1. 按标签对条目进行分组。
  2. 对于每个组:
    1. 从列表的开头删除所有值为0的条目。
    2. 如果有两个或两个以上具有相同值的相邻条目,则仅保留第一个。
      • 现在我们有了一个列表,其值从1开始,在1s和0s之间交替。
    3. 将1和0压缩在一起以计算时间跨度。
  3. 最后将每个组的所有结果展平

我使用了以下测试数据:

var list = new List<TagData> {
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:09:00.000"), Tag = "extrg_01", Value = 0 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:10:00.000"), Tag = "extrg_01", Value = 1 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:10:00.000"), Tag = "extrg_02", Value = 1 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:12:02.000"), Tag = "extrg_01", Value = 1 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:15:02.000"), Tag = "extrg_01", Value = 0 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:16:01.000"), Tag = "extrg_01", Value = 0 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:15:02.000"), Tag = "extrg_02", Value = 0 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:25:50.000"), Tag = "extrg_01", Value = 1 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:45:11.000"), Tag = "extrg_01", Value = 0 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:25:50.000"), Tag = "extrg_02", Value = 1 },
    new TagData { Timestamp = DateTime.Parse("2018-12-01 00:45:11.000"), Tag = "extrg_02", Value = 0 },
};

类定义:

class TagData
{
    public DateTime Timestamp { get; set; }
    public string Tag { get; set; }
    public double Value { get; set; }
}

class TagSummary
{
    public DateTime TimestampStart { get; set; }
    public DateTime TimestampEnd { get; set; }
    public string Tag { get; set; }
    public TimeSpan TimeSpan => TimestampEnd - TimestampStart;
}

代码:

var summaries =
    list.GroupBy(tagdata => tagdata.Tag) // Step (1)
    .Select(group => // Step (2)
    {
        var data = group
            .SkipWhile(tagdata => tagdata.Value == 0) // Step (2.1)
            .Aggregate(new List<TagData>(), (acc, tagdata) => // Step (2.2)
            {
              if (acc.LastOrDefault()?.Value != tagdata.Value)
                  acc.Add(tagdata);
              return acc;
            });

        var ones = data.Where(datatag => datatag.Value == 1);
        var zeros = data.Where(datatag => datatag.Value == 0);
        var result = ones.Zip(zeros, (startTag, endTag) => { // Step (2.3)
            return new TagSummary { TimestampStart = startTag.Timestamp, TimestampEnd = endTag.Timestamp, Tag = startTag.Tag };
        });

        return result;
    })
    .SelectMany(x => x); // Step (3)

Console.WriteLine("timestamp_start     | timestamp_end       | tag      | nrOfSeconds");
foreach (var summary in summaries)
    Console.WriteLine($"{summary.TimestampStart:yyyy-MM-dd HH:mm:ss} | {summary.TimestampEnd:yyyy-MM-dd HH:mm:ss} | {summary.Tag,-8} | {summary.TimeSpan.TotalSeconds:0}");

输出与您指定的相同:

timestamp_start     | timestamp_end       | tag      | nrOfSeconds
2018-12-01 00:10:00 | 2018-12-01 00:15:02 | extrg_01 | 302
2018-12-01 00:25:50 | 2018-12-01 00:45:11 | extrg_01 | 1161
2018-12-01 00:10:00 | 2018-12-01 00:15:02 | extrg_02 | 302
2018-12-01 00:25:50 | 2018-12-01 00:45:11 | extrg_02 | 1161

答案 1 :(得分:0)

我作为IEnumerator进行了操作,因此可以使用foreach。我添加了一个属性,以便代码返回开始时间和结束时间。

sing System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace ConsoleApplication94
{
    class Program
    {
        static void Main(string[] args)
        {
            List<TagData> list = new List<TagData>() {
                new TagData() { Timestamp = DateTime.Parse("2018-12-01 00:10:00.000"), Tag = "extrg_01", Value = 1},
                new TagData() { Timestamp = DateTime.Parse("2018-12-01 00:12:02.000"), Tag = "extrg_01", Value = 1},
                new TagData() { Timestamp = DateTime.Parse("2018-12-01 00:15:02.000"), Tag = "extrg_01", Value = 0},
                new TagData() { Timestamp = DateTime.Parse("2018-12-01 00:16:01.000"), Tag = "extrg_01", Value = 0},
                new TagData() { Timestamp = DateTime.Parse("2018-12-01 00:25:50.000"), Tag = "extrg_01", Value = 1},
                new TagData() { Timestamp = DateTime.Parse("2018-12-01 00:45:11.000"), Tag = "extrg_01", Value = 0}
            };

            TagData data = new TagData(list);

            foreach(TagData tagData in data)
            {
                DateTime start = tagData.Timestamp;
                DateTime end = tagData.Endstamp;
                double seconds = tagData.numberOfSeconds;
            }

        }
    }
    class TagData : IEnumerator<TagData>
    {
        public DateTime Timestamp { get; set; }
        public string Tag { get; set; }
        public double Value { get; set; }
        public DateTime Endstamp { get; set; }
        public double numberOfSeconds;

        private List<TagData> listData;
        private int curIndex;
        private int endIndex;
        private TagData curTagData;

        public TagData() { }
        public TagData(List<TagData> collection)
        {
            listData = collection;
            curIndex = -1;
            endIndex = -1;
            curTagData = default(TagData);

        }

        public bool MoveNext()
        {
            if (curIndex != -1) curIndex = endIndex;
            while ((++curIndex < listData.Count()) && (listData[curIndex].Value != 1)) { }
            if (curIndex < listData.Count())
            {
                endIndex = curIndex;
                while ((endIndex < listData.Count()) && (listData[endIndex].Value != 0)) endIndex++;
                if (endIndex >= listData.Count()) return false;
                listData[curIndex].Endstamp = listData[endIndex].Timestamp;
                listData[curIndex].numberOfSeconds = (listData[curIndex].Endstamp - listData[curIndex].Timestamp).TotalSeconds;
            }
            else
            {
                return false;
            }
            curTagData = listData[curIndex];
            return true;
        }

        public void Reset() { curIndex = -1; endIndex = -1; }

        void IDisposable.Dispose() { }

        public TagData Current
        {
            get { return curTagData; }
        }


        object IEnumerator.Current
        {
            get { return Current; }
        }

        public TagData  GetEnumerator()
        {
            return new TagData(listData);
        }

    }
}

答案 2 :(得分:0)

基于 AndersCarstensen 的类和种子数据:

var z = list
        .Where((t, index) => t.Value == 1 && list[index - 1].Value != 1)
        .Select(t =>
            new TagSummary
            {
                TimestampStart = t.Timestamp,
                TimestampEnd = list.First(x => x.Timestamp > t.Timestamp && x.Value == 0).Timestamp,
                Tag = t.Tag
            }
        );
/*
OUTPUT:
Start: 01.12.2018 0:10:00, End: 01.12.2018 0:15:02, Diff: 00:05:02, Tag: extrg_01
Start: 01.12.2018 0:25:50, End: 01.12.2018 0:45:11, Diff: 00:19:21, Tag: extrg_01
Start: 01.12.2018 0:25:50, End: 01.12.2018 0:45:11, Diff: 00:19:21, Tag: extrg_02
*/