[C#/LINQ] Averageメソッドの使い方 – 要素の平均値を計算する

C#のLINQにおけるAverageメソッドは、コレクション内の数値要素の平均値を計算するために使用されます。

数値型のコレクションに対して適用でき、整数や浮動小数点数などの型に対応しています。

例えば、IEnumerable<int>IEnumerable<double>などに対して使用可能です。

Averageメソッドは、数値型のプロパティを持つオブジェクトのコレクションに対しても、ラムダ式を使って特定のプロパティの平均を計算することができます。

この記事でわかること
  • Averageメソッドの基本的な使い方
  • オブジェクトコレクションでの応用
  • エラーハンドリングの重要性
  • パフォーマンス最適化のポイント
  • LINQの遅延実行と即時実行の違い

目次から探す

Averageメソッドとは

C#のLINQ(Language Integrated Query)におけるAverageメソッドは、コレクション内の数値要素の平均値を計算するための便利な機能です。

このメソッドは、整数型や浮動小数点数型のコレクションに対して使用でき、簡潔なコードで平均値を取得することができます。

Averageメソッドは、コレクションが空である場合や、null値を含む場合には特別な注意が必要です。

これにより、エラーを回避し、正確な結果を得るための適切なエラーハンドリングが求められます。

LINQを使用することで、データの操作が直感的になり、可読性の高いコードを書くことが可能です。

これから、Averageメソッドの基本的な使い方や応用例について詳しく見ていきましょう。

基本的な使用例

整数型コレクションでの平均値計算

整数型のコレクションに対してAverageメソッドを使用することで、簡単に平均値を計算できます。

以下は、整数型のリストから平均値を求めるサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 10, 20, 30, 40, 50 };
        
        // 整数型コレクションの平均値を計算
        double average = numbers.Average();
        
        Console.WriteLine($"整数型コレクションの平均値: {average}");
    }
}
整数型コレクションの平均値: 30

浮動小数点数型コレクションでの平均値計算

浮動小数点数型のコレクションでも、Averageメソッドを使用して平均値を計算できます。

以下は、浮動小数点数型のリストから平均値を求めるサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<double> numbers = new List<double> { 10.5, 20.5, 30.5, 40.5, 50.5 };
        
        // 浮動小数点数型コレクションの平均値を計算
        double average = numbers.Average();
        
        Console.WriteLine($"浮動小数点数型コレクションの平均値: {average}");
    }
}
浮動小数点数型コレクションの平均値: 30.5

空のコレクションに対する動作

空のコレクションに対してAverageメソッドを使用すると、InvalidOperationExceptionが発生します。

以下は、その動作を示すサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int>(); // 空のコレクション
        
        try
        {
            // 空のコレクションの平均値を計算
            double average = numbers.Average();
            Console.WriteLine($"空のコレクションの平均値: {average}");
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}
エラー: Sequence contains no elements

null値を含むコレクションの処理

null値を含むコレクションに対してAverageメソッドを使用する場合、null値は無視されないため、null合体演算子などを駆使して処理する必要があります。

以下は、その動作を示すサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int?> numbers = new List<int?> { 10, null, 30, null, 50 };
        
        // null値を含むコレクションの平均値を計算
        double average = numbers.Average(n => n ?? 0); // nullの場合は0として扱う
        
        Console.WriteLine($"null値を含むコレクションの平均値: {average}");
    }
}
null値を含むコレクションの平均値: 18

オブジェクトコレクションでのAverageメソッド

特定のプロパティの平均値を計算する方法

オブジェクトコレクションに対してAverageメソッドを使用する場合、特定のプロパティの平均値を計算することができます。

以下は、PersonクラスAgeプロパティの平均値を求めるサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Bob", Age = 25 },
            new Person { Name = "Charlie", Age = 35 }
        };
        
        // Ageプロパティの平均値を計算
        double averageAge = people.Average(p => p.Age);
        
        Console.WriteLine($"人々の年齢の平均値: {averageAge}");
    }
}
人々の年齢の平均値: 30

ラムダ式を使ったプロパティの指定

