LINQ - 集計
概要
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