前回は、定量データをスコア順に並び替えたのち、この順番でグループに割り振っていく方法を見た。今回は、定量データをスコア順に並び替えるところは同じだが、割り振り方を変更することにより、より平均的に分類できないかを見ていく。

前回と同じく、100人の学生を3つのクラスA、B、Cに分ける方法を考えてみる。

  1. まず、学生をスコア順にA、B、Cに一人ずつ割り振る。
  2. 次に、学生をA、B、Cのそれぞれに割り振った場合、どのクラスが最もスコアが良くなるかを判定して、最もスコアが良いクラスに割り振る。
  3. 仮に、Aに割り振ったとすると、次の学生はB、Cのどちらに割り振った方がクラスのスコアが良くなるのかを判定して、最もスコアが良いクラスに割り振る。
  4. 仮に、Bに割り振ったとすると、次の学生は自動的にCに割り振って、2へ繰り返す。

それでは、実際にC#で組んでみる。
以前作成した定量データを格納するItemクラスと分類された定量データを格納するGroupクラスは変更がないので、記載は省略した。
実際の分類は以下のクラスで行う。


public class SequentialSelectionClassification
{
    public IEnumerable Classify(IEnumerable source, IEnumerable valueIndices, int groupNumber)
    {
        //平均値
        var averageDictionary = new Dictionary();
        foreach (var valueIndex in valueIndices)
        {
            averageDictionary[valueIndex] = source.Average(x => x.Values[valueIndex]);
        } 

        //スコア計算
        var scoreDictionary = new Dictionary();
        foreach (var item in source)
        {
            var d = 0.0d;
            foreach (var valueIndex in valueIndices)
            {
                var v = (item.Values[valueIndex] - averageDictionary[valueIndex]);
                d += v * v;
            }
            scoreDictionary[item] = d;
        } 

        //スコアを昇順に並び替え
        var list = new List>(scoreDictionary);
        //昇順
        list.Sort((x, y) => x.Value.CompareTo(y.Value));
        // 降順
        //list.Sort((x, y) => -x.Value.CompareTo(y.Value));
        var items = list.Select(x => x.Key).ToArray(); 

        //グループ作成
        var groups = new List();
        for (var i = 0; i < groupNumber; i++)
        {
            groups.Add(new Group(valueIndices));
        } 

        //グループのインデックスと要素数のペア
        var groupCountDictionary = new Dictionary();
        for (var i = 0; i < groupNumber; i++)
        {
            groupCountDictionary[i] = 0;
        } 

        //グループ振り分け
        for (var i = 0; i < groupNumber; i++)
        {
            groups[i].Items.Add(items[i]);
            groupCountDictionary[i]++;
        }
        for (var i = groupNumber; i < items.Length; i++)
        {
            var item = items[i];
            var min = groupCountDictionary.Min(x => x.Value);
            var indices = groupCountDictionary.Where(x => x.Value == min).Select(x => x.Key); 

            var minIndex = -1;
            var minScore = double.MaxValue; 

            foreach (var index in indices)
            {
                var score = Score(averageDictionary, item, groups[index]);
                if (score < minScore)
                {
                    score = minScore;
                    minIndex = index;
                }
            } 

            groups[minIndex].Items.Add(item);
            groupCountDictionary[minIndex]++;
        } 

        return groups;
    } 

    private double Score(Dictionary averageDictionary, Item item, Group group)
    {
        var d = 0.0d;
        foreach (var kvp in averageDictionary)
        {
            var s = group.Items.Sum(x => x.Values[kvp.Key]) + item.Values[kvp.Key];
            s = (double)s / (group.Items.Count + 1) - kvp.Value;
            d += s * s;
        }
        return d;
    }
}

以上で、準備は完了だ。それでは、実際に分類してみる。定量データは乱数を用いてダミーデータを作成している。


