首页 > 解决方案 > 在时间序列中查找开始和停止

问题描述

我想了解是否有办法使用 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 条件(如果可能的话)以查找特定值并尊重 a总是找到下一个可用值的时间条件。

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();

标签: c#.netlinq

解决方案


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

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

  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

推荐阅读