How to use LINQ for grouping Statistics
This article will explain in detail how to use LINQ for grouping statistics. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.
Let's generate the data to be counted as follows:
IEnumerable GetTuples (int n) {var tuples = new Tuple [n]; var rand = new Random (); for (int k = 1, I = 0; I
< n; i++) { var r = rand.Next(n); k += (r >= n-3)? 2: ((r > = n-9)? 1: 0); tuples [I] = new Tuple (k, rand.NextDouble ());} return tuples;}
This method generates n items of data that have been sorted.
Now, let's group by keyword and count the number and average of each group.
First, use the foreach loop of C#, as follows:
IEnumerable ForEach (IEnumerable tuples) {var result = new List (); var count = 0; var sum = 0.0; int? Key = null; foreach (var v in tuples) {if (key! = v.Item1) {if (key! = null) result.Add (new Tuple (key.Value, count, sum / count)); sum = count = 0; key = v.Item1;} count++; sum + = v.Item2;} if (key! = null) result.Add (new Tuple (key.Value, count, sum / count)) Return result;}
The drawback of this approach is that you have to do a statistic after the end of the foreach loop to smell the "bad smell" of the code.
So, let's ReFactor, this time, using an iterator to loop:
IEnumerable Iterate (IEnumerable tuples) {var result = new List (); var count = 0; var sum = 0.0; int? Key = null; for (var iter = tuples.GetEnumerator (); count++, sum + = iter.Current.Item2) {var hasValue = iter.MoveNext (); if (! hasValue | | key! = iter.Current.Item1) {if (key! = null) result.Add (new Tuple (key.Value, count, sum / count)); if (! hasValue) break; sum = count = 0; key = iter.Current.Item1 }} return result;}
In this way, the "bad smell" is eliminated.
Note that both methods assume that the input data is already sorted. If not, the input data should be sorted first.
*. If you use LINQ, it can be easier:
IEnumerable Linq (IEnumerable tuples) {var result = new List (); var Q = from k in tuples group k by k.Item1; foreach (var g inq) result.Add (new Tuple (g.Key, g.Count (), g.Average (v = > v.Item2)); return result;}
Note that the LINQ method takes more time to run and takes up more memory.
Let's look at the Main method:
Static void Main (string [] args) {try {new Program (). Run (Console.Out, int.Parse (args [0]));} catch (Exception ex) {Console.WriteLine (ex);}} void Run (TextWriter writer, int n) {var tuples = GetTuples (n * 1024 * 1024); Write ("ForEach", writer, ForEach (tuples); Write ("Iterate", writer, Iterate (tuples)) Write ("Linq", writer, Linq (tuples);}
The Write method is as follows:
Void Write (string title, TextWriter writer, IEnumerable tuples) {writer.WriteLine ("= >" + title + "