public class Test
{
    public void Test2()
    {
        //ダミーデータ作成
        var items = new List();
        for (var i = 0; i < 100; i++)
        {
            var random = new Random(i);
            var values = new double[2];
            values[0] = random.Next(0, 100);
            values[1] = random.Next(0, 100);
            items.Add(new Item(values));
        }

        //分類
        var classification = new SequentialSelectionClassification();
        var groups = classification.Classify(items, new int[] { 0, 1 }, 3);

        //ダミーデータ表示
        System.Console.WriteLine("dummy-data");
        foreach (var item in items)
        {
            System.Console.WriteLine(item.Values[0] + "," + item.Values[1]);
        }

        //ダミーデータの平均値の表示
        System.Console.WriteLine("dummy-data-average");
        System.Console.WriteLine("0:" + items.Average(x => x.Values[0]));
        System.Console.WriteLine("1:" + items.Average(x => x.Values[1]));

        //結果表示
        foreach (var group in groups)
        {
            System.Console.WriteLine("group-item");
            foreach (var item in group.Items)
            {
                System.Console.WriteLine(item.Values[0] + "," + item.Values[1]);
            }
            System.Console.WriteLine("group-average");
            var dictionary = group.Average();
            foreach (var kvp in dictionary)
            {
                System.Console.WriteLine(kvp.Key + ":" + kvp.Value);
            }
        }
    }
}

この結果は以下のようになった。実際には、前回用いたダミーデータをファイル保存しておき、それを読み取っている。
【スコアを昇順で並び替えた場合】

全データ グループ1 グループ2 グループ3
インデックス0の平均値 51.12 52.81 44.78 55.61
インデックス1の平均値 50.66 45.96 50.06 55.79

【スコアを降順で並び替えた場合】

全データ グループ1 グループ2 グループ3
インデックス0の平均値 51.12 47.53 52.57 53.26
インデックス1の平均値 50.66 53.06 45.69 53.14

評価式を定義する。
(グループ1のインデックス0平均値-全データのインデックス0の平均値)^2
+(グループ2のインデックス0平均値-全データのインデックス0の平均値)^2
+(グループ3のインデックス0平均値-全データのインデックス0の平均値)^2
+(グループ1のインデックス1平均値-全データのインデックス1の平均値)^2
+(グループ2のインデックス1平均値-全データのインデックス1の平均値)^2
+(グループ3のインデックス1平均値-全データのインデックス1の平均値)^2

スコアを昇順で並び替えた場合:111.97
スコアを降順で並び替えた場合:56.18

以上から、スコアを降順に並べてから、グループスコアが最も小さくなるようにグループに振り分けていく方が、より平均的に分類できていることが分かる。

今までは、定量データをスコア順に取り出していた。
次回は、スコア順ではなく適切に取り出すことによって、より平均的に分類できないかを考えてみる。

関連する記事

  • 平均的に分類する方法の考察(1)平均的に分類する方法の考察(1) ある定量データが与えられたとき、それを平均的に分類するための方法を考えてみる。 ここで言う平均的に分類するというのは、分類された各グループのデータ数がほぼ等しく、かつ、分類された各グループの平均値が定量データ全体の平均値にほぼ等しくなるように分類するということである。 具体的には、学生のクラス替えを行う際に、100人の学生を3つのクラスに振り分けたいが、その場合、各ク […]
  • 決定木 – 分類木決定木 – 分類木 決定木とは、分類ルールを木構造で表したものである。分類したいデータを目的変数(従属変数)、分類するために用いるデータを説明変数(独立変数)という。目的変数がカテゴリデータなどの場合は「分類木」、連続値などの量的データの場合は「回帰木」と呼ばれる。 決定木の最大のメリットは、結果にグラフを用いることができるため、視覚的に確認できることである。 ここでは、R言語の「r […]
  • R MASSパッケージcorresp関数のエラー対処方法 RのMASSパッケージ内のcorresp関数を用いたとき、エラーに悩まされたので、皆様と共有しておく。 環境 Ubuntuのバージョン $ cat /etc/lsb-release […]
  • Python pyqueryを用いて簡単にウェブスクレイピングPython pyqueryを用いて簡単にウェブスクレイピング Pythonでpyqueryパッケージを用いて、ウェブスクレイピングを行う方法をご紹介する。ウェブ上には様々なデータがあるが、必要なデータがいつもファイルとしてダウンロードできるとは限らない。ここでご紹介する方法は、htmlファイルを直接解析して、必要な情報を取得するという方法である。 それでは、さっそく見ていこう。 Ubuntu […]
  • Ubuntu 日本語構文・格・照応解析システムKNPをpythonから使えるようにするUbuntu 日本語構文・格・照応解析システムKNPをpythonから使えるようにする Ubuntu14.04で日本語構文・格・照応解析システムKNPをpythonから使えるようにする方法をお伝えする。 Python2系 まずはKNP - KUROHASHI-KAWAHARA […]
平均的に分類する方法の考察(3)