Question

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
?
Was it helpful?

Solution

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

OTHER TIPS

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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top