ある定量データが与えられたとき、それを平均的に分類するための方法を考えてみる。
ここで言う平均的に分類するというのは、分類された各グループのデータ数がほぼ等しく、かつ、分類された各グループの平均値が定量データ全体の平均値にほぼ等しくなるように分類するということである。

具体的には、学生のクラス替えを行う際に、100人の学生を3つのクラスに振り分けたいが、その場合、各クラスの人数がほぼ等しく、また、各クラスの文系能力と理系能力の平均値もほぼ等しく分けるにはどうしたらよいのかについて考えてみる。

実際に分類を行うのは、手作業で行うわけにはいかないので、プログラムを組むことにする。ここでは、C#(.net framwork4.0)を用いることにした。

インターネット上でこのような分類方法またはソースコードについて検索してみたが、検索の仕方が悪いのか良い情報にめぐり合わなかったので考えてみた。このような分類方法に詳しいウェブサイトをご存知だったら教えていただけたら幸いだ。

では、さっそく考察していく。
具体的に考えたほうが分かりやすいので、100人の学生を3つのクラスに分ける方法を考えてみる。定量データは100人の学生の国語と数学の点数が与えられているとしよう。

つまり、Aクラス34人、Bクラス33人、Cクラス33人の3つクラスで、各クラスの国語と数学の点数の平均値がほぼ等しくなるように分けるにはどうすればよいのかを考えてみる。

まず、最初に思いついたのは、学生を平均値に近い学生順に並び替えをして、この順でAクラス→Bクラス→Cクラス→Aクラス→・・・と割り当てていけば、最終的に、3つのクラスの平均値がほぼ等しくなるのでは、ということだ。

consideration-classifier-average-1

そこで問題となるのが、学生の並び替えだ。ここでは、以下の算式でスコアを出して、このスコアによって並び替えを行うこととした。^2は()内の数値を二乗するということである。

スコア=(国語の点数-全学生の国語の平均点)^2+(数学の点数-全学生の数学の平均点)^2

それでは、実際にC#で組んでみる。
ここからは、少し一般化しているので注意して欲しい。
まず、定性データの入れ物として以下のクラスを準備する。


public class Item
{
    private double[] _values;
    public double[] Values { get { return _values; } }

    public Item(double[] values)
    {
        _values = values;
    }
}

次に、グループ分けされた定量データの入れ物として以下のクラスを準備する。コンストラクタのvalueIndeicesはItemクラスのValueプロパティのうち、どのインデックスが平均値に採用されたかを表すものである。


public class Group
{
    private IEnumerable _valueIndices;

    private IList _items = new List();
    public IList Items { get { return _items; } }

    public Group(IEnumerable valueIndices)
    {
        _valueIndices = valueIndices;
    }

    public IDictionary Average()
    {
        var dictionary = new Dictionary();
        foreach (var valueIndex in _valueIndices)
        {
            dictionary[valueIndex] = _items.Average(x => x.Values[valueIndex]);
        }
        return dictionary;
    }
}

実際の分類は以下のクラスで行う。


public class SequentialClassification
{
    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));
        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 count = items.Count();
        for (var i = 0; i < count; i++)
        {
            groups[i % groupNumber].Items.Add(items[i]);
        }

        return groups;
    }
}

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


public class Test
{
    public void Test1()
    {
        //ダミーデータ作成
        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 SequentialClassification();
        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 55.85 44.78 52.57
インデックス1の平均値 50.66 56.05 50.06 45.69

やはりと言うべきだが、精度が悪い。特に、インデックス0と1のバラツキが大きい。この理由は明らかで、各インデックスに対しての平均値をまったく考慮していないからである。次回は、各インデックスの平均値を考慮したものを考えてみる。

関連する記事

  • R言語 CRAN Task View:時系列解析R言語 CRAN Task View:時系列解析 CRAN Task View: Time Series Analysisの英語での説明文をGoogle翻訳を使用させていただき機械的に翻訳したものを掲載しました。 Maintainer: Rob J. Hyndman Contact: Rob.Hyndman at […]
  • RStudioのGlobal Optionsの各種設定RStudioのGlobal Optionsの各種設定 RStudioのGlobal Optionsにどのような内容が記載されているかをお伝えいたします。 RStudioのGlobal Optionsは、メニューバーからToolsを選択して、表示されたポップアップ内にあります。 目次 General: 一般 Code: コード Editing: 編集 Display: 表示 […]
  • R言語 CRAN Task View:ベイズ推論R言語 CRAN Task View:ベイズ推論 CRAN Task View: Bayesian Inferenceの英語での説明文をGoogle翻訳を使用させていただき機械的に翻訳したものを掲載しました。 Maintainer: Jong Hee Park Contact: jongheepark at […]
  • Ubuntuに日本語係り受け解析器CaboChaをインストールする手順Ubuntuに日本語係り受け解析器CaboChaをインストールする手順 日本語のテキストデータを解析する際に、係り受けを抽出したいときがある。係り受けを行うことができるフリーのソフトウェアの一つにCaboChaがある。CaboChaは, Support Vector […]
  • 経営戦略に使えるRFM分析の基礎知識と活用法経営戦略に使えるRFM分析の基礎知識と活用法 RFM分析とは、ある一定期間の購買履歴データを用いて、顧客を分類することにより様々な示唆を得るための分析手法である。分類の仕方は、顧客を直近購買時期・購入頻度・購買額の3つの軸で分類し、各軸ごとにランク分けをする。このことにより、顧客が現在どの位置にいて、それがどのくらいの人数なのかを把握することができる。 あなたは、このような分類をすることにどのようなメリットがあるの […]
平均的に分類する方法の考察(1)