How do I find the mode (most frequent element) of an array?
Here's a LINQ solution:
int mode = myArray
.GroupBy(x => x)
.OrderByDescending(g => g.Count())
.First() // throws InvalidOperationException if myArray is empty
.Key;
This groups the elements of myArray
by value, sorts the groups by the number of values in each group, and takes the value of the first group. If there is more than one mode, then this takes whichever one occurs first (has the lowest index) in myArray
.
If there might be more than one mode and you need all of them, then you can use this variation instead:
var groups = myArray
.GroupBy(x => x)
.Select(g => new { Value = g.Key, Count = g.Count() })
.ToList(); // materialize the query to avoid evaluating it twice below
int maxCount = groups.Max(g => g.Count); // throws InvalidOperationException if myArray is empty
IEnumerable<int> modes = groups
.Where(g => g.Count == maxCount)
.Select(g => g.Value);
This groups the elements of myArray
by value, finds the maximum number of values in any group, and takes the value of every group having that maximum number of values.
If myArray
is very large, then the second version (which is O(n)) might be faster than the first version (which is O(n log n) due to the sort).
C# Finding the Mode
user DesertFox mentioned properly.
You are trying to print a list. Intead of that, as per your requirement, make it as a single string
using String.Join
.
Modify your test method like below
static void Test()
{
try
{
int[] values = { 1, 6, 4, 7, 9, 2, 5, 7, 2, 6, 5, 7, 8, 1, 3, 8, 2 };
List<int> mode = new List<int>();
mode = Mode(values);
Console.WriteLine("Mode: {0}", String.Join(", ", mode));
Console.ReadLine();
}
catch(Exception ex)
{
}
}
Output
Most efficient way to find all modes in a List of randomly generated integers and how often they occured
Different code is more efficient for differing lengths, but as the length approaches 6 million, this approach seems fastest. In general, LINQ is not for improving the speed of code, but the understanding and maintainability, depending on how you feel about functional programming styles.
Your code is fairly fast, and beats the simple LINQ approaches using GroupBy
. It gains a good advantage from using the fact that List.Sort
is highly optimized, and my code uses that as well, but on a local copy of the list to avoid changing the source. My code is similar in approach to yours, but is designed around a single pass doing all the computation needed. It uses an extension method I re-optimized for this problem, called GroupByRuns
, that returns an IEnumerable<IGrouping<T,T>>
. It also is hand expanded rather than fall back on the generic GroupByRuns
that takes extra arguments for key and result selection. Since .Net doesn't have an end user accessible IGrouping<,>
implementation (!), I rolled my own that implements ICollection
to optimize Count()
.
This code runs about 1.3x as fast as yours (after I slightly optimized yours by 5%).
First, the RunGrouping
class to return a group of runs:
public class RunGrouping<T> : IGrouping<T, T>, ICollection<T> {
public T Key { get; }
int Count;
int ICollection<T>.Count => Count;
public bool IsReadOnly => true;
public RunGrouping(T key, int count) {
Key = key;
Count = count;
}
public IEnumerator<T> GetEnumerator() {
for (int j1 = 0; j1 < Count; ++j1)
yield return Key;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Add(T item) => throw new NotImplementedException();
public void Clear() => throw new NotImplementedException();
public bool Contains(T item) => Count > 0 && EqualityComparer<T>.Default.Equals(Key, item);
public void CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException();
public bool Remove(T item) => throw new NotImplementedException();
}
Second, the extension method on IEnumerable
that groups the runs:
public static class IEnumerableExt {
public static IEnumerable<IGrouping<T, T>> GroupByRuns<T>(this IEnumerable<T> src) {
var cmp = EqualityComparer<T>.Default;
bool notAtEnd = true;
using (var e = src.GetEnumerator()) {
bool moveNext() {
return notAtEnd;
}
IGrouping<T, T> NextRun() {
var prev = e.Current;
var ct = 0;
while (notAtEnd && cmp.Equals(e.Current, prev)) {
++ct;
notAtEnd = e.MoveNext();
}
return new RunGrouping<T>(prev, ct);
}
notAtEnd = e.MoveNext();
while (notAtEnd)
yield return NextRun();
}
}
}
Finally, the extension method that finds the max count modes. Basically it goes through the runs and keeps a record of those int
with the current longest run count.
public static class IEnumerableIntExt {
public static IEnumerable<KeyValuePair<int, int>> MostCommon(this IEnumerable<int> src) {
var mysrc = new List<int>(src);
mysrc.Sort();
var maxc = 0;
var maxmodes = new List<int>();
foreach (var g in mysrc.GroupByRuns()) {
var gc = g.Count();
if (gc > maxc) {
maxmodes.Clear();
maxmodes.Add(g.Key);
maxc = gc;
}
else if (gc == maxc)
maxmodes.Add(g.Key);
}
return maxmodes.Select(m => new KeyValuePair<int, int>(m, maxc));
}
}
Given an existing random list of integers rl
, you can get the answer with:
var ans = rl.MostCommon();
Finding sample mode with C#
First you need to calculate the max
in the first loop. Then in the second you can find the numbers that have a count equal to max and add the key
, not the max
to your result.
static List<int> Mode(int[] array)
{
if (array.Length == 0)
{
throw new ArgumentException("Sample can't be empty");
}
List<int> result = new List<int>();
var counts = new Dictionary<int, int>();
int max = 0;
foreach (int num in array)
{
if (counts.ContainsKey(num))
counts[num] = counts[num] + 1;
else
counts[num] = 1;
if(counts[num] > max)
max = counts[num];
}
foreach (var key in counts.Keys)
{
if (counts[key] == max)
{
result.Add(key);
}
}
return result;
}
Related Topics
Xml Error: There Are Multiple Root Elements
ASP.NET How to Stream File to User
Generics: Casting and Value Types, Why Is This Illegal
Itextsharp: Adjust 2 Elements on Exactly One Page
Datagrid Shows Path of Image Instead of Image Itself
Startup.Cs in a Self-Hosted .Net Core Console Application
Unit Testing ASP.NET MVC Authorize Attribute to Verify Redirect to Login Page
Linq Order By, Group by and Order by Each Group
How to Use a C# Class Library in a Project
Right Way to Dispose Image/Bitmap and Picturebox
C# Static Member "Inheritance" - Why Does This Exist at All
Mvvm Light & Wpf - Binding Multiple Instances of a Window to a Viewmodel
Hook on Default "Paste" Event of Winforms Textbox Control
Easier Way of Writing Null or Empty
Is Unityscript/JavaScript Discontinued