我有一个清单:
List<double> final=new List<double>();
final.Add(1);
final.Add(2);
final.Add(3);
我可以使用哪种方法来查找此列表的模式?此外,如果有两种模式,该函数将返回两者中较小的一种。
答案 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;
}