Averageメソッドでは、ラムダ式を使用して特定のプロパティを指定することができます。

以下は、Salaryプロパティの平均値を計算するサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Employee
{
    public string Name { get; set; }
    public double Salary { get; set; }
}
class Program
{
    static void Main()
    {
        List<Employee> employees = new List<Employee>
        {
            new Employee { Name = "Alice", Salary = 50000 },
            new Employee { Name = "Bob", Salary = 60000 },
            new Employee { Name = "Charlie", Salary = 70000 }
        };
        
        // Salaryプロパティの平均値を計算
        double averageSalary = employees.Average(e => e.Salary);
        
        Console.WriteLine($"従業員の給与の平均値: {averageSalary}");
    }
}
従業員の給与の平均値: 60000

複数のプロパティを持つオブジェクトでの応用例

複数のプロパティを持つオブジェクトに対して、特定の条件に基づいて平均値を計算することも可能です。

以下は、PersonクラスAgeプロパティを使って、特定の条件を満たす人々の年齢の平均値を求めるサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Gender { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30, Gender = "Female" },
            new Person { Name = "Bob", Age = 25, Gender = "Male" },
            new Person { Name = "Charlie", Age = 35, Gender = "Male" },
            new Person { Name = "Diana", Age = 40, Gender = "Female" }
        };
        
        // 女性の年齢の平均値を計算
        double averageFemaleAge = people
            .Where(p => p.Gender == "Female")
            .Average(p => p.Age);
        
        Console.WriteLine($"女性の年齢の平均値: {averageFemaleAge}");
    }
}
女性の年齢の平均値: 35

Averageメソッドのエラーハンドリング

空のコレクションに対する例外処理

Averageメソッドを空のコレクションに対して使用すると、InvalidOperationExceptionが発生します。

このエラーを適切に処理するためには、コレクションが空でないことを確認する必要があります。

以下は、その例を示すサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int>(); // 空のコレクション
        
        try
        {
            // 空のコレクションの平均値を計算
            double average = numbers.Average();
            Console.WriteLine($"空のコレクションの平均値: {average}");
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine($"エラー: {ex.Message}"); // エラーメッセージを表示
        }
    }
}
エラー: Sequence contains no elements

null値を含む場合の対策

null値を含むコレクションに対してAverageメソッドを使用する場合、null値は無視されないため、nullを含む場合の処理を明示的に行うことが推奨されます。

以下は、null値を含むコレクションの平均値を計算するサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int?> numbers = new List<int?> { 10, null, 30, null, 50 };
        
        // null値を含むコレクションの平均値を計算
        double average = numbers.Average(n => n ?? 0); // nullの場合は0として扱う
        
        Console.WriteLine($"null値を含むコレクションの平均値: {average}");
    }
}
null値を含むコレクションの平均値: 18

データ型の不一致によるエラー

Averageメソッドを使用する際に、データ型が一致しない場合、コンパイルエラーや実行時エラーが発生することがあります。

特に、異なるデータ型のコレクションに対してAverageメソッドを適用する場合は注意が必要です。

以下は、データ型の不一致によるエラーの例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<object> mixedNumbers = new List<object> { 10, 20, "30", 40 }; // 文字列を含む

        try
        {
            // データ型の不一致による平均値計算
            double average = mixedNumbers.Average(n => n);
            // 以下のように変換処理を行わないとエラーが発生する
            // double average = mixedNumbers.Average(n => Convert.ToDouble(n)); // 変換処理を行う

            Console.WriteLine($"混合データ型の平均値: {average}");
        }
        catch (InvalidCastException ex)
        {
            Console.WriteLine($"エラー: {ex.Message}"); // エラーメッセージを表示
        }
    }
}
error CS0266: 型 'object' 
を 'long?' に暗黙的に変換できません。
明示的な変換が存在します (cast が不足していないかどうかを確認してください)

このように、データ型の不一致に対しては、適切な型変換を行うか、事前にデータ型を確認することでエラーを回避することが重要です。

応用例

条件付きで平均値を計算する方法

