如何使用 Linq 按日期时间和平均结果对字典进行分组

Posted

技术标签:

【中文标题】如何使用 Linq 按日期时间和平均结果对字典进行分组【英文标题】:How to group a Dictionary by DateTime and average results using Linq 【发布时间】:2021-02-11 20:18:23 【问题描述】:

我有这个数据集。

var results = new Dictionary<DateTime, Result>()

Result 类如下所示:

public class Result

   public decimal Temperature  get; set 
   public decimal DayTime  get; set; 

我的数据集将如下所示:

Dictionary Key     Dictionary Value (Result class)
--------------     -------------------------------
01/01/2020 09:00    Temperature: +9, DayTime: true 
01/01/2020 18:00    Temperature: +5, DayTime: false
01/02/2020 09:00    Temperature: +8, DayTime: true 
01/02/2020 18:00    Temperature: +3, DayTime: false 

我的目标是平均每月的昼夜温度,这将给出如下结果:

01/2020 - DayTime   Avg. Temp +9
01/2020 - NightTime Avg. Temp +5
02/2020 - DayTime   Avg. Temp +8
02/2020 - NightTime Avg. Temp + 3

请注意,会有更多数据点,因为我将拥有给定年份每天每一小时的数据。

我已经开始在 Dictionary 类型上使用 GroupBy() 方法,但我不知道如何深入每个结果以按日/夜分开并平均温度。

这可能与 Linq 相关,还是我应该只循环结果,使用 linq 提取月度数据并取平均值?我可以得到一个不同的月份列表,然后循环选择日/夜值并执行平均值?

如果有更简洁的方法可以做到这一点,我很乐意学习。

【问题讨论】:

你试过OrderBy的方法吗? @RamonDias : OrderBy 如何解决平均问题? ResultDayTime 属性不应该是bool吗? 【参考方案1】:

您只需要嵌套GroupBys 来处理每个分组:一个用于日期,一个用于DayTime。我使用SelectMany 来展平子分组,但您可以保留它们。

var ans = results.GroupBy(dr => dr.Key.Date, dr => dr.Value)
                 .SelectMany(rg => rg.GroupBy(r => r.DayTime, r => r.Temperature).Select(tg => new  Date = rg.Key, DayTime = tg.Key, AvgTemp = tg.Average() ))
                 .ToList();

【讨论】:

【参考方案2】:

在一个 linq 中完成这项工作具有挑战性。您需要两个不均匀的 9 小时和 15 小时分组。因此,您按天分组,然后减去 9 小时以开始上午 9:00,然后测试偏移量是否小于 9 小时(上午 9:00 到下午 6:00)或更大。我不得不让温度接受一个空值,因为一个范围可能没有温度。 请参阅下面的解决方案

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1


    class Program
    
        static void Main(string[] args)
        
            TimeSpan SIX_PM = new TimeSpan(18, 0, 0); // Time from midnight to 6PM;

            List<Result> input = new List<Result>() 
                new Result()  DayTime = DateTime.Parse("10/28/20 2:00"), Temperature = 2,
                new Result()  DayTime = DateTime.Parse("10/28/20 4:00"), Temperature = 3,
                new Result()  DayTime = DateTime.Parse("10/28/20 6:00"), Temperature = 4,
                new Result()  DayTime = DateTime.Parse("10/28/20 8:00"), Temperature = 5,
                new Result()  DayTime = DateTime.Parse("10/28/20 10:00"), Temperature = 6,
                new Result()  DayTime = DateTime.Parse("10/28/20 12:00"), Temperature = 7,
                new Result()  DayTime = DateTime.Parse("10/28/20 14:00"), Temperature = 8,
                new Result()  DayTime = DateTime.Parse("10/28/20 16:00"), Temperature = 9,
                new Result()  DayTime = DateTime.Parse("10/28/20 18:00"), Temperature = 10,
                new Result()  DayTime = DateTime.Parse("10/28/20 20:00"), Temperature = 11,
                new Result()  DayTime = DateTime.Parse("10/28/20 22:00"), Temperature = 12,
                new Result()  DayTime = DateTime.Parse("10/29/20 0:00"), Temperature = 13,
                new Result()  DayTime = DateTime.Parse("10/29/20 2:00"), Temperature = 14,
                new Result()  DayTime = DateTime.Parse("10/29/20 4:00"), Temperature = 15,
                new Result()  DayTime = DateTime.Parse("10/29/20 6:00"), Temperature = 16,
                new Result()  DayTime = DateTime.Parse("10/29/20 8:00"), Temperature = 17,
                new Result()  DayTime = DateTime.Parse("10/29/20 10:00"), Temperature = 18,
                new Result()  DayTime = DateTime.Parse("10/29/20 12:00"), Temperature = 19,
                new Result()  DayTime = DateTime.Parse("10/29/20 14:00"), Temperature = 20,
                new Result()  DayTime = DateTime.Parse("10/29/20 16:00"), Temperature = 21,
                new Result()  DayTime = DateTime.Parse("10/29/20 18:00"), Temperature = 22,
                new Result()  DayTime = DateTime.Parse("10/29/20 20:00"), Temperature = 23,
                new Result()  DayTime = DateTime.Parse("10/29/20 22:00"), Temperature = 24
            ;




            Dictionary<DateTime, Result> results = input.GroupBy(x => x.DayTime, y => y)
                .ToDictionary(x => x.Key, y => y.FirstOrDefault());

            var data = results.GroupBy(x => x.Key.AddHours(-9).Date)
                .SelectMany(x => Enumerable.Range(0, 2)
                .Select(y => (y == 0) 
                    ? new
                        
                            Date = x.Key.AddHours(9).ToString(),
                            AverageTemperature = x.Where(z => z.Key - x.Key  < SIX_PM)
                                .Average(z => z.Value.Temperature),
                            DayTime = "DayTime"
                        
                    : new
                        
                            Date = x.Key.AddHours(18).ToString(),
                            AverageTemperature = x.Where(z => z.Key - x.Key  >= SIX_PM)
                                .Average(z => z.Value.Temperature),
                            DayTime = "NightTime"
                        
            ))
            .Where(x => x.AverageTemperature != null)
            .ToList();


    
        
        public class Result
        
            public decimal? Temperature  get; set; 
            public DateTime DayTime  get; set; 
        
    

【讨论】:

以上是关于如何使用 Linq 按日期时间和平均结果对字典进行分组的主要内容,如果未能解决你的问题,请参考以下文章

如何按日期对字典数组进行排序?

按日期对 PHAsset 获取结果进行分组的智能方法

LINQ 按时间段聚合和分组

使用 Linq 按名称频率对列表进行排序

在 C# 中按名称和日期作为键对字典进行排序? [复制]

在python中按日期时间对字典列表进行排序