はじめに
趣味でテスト理論周りをいろいろ漁っていて、潜在ランク理論というものを見つけました。
潜在ランク理論の特徴は、例えばテストの得点では1点刻みの連続的な能力評価を行うのに対して、 段階的で順序的な能力評価を行う点です。
テストにおける1点や2点の差は誤差の範囲内であり、その数点で評価を行うのはあまり意味のないのでは、という考えで、誤差の得点で一喜一憂させるよりも、段階評価で一定の範囲内の人に同一な評価を行うことで、測定誤差の影響を抑えたフォードバックができるようです。
原理としては、自己組織化マップ(SOM)、またはグラフィックマッピング(GTM)を利用しているようですが、ここの理解は後回しです。
潜在ランク理論の適用先としては、以下のようなことが考えられそうです。
個人的には潜在ランク理論は順序性をもったクラスタリング手法という理解です。 今だと連続的な得点をエイヤでいくつか閾値を決めて段階的にすることが多いですが、 潜在ランク理論を使えばより意味のある切り方ができる可能性があります。
記事を書く上では下記のサイトを参考にさせていただきました。
実験
実際に潜在ランク理論を使ってみます。 今回のデータは、Pokemon with stats | Kaggleのポケモンの個体値のデータセットを使います。
データ構造は以下のようになっており、ポケモンの名前とHP、Attack、Defenceなどの個体値のデータが入っています。

今回は第一世代(赤緑)のポケモンについて、HP、Attack、Defence、Sp. Atk、Sp. Def、Speedの6つのパラメータを用い、 順序性をもつクラスタリングを行います。
pythonでは潜在ランク理論を実行するパッケージがなかったので、 このためにRstudioを自機にインストールしましてrでコードを書きました。
rのコードは以下のようになりました。
#潜在ランク理論のパッケージ読み込み source("http://bit.ly/latent_rank") df=read.csv("Pokemon.csv") name=df[1:166, "Name"] #使用するカラムの指定 #第一世代のみ(メガあり) columnList <- c("HP","Attack","Defense","Sp..Atk","Sp..Def","Speed") df <- df[1:167, columnList] #最適なrankを探索 for (i in 2:10){ print(i) result <- LRA(df,i) AIC(result) } #このときはAICが最小になるのは6 result <- LRA(df,6) #結果の確認 summary(result) #主成分得点の確認 plot(result) #各サンプルの所属確率を保存 r1=c() r2=c() r3=c() r4=c() r5=c() r6=c() rank=c() for ( i in 1:166){ r1 <- c(r1,result$res[i,1]) r2 <- c(r2,result$res[i,2]) r3 <- c(r3,result$res[i,3]) r4 <- c(r4,result$res[i,4]) r5 <- c(r5,result$res[i,5]) r6 <- c(r6,result$res[i,6]) rank <- c(rank,result$rank[i]) } out=data.frame(name,r1,r2,r3,r4,r5,r6,rank) #ファイル書き込み write.csv(out, "lraresult.csv")
潜在ランク理論ではランク数を指定する必要があります。 今回はランク数が6の時にAICが最も小さくなったので6つのランクに各ポケモンを振り分けました。
結果の主成分得点は以下のようになりました。 これは総合力みたいなものになります。

ランク3と4はほとんど値が同じですが、それ以外の部分に関してはランクに対して比較的線形に上がるようになっています。
次に各ランクの各パラメータの平均値を確認します。 基本的にはランクが上がると値が大きくなる傾向がありますが、 Defenceの1、2やSpeedの3,4など逆転しているものもあります。

いくつかのポケモンの各ランクへの所属確率をみてみます。
- ゲンガーの場合

ランク6の確率も少しありますが、ランク3の確率が大きく出ています。 すばやさととくこうは高いですが、あとの個体値は低いので妥当かもしれません。
- ブースターの場合

ランク4の確率も少しありますが、ランク6の確率が大きく出ています。 ポテンシャルは高いんだ。。。
また、各ランクに入るポケモンをいくつか抜粋した結果は以下のようになります。
| ランク | ポケモン |
|---|---|
| 1 | キャタピー、ビートル、ポッポ、ニドラン、ニャース、ズバット、シェルダー、コイキング |
| 2 | フシギダネ、ヒトカゲ、ピカチュー、パラス、ダグトリオ、コダック、ミニリュー、メタモン |
| 3 | リザード、バタフリー、ライチュー、パラセクト、ペルシアン、ゲンガー、ポリゴン、ハクリュー |
| 4 | サンドパン、ケーシィ、ユンゲラー、メノクラゲ、ゴースト、ベロリンガ、レアコイル、ラプラス |
| 5 | フシギバナ、リザードン、ピジョット、ゴルバット、ゴルダック、ゴーリキー、カイリキー、アズマオウ、ファイアー、ギャラドス |
| 6 | ミュウ、ミュウツー、カイリュー、サワムラー、カメックス、ブースター、ドククラゲ、フーディン、その他メガXX系 |
傾向としては、1 ,2が進化前、3,4が2段階進化の進化後や進化がないポケモン、5が3段階進化の最終進化系、6が伝説やメガXX系となっています。ただ、ランク5にファイヤーが入っていたり、ランク6にサワムラーやドククラゲが入っている部分など感覚に反するランク分けになったものもいくつかあります。この原因としては、潜在ランク理論は単純に数値の値の合計値でみているわけではなく、クラスタリングの1種なので、ランクが高いと判断されたポケモンにパラメータが近いと判断されたポケモンが、よりパラメータ平均は高いけどその傾向が他とは異なるポケモンよりも同じクラスに入りやすいのではないかと考えています。
まとめ
潜在ランク理論を用いてポケモンを6つのクラスにランク分けしました。 基本的にはランクが高い方が個体値の合計値が高い傾向がありますが、 潜在ランク理論はクラスタリングの性質をもつために、少し感覚と異なるランク付けをする可能性があることがわかりました。 おそらくランクを考えつつも同質なユーザをまとめることができるので、施策を考えるような用途には使いやすいかなと思いました。
今回使用したコードは以下