2013-10-29 159 views
2

我正試圖解決如果可以在LINQ to Objects語句中執行以下操作。如何使用LINQ獲取日期範圍內的平均值

我有一個關鍵字作爲DateTime(鍵是多天的值)和一個雙值的字典。我有太多的數據要繪製在圖表上,所以想要每個5分鐘的平均值。

採樣輸入

01/01/2012 23:53 5 
01/01/2012 23:54 2 
01/01/2012 23:55 1 
01/01/2012 23:56 3 
01/01/2012 23:57 4 
01/01/2012 23:58 5 
01/01/2012 23:59 6 
02/01/2012 00:00 2 
02/01/2012 00:01 4 
02/01/2012 00:02 5 

期望輸出

01/01/2012 23:55 3 
02/01/2012 00:00 4.4 
+0

你使用的是什麼樣的'LINQ'? –

+0

'作爲日期時間的鍵(跨越多天)'咦?一個'DateTime'如何跨越多天? –

+0

對不起,每個值只是一個日期時間,但是密鑰不是從一天開始的一組時間。 – John

回答

6

使用這種方法helper

static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d) 
{ 
    int f=0; 
    double m = (double)(dt.Ticks % d.Ticks)/d.Ticks; 
    if (m >= 0.5) 
     f=1;    
    return new DateTime(((dt.Ticks/ d.Ticks)+f) * d.Ticks); 
} 

它的那樣簡單

var result = from kvp in data 
      let key = RoundToNearestInterval(kvp.Key, TimeSpan.FromMinutes(5)) 
      group kvp by key into g 
      select new { g.Key, Value = g.Average(x => x.Value) }; 

var result = data.GroupBy(kvp => RoundToNearestInterval(kvp.Key, TimeSpan.FromMinutes(5)), kvp => kvp.Value) 
       .Select(g => new { g.Key, Value = g.Average() }); 

LINQPad例如:

void Main() 
{ 
    var tmp = new Dictionary<string, int> 
    { 
     {"01/01/2012 23:53", 5}, 
     {"01/01/2012 23:54", 2}, 
     {"01/01/2012 23:55", 1}, 
     {"01/01/2012 23:56", 3}, 
     {"01/01/2012 23:57", 4}, 
     {"01/01/2012 23:58", 5}, 
     {"01/01/2012 23:59", 6}, 
     {"02/01/2012 00:00", 2}, 
     {"02/01/2012 00:01", 4}, 
     {"02/01/2012 00:02", 5} 
    }; 
    var data = tmp.ToDictionary(d => DateTime.Parse(d.Key), d=>d.Value); 

    var result = from kvp in data 
       let key = RoundToNearestInterval(kvp.Key, TimeSpan.FromMinutes(5)) 
       group kvp by key into g 
       select new {g.Key, Value = g.Average (x => x.Value) }; 

    result.ToDictionary(r => r.Key, v => v.Value).Dump(); 
} 

enter image description here

0

看起來你dictionary包含ordered元素,所以我們可以做這樣的事情:

var firstDate = yourDict.First().Key; 
var output = yourDict.GroupBy(e=> (int)(e.Key - firstDate).TotalMinutes/5) 
        .ToDictionary(g => g.First().Key 
             .AddMinutes(g.Average(e=>(e.Key - g.First().Key).TotalMinutes)), 
            g => g.Average(e=>e.Value)); 

注意:OP的輸入數據使用不同於en-US的cutlure,該月份在一天之後。這是需要進行一些測試的明顯的一點。否則測試將不正確。

0

這裏的LINQ查詢會做你想要什麼,你可以在LINQPad測試:

