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

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

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

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

関連する記事

  • 中小企業の経営者なら知っておきたい財務データ活用法中小企業の経営者なら知っておきたい財務データ活用法 あなたは財務データを経営ツールとして有効に活用できているだろうか。目の前の仕事で忙しい、財務データの見方が分からない、顧問税理士に任せてあるなどの理由を付けて回避していないだろうか。 目の前の仕事で忙しいといって、ただがむしゃらに働いたところで利益を出し続けなければ生き残っていけない。どの方向を向いてがむしゃらに働くかを把握しておかなければならない。 財務データの […]
  • Ubuntu 20.04にDockerをインストールする手順Ubuntu 20.04にDockerをインストールする手順 Ubuntu 20.04にDockerをインストールおよびインストールの確認、sudoなしでdockerコマンドを実行する方法をお伝えします。 Ubuntu 20.04にDockerをインストールする方法はいくつかありますが、ここでは最も簡単な方法であるsnapでインストールすることにします。 Ubuntu […]
  • MINTELのGNPDから出力されたCSVの文字化けの対処法 ミンテルGNPD(世界新商品情報データベース)から出力されたCSVが文字化けしている場合の対処法を備忘録として残しておきます。 Windowsパソコンにダウンロードかつ英文のみ ここでの対応は、「café」のようなアキュート・アクセントなどが文字化けしている場合の対応になります。 ubuntu上で文字コードをUTF-8に変換するにはiconvコマンドを用いて、次 […]
  • R言語 CRAN Task View:空間データの分析R言語 CRAN Task View:空間データの分析 CRAN Task View: Analysis of Spatial Dataの英語での説明文をGoogle翻訳を使用させていただき機械的に翻訳したものを掲載しました。 Maintainer: Roger Bivand Contact: Roger.Bivand at […]
  • R UbuntuでRからMariaDBへ接続する方法 UbuntuでRからMariaDBへ接続して、データフレームとして取得する方法をお伝えする。 環境 Ubuntuのバージョン $ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu […]
平均的に分類する方法の考察(3)