У меня есть список:
List<double> final=new List<double>();
final.Add(1);
final.Add(2);
final.Add(3);
Какой метод я могу использовать для поиска режима этого списка? Кроме того, если есть два режима, функция вернет меньшую из двух.
У меня есть список:
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];
}
То, что я здесь сделал, по существу является реализацией алгоритма, описанного на странице Wikipedia в разделе "Режим образца". Обратите внимание, что, сначала отсортировав образец, это вернет меньший режим в многорежимном случае.
Кроме того, код Octave на странице Wikipedia предполагает индексирование на основе 1; потому что С# на основе 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
) будет выглядеть так в конце этого первого цикла:
[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 могут быть немного сложными для понимания, но это мое личное мнение.