13

我有一个清单:

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

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

4

4 回答 4

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

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

如果列表为空,modeValue将为null.

于 2013-10-19T15:11:08.287 回答
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 页面上Mode of a sample部分中描述的算法的实现。请注意,通过首先对样本进行排序,这将在多模式情况下返回较小的模式。

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


因为这种方法比使用 a 来累积计数的直观方法不太明显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。出于效率原因,我没有将它们存储在列表中(毕竟我们只想要其中最大的一个),但它们的结果是:

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

因此,maxDerivative最终是 4 和maxDerivativeIndex2。因此:

sample[indices[maxDerivativeIndex] - 1]
    -> sample[indices[2] - 1]
    -> sample[6 - 1]
    -> 6
于 2014-01-15T07:08:49.337 回答
1

另一种解决方案:

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

这将返回一个集合,指示每个值在列表中出现的次数,最流行的(均值)在前。您可以使用 来完成此操作counts.FirstOrDefault();,但集合可能更有用,因为您将能够看到何时有多个模式!

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

于 2014-01-17T12:19:23.573 回答
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;
    }
于 2018-10-31T08:42:06.543 回答