将中位数方法添加到列表中

时间:2011-03-11 15:49:52

标签: c# list median

我想覆盖C#中的List对象,以便添加像Sum或Average这样的中位数方法。我已经找到了这个功能:

public static decimal GetMedian(int[] array)
{
    int[] tempArray = array;
    int count = tempArray.Length;

    Array.Sort(tempArray);

    decimal medianValue = 0;

    if (count % 2 == 0)
    {
        // count is even, need to get the middle two elements, add them together, then divide by 2
        int middleElement1 = tempArray[(count / 2) - 1];
        int middleElement2 = tempArray[(count / 2)];
        medianValue = (middleElement1 + middleElement2) / 2;
    }
    else
    {
        // count is odd, simply get the middle element.
        medianValue = tempArray[(count / 2)];
    }

    return medianValue;
}

你能告诉我怎么做吗?

8 个答案:

答案 0 :(得分:21)

使用扩展方法,并复制输入的数组/列表。

public static decimal GetMedian(this IEnumerable<int> source)
{
    // Create a copy of the input, and sort the copy
    int[] temp = source.ToArray();    
    Array.Sort(temp);

    int count = temp.Length;
    if (count == 0)
    {
        throw new InvalidOperationException("Empty collection");
    }
    else if (count % 2 == 0)
    {
        // count is even, average two middle elements
        int a = temp[count / 2 - 1];
        int b = temp[count / 2];
        return (a + b) / 2m;
    }
    else
    {
        // count is odd, return the middle element
        return temp[count / 2];
    }
}

答案 1 :(得分:16)

不要使用该功能。它存在严重缺陷。看看这个:

int[] tempArray = array;     
Array.Sort(tempArray); 

数组是C#中的引用类型这会对您提供的数组进行排序,而不是副本。获取数组的中位数不应更改其顺序;它可能已经按照不同的顺序排序。

使用Array.Copy首先制作数组的副本,然后然后副本进行排序。

答案 2 :(得分:6)

我肯定会制作那些Extension Methods

public static class EnumerableExtensions
{
    public static decimal Median(this IEnumerable<int> list)
    {
        // Implementation goes here.
    }

    public static int Sum(this IEnumerable<int> list)
    {
        // While you could implement this, you could also use Enumerable.Sum()
    }
}

然后您可以通过以下方式使用这些方法:

List<int> values = new List<int>{ 1, 2, 3, 4, 5 };
var median = values.Median();

<强>更新

哦......正如Eric提到的,你应该找到另一个中位数的实现。你提供的那个不仅修改了原始数组,但是,如果我正确读取它,也会返回一个整数而不是预期的小数。

答案 3 :(得分:2)

您可能不想使用排序来查找中位数,因为有更有效的方法来计算它。您可以在我的以下答案中找到此代码,其中还添加了Median作为IList<T>的扩展方法:

Calculate median in c#

答案 4 :(得分:0)

您可以为要支持的集合类型创建扩展方法。然后你就可以调用它,就好像它是该类的一部分一样。

MSDN - Extension Methods documentation and examples

答案 5 :(得分:0)

平均值和总和是可用于任何IEnumerable的扩展方法,提供正确的转换函数作为参数MSDN

decimal Median<TSource>(this IEnumerable<TSource> collection, Func<TSource,decimal> transform)
{
   var array = collection.Select(x=>transform(x)).ToArray();
   [...]
   return median;
}

transform将采用一个集合项并将其转换为十进制(averagable and comparative)

我不会在这里详细介绍Median方法的实现,但它并不复杂。

编辑:我看到你添加了输出小数平均值的进一步要求。

PS:参数检查是为了清除。

答案 6 :(得分:0)

我会对你的方法做一些修改:

替换这个:

     int[] tempArray = array; 

使用:

     int[] tempArray = (int[])array.Clone();

答案 7 :(得分:0)

我创建了自己的解决方案我在SQL服务器中有大表,而.ToList()和.ToArray()不能正常工作(你从数据库bofore中取出所有其他行,我需要的只是长度记录,中间1或2行(奇数或偶数) 如果有兴趣我有一个带有Expression的版本,如果是十进制,则返回TResult

   public static decimal MedianBy<T, TResult>(this IQueryable<T> sequence, Expression<Func<T, TResult>> getValue)
{
    var count = sequence.Count();
    //Use Expression bodied fuction otherwise it won't be translated to SQL query
    var list = sequence.OrderByDescending(getValue).Select(getValue);
    var mid = count / 2;
    if (mid == 0)
    {
        throw new InvalidOperationException("Empty collection");
    }
    if (count % 2 == 0)
    {
        var elem1 = list.Skip(mid - 1).FirstOrDefault();
        var elem2 = list.Skip(mid).FirstOrDefault();

        return (Convert.ToDecimal(elem1) + Convert.ToDecimal(elem2)) / 2M;
        //TODO: search for a way to devide 2 for different types (int, double, decimal, float etc) till then convert to decimal to include all posibilites
    }
    else
    {
        return Convert.ToDecimal(list.Skip(mid).FirstOrDefault());
        //ElementAt Doesn't work with SQL
        //return list.ElementAt(mid);
    }
}