如何找到列表<double>的模式?</double>

时间:2013-10-19 15:07:10

标签: c#

我有一个清单:

List<double> final=new List<double>();
final.Add(1);
final.Add(2);
final.Add(3);

我可以使用哪种方法来查找此列表的模式?此外,如果有两种模式,该函数将返回两者中较小的一种。

4 个答案:

答案 0 :(得分:27)

int? modeValue =
 final
 .GroupBy(x => x)
 .OrderByDescending(x => x.Count()).ThenBy(x => x.Key)
 .Select(x => (int?)x.Key)
 .FirstOrDefault();

所需要的只是一些组合的LINQ操作。您也可以使用查询表达式表达相同的内容。

如果列表为空,modeValue将为null

答案 1 :(得分:5)

usr给出的答案似乎可以解决问题,但如果你想要一些非Linq,请试试这个:

    public int? FindMode(List<int> sample)
    {
        if (sample == null || sample.Count == 0)
        {
            return null;
        }

        List<int> indices = new List<int>();
        sample.Sort();

        //Calculate the Discrete derivative of the sample and record the indices
        //where it is positive.
        for (int i = 0; i < sample.Count; i++)
        {
            int derivative;

            if (i == sample.Count - 1)
            {
                //This ensures that there is a positive derivative for the
                //last item in the sample. Without this, the mode could not
                //also be the largest value in the sample.
                derivative = int.MaxValue - sample[i];
            }
            else
            {
                derivative = sample[i + 1] - sample[i];
            }

            if (derivative > 0)
            {
                indices.Add(i + 1);
            }
        }

        int maxDerivative = 0, maxDerivativeIndex = -1;

        //Calculate the discrete derivative of the indices, recording its
        //maxima and index.
        for (int i = -1; i < indices.Count - 1; i++)
        {
            int derivative;

            if (i == -1)
            {
                derivative = indices[0];
            }
            else
            {
                derivative = indices[i + 1] - indices[i];
            }

            if (derivative > maxDerivative)
            {
                maxDerivative = derivative;
                maxDerivativeIndex = i + 1;
            }
        }

        //The mode is then the value of the sample indexed by the
        //index of the largest derivative.
        return sample[indices[maxDerivativeIndex] - 1];
    }

我在这里所做的基本上是在样本模式部分的Wikipedia page中描述的算法的实现。请注意,首先对样本进行排序,这将在多模式情况下返回较小的模式。

此外,维基百科页面上的Octave代码假定基于1的索引;因为C#是基于0的,所以我会看到我使用indices.Add(i + 1)maxDerivativeIndex = i + 1进行补偿。出于同样的原因,我还使用indices[maxDerivativeIndex] - 1在返回最终模式时映射回基于0的索引。


因为这种方法比使用Dictionary累积计数的直观方法稍微不那么明显,所以这是一个有效的例子。

调用上述方法:

int? mode = FindMode(new List<int>(new int[] { 1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17 }));

在初始检查和排序之后,离散导数(即indices列表)在第一个for循环结束时将如下所示:

[1, 2, 6, 8, 10, 11]

接下来,我们计算indices的{​​{3}}。出于效率原因,我不是将它们存储在一个列表中(毕竟我们只想要它们中最大的一个),但它们可以解决:

[1, 1, 4, 2, 2, 1]

因此,maxDerivative最终为4,maxDerivativeIndex 2.因此:

sample[indices[maxDerivativeIndex] - 1]
    -> sample[indices[2] - 1]
    -> sample[6 - 1]
    -> 6

答案 2 :(得分:1)

另一种解决方案:

var counts = final
    .Distinct()
    .Select(o => new { Value = o, Count = final.Count(c => c == o) })
    .OrderByDescending(o => o.Count);

这将返回一个集合,指示每个值在列表中出现的次数,最常用(平均值)。你可以使用counts.FirstOrDefault();来实现这个目标,但是一个集合可能会更有用,因为你可以看到何时有多个模式!

我发现GroupBy LINQ查询可能有点难以理解,但这是我个人的看法。

答案 3 :(得分:1)

由于其他方法在发生相同次数的次数时不会返回所有数字,因此更正确的替代解决方案

输入:1、2、3、4、5

输出:1,2,3,4,5

输入:1,1,2,2

输出:1,2

输入:1,1,2,4,5 输出1

string getMode()
    {
        IDictionary<float, int> mode = new Dictionary<float, int>();    //Dictionary (Float is the number) (Int is  the occurences)
        foreach (float number in numbers)                               //Loop through List named numbers (List is made of floats)
        {
            if (mode.ContainsKey(number))                               //If dictionary already contains current number increase occurences by 1
            {
                mode[number] ++;
            }
            else
            {
                mode.Add(number, 1);                                    //If dictionary does not contain current number add new occurence
            }
        }

        List<float> currentMax = new List<float>();                     //Create new List of the max number
        int occurences = 0;                                             //Max occurences
        bool foundMultiple = false;                                     //Check if multiple found
        foreach (KeyValuePair<float, int> entry in mode.Reverse())      //Loop through dictionary
        {
            if(occurences < entry.Value)                                //If occurences is smaller than current input
                                                                        //Clear old input and add current number to list
            {
                currentMax.Clear();
                currentMax.Add(entry.Key);
                occurences = entry.Value;
                foundMultiple = false;
            }
            else if(occurences == entry.Value)                          //If number with the same amount of occurences occures
                                                                        //Add to List
            {
                currentMax.Add(entry.Key);
                foundMultiple = true;
            }
        }

        string returnText = "";                                         //Text to return
        if(foundMultiple == true)                                           
        {
           foreach(float number in currentMax)                          //Loop through text
            {
                returnText += number.ToString() + ",";                  //Add to return text
            }
        }
        else
        {
            returnText = currentMax[0].ToString();                         //If there aren't multiple return just first index
        }

        if (returnText.EndsWith(","))
        {
            returnText = returnText.Remove(returnText.Length - 1);      //Format string to avoid a comma at the end
        }
        return returnText;
    }