开发者

Return Modal Average in LINQ (Mode)

I am not sure if CopyMost is the correct term to use here, but it's the term my client used ("CopyMost Data Protocol"). Sounds like he wants the mode? I have a set of data:

Increment    Value
.02            1
.04            1
.06           开发者_高级运维 1
.08            2
.10            2

I need to return which Value occurs the most "CopyMost". In this case, the value is 1. Right now I had planned on writing an Extension Method for IEnumerable to do this for integer values. Is there something built into Linq that already does this easily? Or is it best for me to write an extension method that would look something like this

records.CopyMost(x => x.Value);

EDIT

Looks like I am looking for the modal average. I've provided an updated answer that allows for a tiebreaker condition. It's meant to be used like this, and is generic.

records.CopyMost(x => x.Value, x => x == 0);

In this case x.Value would be an int, and if the the count of 0s was the same as the counts of 1s and 3s, it would tiebreak on 0.


Well, here's one option:

var query = (from item in data
             group 1 by item.Value into g
             orderby g.Count() descending
             select g.Key).First();

Basically we're using GroupBy to group by the value - but all we're interested in for each group is the size of the group and the key (which is the original value). We sort the groups by size, and take the first element (the one with the most elements).

Does that help?


Jon beat me to it, but the term you're looking for is Modal Average.

Edit:

If I'm right In thinking that it's modal average you need then the following should do the trick:

var i = (from t in data
         group t by t.Value into aggr
         orderby aggr.Count() descending
         select aggr.Key).First();


This method has been updated several times in my code over the years. It's become a very important method, and is much different than it use to be. I wanted to provide the most up to date version in case anyone was looking to add CopyMost or a Modal Average as a linq extension.

One thing I did not think I would need was a tiebreaker of some sort. I have now overloaded the method to include a tiebreaker.

public static K CopyMost<T, K>(this IEnumerable<T> records, Func<T, K> propertySelector, Func<K, bool> tieBreaker)
{
    var grouped = records.GroupBy(x => propertySelector(x)).Select(x => new { Group = x, Count = x.Count() });
    var maxCount = grouped.Max(x => x.Count);
    var subGroup = grouped.Where(x => x.Count == maxCount);

    if (subGroup.Count() == 1)
        return subGroup.Single().Group.Key;
    else
        return subGroup.Where(x => tieBreaker(x.Group.Key)).Single().Group.Key;
}

The above assumes the user enters a legitimate tiebreaker condition. You may want to check and see if the tiebreaker returns a valid value, and if not, throw an exception. And here's my normal method.

public static K CopyMost<T, K>(this IEnumerable<T> records, Func<T, K> propertySelector)
{
    return records.GroupBy(x => propertySelector(x)).OrderByDescending(x => x.Count()).Select(x => x.Key).First();
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