代码之家  ›  专栏  ›  技术社区  ›  user3247900

在时间序列中查找开始和停止

  •  2
  • user3247900  · 技术社区  · 6 年前

    我想了解是否有一种方法可以使用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中添加Where条件(如果可能的话),以查找特定值并尊重时间条件以始终查找下一个可用值。

    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 回复  |  直到 6 年前
        1
  •  1
  •   Anders Carstensen    6 年前

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

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

    1. 按标记对条目分组。
    2. 每组:
      1. 从列表开头删除值为0的所有条目。
      2. 如果有两个或多个具有相同值的相邻条目,则只保留第一个条目。
        • 现在我们有一个列表,从1开始,在1和0之间交替。
      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
    
        2
  •  0
  •   JohnyL    6 年前

    基于 安德森·卡斯滕森 的类和种子数据:

    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
    */
    
        3
  •  0
  •   jdweng    6 年前

    我是作为一个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);
            }
    
        }
    }