如何找到列表<双精度>的模式
本文关键字:模式 双精度 何找 列表 | 更新日期: 2023-09-27 18:36:14
>我有一个列表:
List<double> final=new List<double>();
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<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];
}
我在这里所做的本质上是维基百科页面上示例模式部分中描述的算法的实现。请注意,通过首先对样本进行排序,这将返回多模式情况下的较小模式。
此外,维基百科页面上的 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
的离散导数。出于效率原因,我没有将它们存储在列表中(毕竟,我们只想要其中最大的一个),但它们的结果是:
[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<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;
}