1

Let's say I have a List<PageHit>, with PageHit having these properties: DateTime RequestTime and int PageResponseTime.

Ultimately what I'm trying to do is to round each RequestTime value to the nearest 30 minute increment (using How can I round up the time to the nearest X minutes?), group them together, and get the overall average of PageResponseTime for that increment block (real world user story: track the average page response time per 30 minute block).

This is as far as I've gotten, but my brain won't immediately show me how to efficiently get the average for each increment without some gross loop. Is there a way to do this in Step 1?

// Step 1: round all request times up
pageHitList.ForEach(x => x.RequestTime = x.RequestTime.RoundUp(TimeSpan.FromMinutes(30));

// Step 2: Get the average of each increment block
?
Community
  • 1
  • 1
Matt
  • 1,897
  • 4
  • 28
  • 49
  • Given that `ForEach` has a `void` return type, what is the type of `hits`? – Jon Skeet Apr 25 '14 at 16:57
  • Fixed, that assignment was incorrect. – Matt Apr 25 '14 at 16:58
  • So did you not actually want to modify the existing data? It's worth being very clear about whether when you do rounding it's *just* for the sake of something else (the grouping in this case) or whether you want to modify your actual data for other future operations. – Jon Skeet Apr 26 '14 at 07:30

3 Answers3

5

Haven't tested it, but this is what I would do:

var avg = pageHitList.GroupBy(x => x.RequestTime.RoundUp(TimeSpan.FromMinutes(30)));
                     .Select(hit => new { 
                                 hit.Key, 
                                 Average = hit.Average(x => x.PageResponseTime) 
                             });
Josh Wyant
  • 1,177
  • 1
  • 8
  • 13
  • 1
    Thanks Josh, your edit corrected an initial mistake and after testing it, it's perfect. You also correctly included the RoundUp method. Thank you! – Matt Apr 25 '14 at 17:19
0

Firstly, I'd add a method to the PageHit class (or as an extension method) to calculate the rounded response time

    public class PageHit
    {
        public DateTime RequestTime { get; set; }
        public int PageResponseTime { get; set; }

        public DateTime GetRequestTimeToNearest30Mins()
        {
            return RoundUp(RequestTime, TimeSpan.FromMinutes(30));
        }
    }

Then you can do something like the following

    public void GetAverageRequestTimeByPeriod()
    {
        // Firstly project out both the PageHit and the rounded request time
        var averages = _pageHits.Select(t => new { RoundedTime = t.GetRequestTimeToNearest30Mins(), PageHit = t})
                                // Then Group them all by the rounded time, forming the blocks you mention
                                .GroupBy(t => t.RoundedTime)
                                // Project out the block time and the average of each page hit response time in the block
                                .Select(g => new { RequestTimeBlock = g.Key, AverageResponseTime = g.Average(t => t.PageHit.PageResponseTime)})
                                .ToArray();
    }

Obviously you'd want to do something with the resulting averages, but I'll leave that part to you

Richard
  • 1,602
  • 1
  • 15
  • 16
0

Something like this should work...

        var averageByBlock = (from hit in pageHitList
                             group hit by hit.RequestTime into g
                             let groupCount = g.Count()
                             select new Tuple<DateTime, double>(g.Key, ((groupCount > 0) ? (g.Aggregate(0.0, (sum, ph) => sum + ph.PageResponseTime) / groupCount) : 0.0)));

I am not sure if this is the most optimized but I ran this code on a list of 100K entries and it ran pretty fast.

pvenky
  • 182
  • 1
  • 11