特定の条件を満たす要素の平均値を計算することができます。

以下は、PersonクラスAgeプロパティを使って、年齢が30歳以上の人々の平均年齢を求めるサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Bob", Age = 25 },
            new Person { Name = "Charlie", Age = 35 },
            new Person { Name = "Diana", Age = 40 }
        };
        
        // 年齢が30歳以上の人々の平均年齢を計算
        double averageAge = people
            .Where(p => p.Age >= 30)
            .Average(p => p.Age);
        
        Console.WriteLine($"30歳以上の人々の平均年齢: {averageAge}");
    }
}
30歳以上の人々の平均年齢: 35

グループ化されたデータの平均値を計算する

LINQを使用して、グループ化されたデータの平均値を計算することも可能です。

以下は、EmployeeクラスDepartmentごとにSalaryの平均値を計算するサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Employee
{
    public string Name { get; set; }
    public double Salary { get; set; }
    public string Department { get; set; }
}
class Program
{
    static void Main()
    {
        List<Employee> employees = new List<Employee>
        {
            new Employee { Name = "Alice", Salary = 50000, Department = "HR" },
            new Employee { Name = "Bob", Salary = 60000, Department = "IT" },
            new Employee { Name = "Charlie", Salary = 70000, Department = "IT" },
            new Employee { Name = "Diana", Salary = 55000, Department = "HR" }
        };
        
        // 部門ごとの給与の平均値を計算
        var averageSalaries = employees
            .GroupBy(e => e.Department)
            .Select(g => new
            {
                Department = g.Key,
                AverageSalary = g.Average(e => e.Salary)
            });
        
        foreach (var item in averageSalaries)
        {
            Console.WriteLine($"{item.Department}の平均給与: {item.AverageSalary}");
        }
    }
}
HRの平均給与: 52500
ITの平均給与: 65000

複数のコレクションを結合して平均値を計算する

複数のコレクションを結合して、全体の平均値を計算することもできます。

以下は、2つの異なるリストの数値を結合して平均値を求めるサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> list1 = new List<int> { 10, 20, 30 };
        List<int> list2 = new List<int> { 40, 50, 60 };
        
        // 2つのリストを結合して平均値を計算
        double average = list1.Concat(list2).Average();
        
        Console.WriteLine($"結合したリストの平均値: {average}");
    }
}
結合したリストの平均値: 35

LINQクエリ式でのAverageメソッドの使用

LINQのクエリ式を使用してAverageメソッドを利用することもできます。

以下は、PersonクラスAgeプロパティの平均値をLINQクエリ式で計算するサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Bob", Age = 25 },
            new Person { Name = "Charlie", Age = 35 }
        };
        
        // LINQクエリ式を使用して年齢の平均値を計算
        var averageAge = (from p in people
                          select p.Age).Average();
        
        Console.WriteLine($"人々の年齢の平均値: {averageAge}");
    }
}
人々の年齢の平均値: 30

パフォーマンスと最適化

大規模データセットでのパフォーマンス

大規模データセットに対してAverageメソッドを使用する場合、パフォーマンスが重要な要素となります。

LINQは遅延実行を利用しており、必要なデータのみを処理するため、全体のデータを一度にメモリに読み込むことなく、効率的に計算を行います。

しかし、データセットが非常に大きい場合、計算にかかる時間やメモリ使用量が増加する可能性があります。

以下は、大規模データセットでのAverageメソッドの使用例です。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
class Program
{
    static void Main()
    {
        // 大規模データセットの生成
        List<int> largeDataset = Enumerable.Range(1, 1000000).ToList();
        
        // パフォーマンス計測
        Stopwatch stopwatch = Stopwatch.StartNew();
        
        // 平均値の計算
        double average = largeDataset.Average();
        
        stopwatch.Stop();
        
        Console.WriteLine($"平均値: {average}");
        Console.WriteLine($"計算にかかった時間: {stopwatch.ElapsedMilliseconds} ms");
    }
}
平均値: 500000.5
計算にかかった時間: 2 ms

