データ分析

例えば、次のようなテストの結果があった時、 各教科の平均点と偏差値、また数学と英語の相関係数を求めたい。

数学 英語
name0 20 11
name1 5 4
name2 30 31
name3 90 48
name4 45 65
name5 3 12

データ分析の初歩的知識を仮定するが、簡単に今回使用する概念についてまとめておく。

分散と標準偏差

$n$個の数値データの列$\{x_n\}$が与えられ時、分散は$a$を平均値として、 \[ \sigma^2 = \frac{1}{n} \sum_{k=1}^n (x_k- a)^2 \] と定義される。平方根を取った値$\sigma$は標準偏差と呼ばれる。

偏差値

偏差値は、次式によって定義される。 \[ 50 + 10 \cdot \frac{x_i-a}{\sigma} \]

共分散

二つの$n$個の数値データ$\{x_n\},\{y_n\}$に対して、それぞれの平均値を$a_1,a_2$とすると、 共分散は次の式で定義される. \[ \frac{1}{n} \sum_{k=1}^n (x_k-a_1)(y_k-a_2). \]

相関係数

二つの$n$個の数値データ$\{x_n\},\{y_n\}$が与えられとする。 $\{x_n\}$の標準偏差を$\sigma_1$,$\{y_n\}$の標準偏差を$\sigma_2$とし、 共分散を$\sigma_{3}$とおく。このとき、 相関係数は、 \[ \frac{\sigma_{3}}{\sigma_{1}\cdot \sigma_{2} } \] によって定義される。

プログラムの作成

成績一覧を表示するプログラムから相関係数を求めるプログラムまで、段階的に作成していく。

成績一覧を表示する

成績を扱うクラス Grade を定義して、与えられた数学と英語の成績一覧表をもとに、データが読み取りやすいように 成績一覧を表示する。

class Program
{
    static void Main(string[] args)
    {
        // 数学と英語のテスト結果一覧.
        Grade[] grades = new Grade[] {
          new Grade( "Name0", 20, 11),new Grade( "Name1",  5, 4),
          new Grade( "Name2", 30, 31),new Grade( "Name3", 90,48),
          new Grade( "Name4", 45, 65),new Grade( "Name5",  3, 12)
        };
        Console.WriteLine("成績一覧");
        foreach (var item in grades)
        {
            Console.WriteLine(item);
        }
    }
}
class Grade // 成績
{
    public string Name;
    public int MathScore;
    public int EnglishScore;
    public Grade(string name, int math, int english)
    {
        Name = name;
        MathScore = math;
        EnglishScore = english;
    }
    public override string ToString()
    {
        return $"名前 {Name}, 数学{MathScore}, 英語{EnglishScore}";
    }
}

実行結果.

成績一覧
名前 Name0, 数学20, 英語11
名前 Name1, 数学5, 英語4
名前 Name2, 数学30, 英語31
名前 Name3, 数学90, 英語48
名前 Name4, 数学45, 英語65
名前 Name5, 数学3, 英語12

数学の偏差値を求めるプログラム

次に数学の平均点を表示して、数学の偏差値を計算するメソッドMathDeviationValueを作成する。

class Program
{
    static void Main(string[] args)
    {
        // 数学と英語のテスト結果一覧.
        Grade[] grades = new Grade[] {
          new Grade( "Name0", 20, 11),new Grade( "Name1",  5, 4),
          new Grade( "Name2", 30, 31),new Grade( "Name3", 90,48),
          new Grade( "Name4", 45, 65),new Grade( "Name5",  3, 12)
        };
        Console.WriteLine("成績一覧");
        foreach (var item in grades)
        {
            Console.WriteLine(item);
        }
        Console.WriteLine("数学の平均点");
        Console.WriteLine("{0:F3}",grades.Average(g=>g.MathScore));
        Console.WriteLine("数学偏差値一覧");
        for (int i = 0; i < grades.Length; i++)
        {
            Console.WriteLine("{0}:{1:F3}",grades[i].Name,MathDeviationValue(grades,i));
        }
    }
    static double MathDeviationValue(Grade[] grades,int n)
    {
        // 数学の平均
        double averageMath = grades.Average(c => c.MathScore);
        // 数学の分散
        double sx2 = grades.Select(g => (g.MathScore - averageMath)).Average(x => x * x);
        // 数学の標準偏差
        double sx = Math.Sqrt(sx2);
        // 指定番目の偏差値を返す。
        return 50 + 10 * (grades[n].MathScore - averageMath) / sx; 
    }
}
class Grade // 成績
{
    public string Name;
    public int MathScore;
    public int EnglishScore;
    public Grade(string name, int math, int english)
    {
        Name = name;
        MathScore = math;
        EnglishScore = english;
    }
    public override string ToString()
    {
        return $"名前 {Name}, 数学{MathScore}, 英語{EnglishScore}";
    }
}

