LINQ按时间段聚合和分组

Jas*_*ges 34 c# linq aggregate-functions

我试图了解如何使用LINQ按时间间隔对数据进行分组; 然后理想地聚合每个组.

查找具有明确日期范围的众多示例,我正在尝试按时段分组,例如5分钟,1小时,1天.

例如,我有一个用Date包装DateTime的类:

public class Sample
{
     public DateTime timestamp;
     public double value;
}
Run Code Online (Sandbox Code Playgroud)

这些观察结果包含在List集合中的一系列中:

List<Sample> series;
Run Code Online (Sandbox Code Playgroud)

因此,按小时分组和按平均值计算总值,我正在尝试执行以下操作:

var grouped = from s in series
              group s by new TimeSpan(1, 0, 0) into g
              select new { timestamp = g.Key, value = g.Average(s => s.value };
Run Code Online (Sandbox Code Playgroud)

这基本上是有缺陷的,因为它将TimeSpan本身分组.我无法理解如何在查询中使用TimeSpan(或表示间隔的任何数据类型).

Bro*_*ass 45

您可以将时间戳舍入到下一个边界(即过去最接近最近的5分钟边界)并将其用作分组:

var groups = series.GroupBy(x =>
{
    var stamp = x.timestamp;
    stamp = stamp.AddMinutes(-(stamp.Minute % 5));
    stamp = stamp.AddMilliseconds(-stamp.Millisecond - 1000 * stamp.Second);
    return stamp;
})
.Select(g => new { TimeStamp = g.Key, Value = g.Average(s => s.value) })
.ToList();
Run Code Online (Sandbox Code Playgroud)

以上通过在分组中使用修改的时间戳来实现,其将分钟设置为前5分钟边界并移除秒和毫秒.当然,相同的方法可以用于其他时间段,即小时和天.

编辑:

基于这个组成的样本输入:

var series = new List<Sample>();
series.Add(new Sample() { timestamp = DateTime.Now.AddMinutes(3) });
series.Add(new Sample() { timestamp = DateTime.Now.AddMinutes(4) });
series.Add(new Sample() { timestamp = DateTime.Now.AddMinutes(5) });
series.Add(new Sample() { timestamp = DateTime.Now.AddMinutes(6) });
series.Add(new Sample() { timestamp = DateTime.Now.AddMinutes(7) });
series.Add(new Sample() { timestamp = DateTime.Now.AddMinutes(15) });
Run Code Online (Sandbox Code Playgroud)

我为我制作了3组,其中一组分组时间戳为3:05,一组为3:10,另一组为下午3:20(您的结果可能会根据当前时间而有所不同).

  • 谢谢(+1),但是发现了一个问题,我发现我的日期时间具有相同的年、月、分、秒和毫秒,但有不同的刻度。注意它们来自 EF 从数据库中提取的 DateTimeOffsets。由于这个(亚毫秒差异),我建议使用 new DateTime() 来确保您的 DateTimes 确实被认为是相等的。见 http://stackoverflow.com/a/27234349/661584 (2认同)

Hen*_*man 11

您需要一个对时间戳进行舍入的函数.就像是:

 var grouped = from s in series
          group s by new DateTime(s.timestamp.Year, s.timestamp.Month,  
                s.timestamp.Day, s.timestamp.Hour, 0, 0) into g
          select new { timestamp = g.Key, value = g.Average(s => s.value };
Run Code Online (Sandbox Code Playgroud)

适用于每小时的垃圾箱.请注意,结果中的时间戳现在将是DateTime,而不是TimeSpan.


Dua*_*ney 6

我在这场比赛上的比赛已经很晚了,但是我在寻找其他东西的时候遇到了这个问题,我觉得我有更好的方法.

series.GroupBy (s => s.timestamp.Ticks / TimeSpan.FromHours(1).Ticks)
        .Select (s => new {
            series = s
            ,timestamp = s.First ().timestamp
            ,average = s.Average (x => x.value )
        }).Dump();
Run Code Online (Sandbox Code Playgroud)

这是一个linqpad程序示例,您可以进行验证和测试

void Main()
{
    List<Sample> series = new List<Sample>();

    Random random = new Random(DateTime.Now.Millisecond);
    for (DateTime i = DateTime.Now.AddDays(-5); i < DateTime.Now; i += TimeSpan.FromMinutes(1))
    {
        series.Add(new UserQuery.Sample(){ timestamp = i, value = random.NextDouble() * 100 });
    }
    //series.Dump();
    series.GroupBy (s => s.timestamp.Ticks / TimeSpan.FromHours(1).Ticks)
        .Select (s => new {
            series = s
            ,timestamp = s.First ().timestamp
            ,average = s.Average (x => x.value )
        }).Dump();
}

// Define other methods and classes here
public class Sample
{
     public DateTime timestamp;
     public double value;
}
Run Code Online (Sandbox Code Playgroud)