LINQ - 集計

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

LINQの集計に関する拡張メソッドは、コレクション内の要素を効率的に処理して、単一の結果を得るために使用される。

これらのメソッドは、データ分析やビジネスロジックの実装において有効である。
例えば、売上データの分析や、ユーザーの行動パターンの把握などに活用できる。

実例としては、顧客の年齢データから最年少と最年長の顧客を特定して、平均年齢を計算するシナリオが考えられる。
このような場合、Minメソッド、Maxメソッド、Averageメソッドを組み合わせて効果的に情報を抽出することができる。

Maxメソッドは、シーケンス内の最大値を返す。
数値型だけでなく、比較可能な任意の型に対して使用できる。
空のシーケンスに対して呼び出すと例外が発生するため、注意が必要である。

Minメソッドは、シーケンス内の最小値を返す。
Maxメソッドと同様、様々な型に対して使用できる。
空のシーケンスへの対応にも同じ注意が必要である。

Averageメソッドは、数値シーケンスの平均値を計算する。
整数型や浮動小数点型のシーケンスに対して使用する。
結果は、常に浮動小数点型となる。

Sumメソッドは、数値シーケンスの合計を計算する。
整数型や浮動小数点型のシーケンスに対して使用する。
大きな値の合計を計算する際はオーバーフローに注意が必要である。

Countメソッドは、シーケンス内の要素数を返す。
条件を指定して、特定の条件を満たす要素の数を数えることも可能である。
大規模なコレクションでは、パフォーマンスに影響を与える可能性がある。

Aggregateメソッドは、カスタム集計操作を実行する。
より複雑な集計ロジックを実装する場合に使用する。
初期値とラムダ式を指定して、シーケンスの各要素に対して累積的に操作を適用する。

パフォーマンスの観点からは、これらのメソッドは一般的に効率的であるが、大規模なデータセットを扱う場合には注意が必要である。
特に、複数の集計操作を行う場合、可能な限り1度のイテレーションで処理するよう設計することが重要である。


結果の表示

ここでは、結果の表示において、独自の拡張メソッドToResult(this IEnumerable)を使用している。

 // 結果表示用の拡張メソッド
 public static String ToResult<TSource>(this IEnumerable<TSource> source)
 {
    return "{" + string.Join(", ", source) + "}";
 }
 
 public static String ToResult<TKey, TSource>(this IEnumerable<IGrouping<TKey, TSource>> source)
 {
    return source.Select(group => string.Format("Key={0}, Source={1}", group.Key, group.ToResult())).ToResult();
 }



集計

メソッド名 機能
Max 最大値を返す。
Min 最小値を返す。
Average 平均値を返す。
Sum 合計を返す。
Count 要素数を返す。
Aggregate アキュムレータ関数で処理した結果を返す。
 // 基本的な例
 var source = new[] { 3, 4, 5, 6, 7, 8, 9, 9 };
 
 Console.WriteLine(source.Max());
 // -> 9
 
 Console.WriteLine(source.Min());
 // -> 3
 
 Console.WriteLine(source.Average());
 // -> 6.375
 
 Console.WriteLine(source.Sum());
 // -> 51
 
 Console.WriteLine(source.Count());
 // -> 8
 
 Console.WriteLine(source.Aggregate((now, next) => now * next));
 // -> 1632960
 
 // 参考:標本分散
 double ave = source.Average();
 Console.WriteLine(source.Sum(e => Math.Pow(e - ave, 2)) / source.Count());
 // -> 4.484375
 
 
 // 応用例
 List<Product> products = new List<Product>
 {
    new Product { Name = "Apple",  Price = 0.5m, Stock = 100 },
    new Product { Name = "Banana", Price = 0.3m, Stock = 150 },
    new Product { Name = "Orange", Price = 0.6m, Stock = 80 },
    new Product { Name = "Mango",  Price = 1.2m, Stock = 30 }
 };
 
 Console.WriteLine($"最も高価な商品: {products.Max(p => p.Price)}");
 // -> 1.2
 
 Console.WriteLine($"最も安価な商品: {products.Min(p => p.Price)}");
 // -> 0.3
 
 Console.WriteLine($"平均価格: {products.Average(p => p.Price):C}");
 // -> 0.65
 
 Console.WriteLine($"総在庫数: {products.Sum(p => p.Stock)}");
 // -> 360
 
 Console.WriteLine($"0.5ドル以上の商品数: {products.Count(p => p.Price >= 0.5m)}");
 // -> 3
 
 decimal totalValue = products.Aggregate(0m, (total, product) => total + (product.Price * product.Stock));
 Console.WriteLine($"総在庫価値: {totalValue:C}");
 // -> 177.00
 
 
 // 複合的な使用例
 var summary = new
 {
    MostExpensive = products.OrderByDescending(p => p.Price).First().Name,
    AveragePrice = products.Average(p => p.Price),
    TotalStock = products.Sum(p => p.Stock),
    TotalValue = totalValue
 };
 
 Console.WriteLine($"最も高価な商品: {summary.MostExpensive}");
 // -> Mango
 
 Console.WriteLine($"平均価格: {summary.AveragePrice:C}");
 // -> 0.65
 
 Console.WriteLine($"総在庫数: {summary.TotalStock}");
 // -> 360
 
 Console.WriteLine($"総在庫価値: {summary.TotalValue:C}");
 // -> 177.00