実行結果.

成績一覧
名前 Name0, 数学20, 英語11
名前 Name1, 数学5, 英語4
名前 Name2, 数学30, 英語31
名前 Name3, 数学90, 英語48
名前 Name4, 数学45, 英語65
名前 Name5, 数学3, 英語12
数学の平均点
32.167
数学偏差値一覧
Name0:45.887
Name1:40.817
Name2:49.268
Name3:69.550
Name4:54.338
Name5:40.141

解説

数学の分散を求めるところで

double sx2 = grades.Select(g => (g.MathScore - averageMath)).Average(x => x * x);

とあるが、分散を定義式から安直に書くならば右辺は

grades.Select(g => (g.MathScore - averageMath)*(g.MathScore - averageMath)).Average();

となるかもしれない。しかし、この表現は、読みにくいので避けたいところ。

読みにくい部分は、2乗しているところだから、Selectを二度用いて、

grades.Select(g => (g.MathScore - averageMath)).Select(x=>x*x).Average();

とすると良い。しかし、二つ目のSelectはAverageに引数を設定すれば除去できる。

相関係数を求める

最後に相関係数を求めるメソッドCorrelationCoefficientを作成する。

class Program
{
    static void Main(string[] args)
    {
        // 数学と英語のテスト結果一覧.
        Grade[] grades = new Grade[] {
          new Grade( "Name0", 20, 11),new Grade( "Name1",  5, 4),
          new Grade( "Name2", 30, 31),new Grade( "Name3", 90,48),
          new Grade( "Name4", 45, 65),new Grade( "Name5",  3, 12)
        };
        Console.WriteLine("成績一覧");
        foreach (var item in grades)
        {
            Console.WriteLine(item);
        }
        Console.WriteLine("数学と英語の相関係数.");
        Console.WriteLine("相関係数={0:f3}", CorrelationCoefficient(grades));
    }
    static double CorrelationCoefficient(Grade[] grades)
    {
        // 数学の平均
        double averageMath = grades.Average(c => c.MathScore);
        // 英語の平均
        double averageEnglish = grades.Average(c => c.EnglishScore);
        // 数学の分散
        double sx2 = grades.Select(g => (g.MathScore - averageMath)).Average(x => x * x);
        // 英語の分散
        double sy2 = grades.Select(c => (c.EnglishScore - averageEnglish)).Average(x => x * x);
        // 数学と英語の共分散
        double sxy = grades.Average(c => (c.MathScore - averageMath) * (c.EnglishScore - averageEnglish));
        // 数学の標準偏差
        double sx = Math.Sqrt(sx2);
        // 英語の標準偏差
        double sy = Math.Sqrt(sy2);
        // 相関係数を返す.
        return sxy / (sx * sy);
    }
}
class Grade // 成績
{
    public string Name;
    public int MathScore;
    public int EnglishScore;
    public Grade(string name, int math, int english)
    {
        Name = name;
        MathScore = math;
        EnglishScore = english;
    }
    public override string ToString()
    {
        return $"名前 {Name}, 数学{MathScore}, 英語{EnglishScore}";
    }
}

実行結果.

成績一覧
名前 Name0, 数学20, 英語11
名前 Name1, 数学5, 英語4
名前 Name2, 数学30, 英語31
名前 Name3, 数学90, 英語48
名前 Name4, 数学45, 英語65
名前 Name5, 数学3, 英語12
数学と英語の相関係数.
相関係数=0.756