如何找到列表的模式?

我有一个清单:

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

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

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

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

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

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

  public int? FindMode(List sample) { if (sample == null || sample.Count == 0) { return null; } List indices = new List(); 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]; } 

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

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


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

调用上面的方法:

 int? mode = FindMode(new List(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,而maxDerivativeIndex 2.因此:

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

另一种解决方案:

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

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

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

另一种解决方案更正确,因为其他方法如果出现相同的次数则不返回所有数字

输入: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 mode = new Dictionary(); //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 currentMax = new List(); //Create new List of the max number int occurences = 0; //Max occurences bool foundMultiple = false; //Check if multiple found foreach (KeyValuePair 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; }