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

前回と同じく、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

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

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

関連する記事

  • 地図で見る石川県金沢市の人口 2014年1月地図で見る石川県金沢市の人口 2014年1月 金沢市役所が公開している平成26年1月の住民基本台帳人口と総務省統計局が公開している地図データを基に人口、人口密度、世帯数、未成年・高齢者の年齢別などの数値および前年同月からの増減率を地図上に色分けして視覚化したものと上位・下位のランキングをご紹介する。 人口、世帯数などのデータを活用しようと考えた場合、まずは国勢調査を思いつくが、国勢調査は5年ごとに行われており、最近 […]
  • Ubuntu16.04で任意のバージョンのNode.jsをインストールする方法Ubuntu16.04で任意のバージョンのNode.jsをインストールする方法 Ubuntu16.04で、任意のバージョンのNode.js環境を構築する方法をお伝えいたします。 apt-getコマンドによりNode.jsをインストールしようとすると、かなり古いバージョンがインストールされます。 そのため、最新または任意のバージョンをインストールするときは、PPA(personal package […]
  • R スティール・ドゥワス(Steel-Dwass)法R スティール・ドゥワス(Steel-Dwass)法 スティール・ドゥワス(Steel-Dwass)法とは、テューキー(Tukey)法の多重比較に対応するノンパラメトリックな多重比較である。 スティール・ドゥワス法を簡単に言うと、正規分布を仮定しない各群間を順位を用いて多重比較で調べる方法である。 Rで、スティール・ドゥワス法を使う場合は、「スティール・ドゥワス(Steel-Dwass)の方法による多重比較」のページにソ […]
  • Python 度数分布表から乱数を生成するPython 度数分布表から乱数を生成する Pythonで与えられた度数分布に従う乱数を生成する方法をご紹介する。 さっそくだが、全コードを以下に記す。 import random import matplotlib.pyplot as plt def random_freq_index(freq_list): """ 度数分布表から乱数を生成してインデックス番号を返します […]
  • Ubuntu18.04で任意のバージョンのNode.jsをインストールする方法Ubuntu18.04で任意のバージョンのNode.jsをインストールする方法 Ubuntu18.04で、任意のバージョンのNode.js環境を構築する方法をお伝えいたします。 aptコマンドによりNode.jsをインストールしようとすると、かなり古いバージョンがインストールされます。 そのため、最新または任意のバージョンをインストールするときは、PPA(personal package […]
平均的に分類する方法の考察(3)