void Main() 
{ 
    var points = new[] 
    { 
     new { dt = new DateTime(2012, 1, 1, 23, 53, 00), value = 5 }, 
     new { dt = new DateTime(2012, 1, 1, 23, 54, 00), value = 2 }, 
     new { dt = new DateTime(2012, 1, 1, 23, 55, 00), value = 1 }, 
     new { dt = new DateTime(2012, 1, 1, 23, 56, 00), value = 3 }, 
     new { dt = new DateTime(2012, 1, 1, 23, 57, 00), value = 4 }, 
     new { dt = new DateTime(2012, 1, 1, 23, 58, 00), value = 5 }, 
     new { dt = new DateTime(2012, 1, 1, 23, 59, 00), value = 6 }, 
     new { dt = new DateTime(2012, 1, 2, 00, 00, 00), value = 2 }, 
     new { dt = new DateTime(2012, 1, 2, 00, 01, 00), value = 4 }, 
     new { dt = new DateTime(2012, 1, 2, 00, 01, 00), value = 5 } 
    }; 

    var interval = TimeSpan.FromMinutes(5); 
    var averageByInterval = 
     from point in points 
     let intervalStart = new DateTime(((int)((point.dt.Ticks + interval.Ticks/2)/interval.Ticks)) * interval.Ticks) 
     group point.value by intervalStart into g 
     select new { g.Key, average = g.Average() }; 
    averageByInterval.Dump(); 
} 

輸出:

output

0
var data = new Dictionary<DateTime, double>(); 

    data.Add(new DateTime(2012, 1, 1, 23, 53, 0), 5); 
    data.Add(new DateTime(2012, 1, 1, 23, 54, 0), 2); 
    data.Add(new DateTime(2012, 1, 1, 23, 55, 0), 1); 
    data.Add(new DateTime(2012, 1, 1, 23, 56, 0), 3); 
    data.Add(new DateTime(2012, 1, 1, 23, 57, 0), 4); 
    data.Add(new DateTime(2012, 1, 1, 23, 58, 0), 5); 
    data.Add(new DateTime(2012, 1, 1, 23, 59, 0), 6); 
    data.Add(new DateTime(2012, 1, 2, 0, 0, 0), 2); 
    data.Add(new DateTime(2012, 1, 2, 0, 1, 0), 4); 
    data.Add(new DateTime(2012, 1, 2, 0, 2, 0), 5); 

    var result = data.GroupBy(kvp => 
    { 
    var dt = kvp.Key; 
    var nearest5 = (int)Math.Round(dt.Minute/5.0) * 5; 
    //Add the minutes after inital date creation to deal with minutes=60 
    return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0).AddMinutes(nearest5); 
    }) 
    .Select(g => 
    { 
    return new KeyValuePair<DateTime, double>(g.Key, g.Average(row => row.Value)); 
    }); 

    foreach (var r in result) 
    { 
    Console.WriteLine(r.Key + " " + r.Value); 
    // 1/01/2012 11:55:00 PM 3 
    // 2/01/2012 12:00:00 AM 4.4 

    } 
+0

這將工作,但爲什麼不只是簡化爲'data.GroupBy(DT => Math.Round(新時間跨度(dt.Key.Ticks).TotalMinutes/5.0)* 5) \t \t \t \t \t。選擇(克=> new {Key = new DateTime()。AddMinutes(g.Key),Value = g。Average(row => row.Value)})'? – sloth

+0

無論如何它的工作方式都存在問題,因此無論如何它已被稍許更改 – wdavo

0

試試這個:

var results = 
    data 
     .GroupBy(
      x => (x.Key.Ticks/TimeSpan.TicksPerMinute + 2)/5, 
      x => x.Value) 
     .Select(x => new 
     { 
      Key = new DateTime(x.Key * TimeSpan.TicksPerMinute * 5), 
      Value = x.Average() 
     }); 
+0

與預期輸出不匹配。輸出應該是'{{2012/1/01 23:55,3},{02/01/2012 00:00,4.4}}'。 – sloth

+0

舍入到最近的5分鐘把我扔了。我已經解決了我的答案。 – Enigmativity

相關問題