遅延実行と即時実行の違い

LINQの特徴の一つに遅延実行があります。

これは、クエリが実行されるまで実際のデータ処理が行われないことを意味します。

Averageメソッドは、コレクションが必要なときにのみ計算を行います。

一方、即時実行は、クエリが定義された時点でデータを処理し、結果を返します。

以下は、遅延実行と即時実行の違いを示すサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
        
        // 遅延実行
        var query = numbers.Where(n => n > 2).Average(); // 実行はここでは行われない
        
        Console.WriteLine($"遅延実行の平均値: {query}"); // ここで実行される
        
        // 即時実行
        double immediateAverage = numbers.Where(n => n > 2).ToList().Average(); // ToList()で即時実行
        
        Console.WriteLine($"即時実行の平均値: {immediateAverage}");
    }
}
遅延実行の平均値: 4
即時実行の平均値: 4

メモリ使用量の最適化

メモリ使用量を最適化するためには、必要なデータのみを処理することが重要です。

特に大規模データセットを扱う場合、全てのデータをメモリに読み込むのではなく、必要な部分だけをフィルタリングして処理することが推奨されます。

以下は、メモリ使用量を最適化するためのサンプルコードです。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        // 大規模データセットの生成
        List<int> largeDataset = Enumerable.Range(1, 1000000).ToList();
        
        // 必要なデータのみをフィルタリング
        double average = largeDataset
            .Where(n => n % 2 == 0) // 偶数のみを対象
            .Average();
        
        Console.WriteLine($"偶数の平均値: {average}");
    }
}
偶数の平均値: 500001

このように、フィルタリングを行うことで、メモリ使用量を抑えつつ、必要な計算を効率的に行うことができます。

よくある質問

空のコレクションに対してAverageメソッドを使うとどうなる?

空のコレクションに対してAverageメソッドを使用すると、InvalidOperationExceptionが発生します。

このエラーは、コレクションに要素が存在しないため、平均値を計算できないことを示しています。

したがって、Averageメソッドを使用する前に、コレクションが空でないことを確認することが重要です。

例えば、次のようにエラーハンドリングを行うことができます。

if (numbers.Any())
{
    double average = numbers.Average();
}
else
{
    Console.WriteLine("コレクションは空です。");
}

null値を含むコレクションでの平均値計算はどうすればいい?

null値を含むコレクションで平均値を計算する場合、Averageメソッドはnull値を無視しますが、nullを含む場合の処理を明示的に行うことが推奨されます。

例えば、nullの場合は0として扱うか、フィルタリングを行ってnullを除外する方法があります。

以下のように記述できます。

double average = numbers.Average(n => n ?? 0); // nullの場合は0として扱う

または、nullを除外して計算することも可能です。

double average = numbers.Where(n => n.HasValue).Average(n => n.Value);

Averageメソッドの結果が整数になるのはなぜ?

Averageメソッドの結果が整数になるのは、コレクション内の要素が整数型である場合、計算結果も整数型として返されるためです。

C#では、整数同士の計算は整数として行われ、結果も整数になります。

例えば、整数型のコレクションの平均値を計算すると、結果はdouble型ですが、整数の平均が整数になることがあります。

List<int> numbers = new List<int> { 1, 2, 3 };
double average = numbers.Average(); // 結果は2.0

この場合、結果はdouble型ですが、整数の平均を求めると、整数部分のみが表示されることがあります。

浮動小数点数型のコレクションを使用することで、より精度の高い結果を得ることができます。

まとめ

この記事では、C#のLINQにおけるAverageメソッドの基本的な使い方から、オブジェクトコレクションでの応用、エラーハンドリング、パフォーマンスの最適化に至るまで、幅広く解説しました。

特に、条件付きでの平均値計算やグループ化されたデータの平均値を求める方法など、実践的な例を通じて、さまざまなシナリオでの活用方法を紹介しました。

これを機に、LINQのAverageメソッドを活用して、データ処理の効率を向上させるためのプログラムを作成してみてはいかがでしょうか。

  • URLをコピーしました!
目次から探す