データの平均値を計算する
統計データの解析や、ゲームのスコア計算、システムログのパフォーマンス分析など、データの「平均値」を求める処理はプログラミングにおいて頻出します。
C#のLINQ(Language Integrated Query)が提供するAverageメソッドを使用すると、forループで合計と個数を管理することなく、簡潔に平均値を算出できます。
この記事では、単純な数値配列の平均から、オブジェクトリスト内の特定プロパティの平均、さらにはリストが空の場合の例外処理について解説します。
1. 単純な数値リストの平均
int、long、double、decimalなどの数値型のコレクションに対して、引数なしでAverage()メソッドを呼び出すと、その全要素の算術平均が返されます。
戻り値の型は、元の型に応じてdoubleまたはdecimalになります(intの平均はdoubleになります)。
コード例:ダウンロード速度の平均
using System;
using System.Linq; // LINQを使用するために必須
public class AverageBasicExample
{
public static void Main()
{
// 過去5回のダウンロード速度 (Mbps)
int[] downloadSpeeds = { 120, 95, 110, 105, 130 };
// 平均値を計算 (int配列だが、戻り値は double になる)
double averageSpeed = downloadSpeeds.Average();
Console.WriteLine($"速度ログ: {string.Join(", ", downloadSpeeds)}");
Console.WriteLine($"平均速度: {averageSpeed} Mbps");
}
}
出力結果:
速度ログ: 120, 95, 110, 105, 130
平均速度: 112 Mbps
2. オブジェクトリストの特定プロパティの平均
オブジェクトのリストを扱う場合、引数にラムダ式 x => x.Property を指定することで、「どの値の平均を算出するか」を定義できます。
コード例:従業員の平均勤続年数
using System;
using System.Collections.Generic;
using System.Linq;
// 従業員クラス
public class Employee
{
public string Name { get; set; }
public int YearsOfService { get; set; } // 勤続年数
}
public class AveragePropertyExample
{
public static void Main()
{
var employees = new List<Employee>
{
new Employee { Name = "佐藤", YearsOfService = 5 },
new Employee { Name = "鈴木", YearsOfService = 12 },
new Employee { Name = "高橋", YearsOfService = 3 },
new Employee { Name = "田中", YearsOfService = 8 }
};
// YearsOfService (勤続年数) の平均を算出
double averageYears = employees.Average(e => e.YearsOfService);
Console.WriteLine($"従業員数: {employees.Count}名");
Console.WriteLine($"平均勤続年数: {averageYears} 年");
}
}
出力結果:
従業員数: 4名
平均勤続年数: 7 年
3. 計算式を含めた平均
Averageメソッドのラムダ式内では、単純なプロパティアクセスだけでなく、計算を行うことも可能です。
例えば、各データの「成功数」と「試行数」から「成功率」を計算し、その「平均成功率」を求めることができます。
コード例:メール開封率の平均
using System;
using System.Collections.Generic;
using System.Linq;
public class EmailCampaign
{
public string Subject { get; set; }
public int SentCount { get; set; } // 送信数
public int OpenedCount { get; set; } // 開封数
}
public class AverageCalculationExample
{
public static void Main()
{
var campaigns = new List<EmailCampaign>
{
new EmailCampaign { Subject = "Summer Sale", SentCount = 1000, OpenedCount = 250 },
new EmailCampaign { Subject = "Winter Sale", SentCount = 2000, OpenedCount = 400 },
new EmailCampaign { Subject = "New Arrival", SentCount = 1500, OpenedCount = 450 }
};
// 各キャンペーンごとの開封率 (Opened / Sent) を計算し、その平均を求める
// 注意: 整数同士の割り算にならないよう (double) キャストが必要
double averageOpenRate = campaigns.Average(c => (double)c.OpenedCount / c.SentCount);
// パーセンテージ表示 (P1)
Console.WriteLine($"平均開封率: {averageOpenRate:P1}");
}
}
出力結果:
平均開封率: 25.0%
(計算: 25% + 20% + 30% = 75% / 3 = 25%)
重要な注意点:空のリストと例外
Averageメソッドを使用する際、最も注意すべき点は「要素数が0(空)の場合」の挙動です。
- 数値型(
intなど)のコレクションが空の場合:InvalidOperationException(シーケンスに要素が含まれていません)が発生します。0で割ることになるためです。 - null許容型(
int?など)のコレクションが空、または全てnullの場合: 例外は発生せず、nullが返されます。
安全な平均値の算出方法
リストが空になる可能性がある場合は、以下のいずれかの対策が必要です。
- 事前に
.Any()で要素の存在を確認する。 DefaultIfEmpty()を使用する。- null許容型にキャストして
nullを受け取れるようにする。
var emptyList = new List<int>();
// 対策例: null許容型にキャストしてから Average を呼ぶ
// リストが空なら null が返り、?? 演算子で 0.0 に変換される
double safeAverage = emptyList.Cast<int?>().Average() ?? 0.0;
Console.WriteLine($"安全な平均値: {safeAverage}");
まとめ
Averageメソッドは、データの傾向を把握するための基本的な集計機能です。
collection.Average(): 単純な数値リストの平均。collection.Average(x => x.Prop): オブジェクトリストの特定項目の平均。- 注意点: 空のリストに対して実行すると例外が発生するため、データが0件になる可能性がある場合は対策が必要です。
