レーダーチャートつき成績個票
科目ごとの成績バランスを比較するには,レーダーチャートが有効です。成績個票に,レーダーチャートもつけてみましょう。ただ,得点のレーダーチャートを作るのは,科目によって難易度が異なる場合は必ずしも正確にバランス比較ができるとはいえません。そこで,得点を「標準化」します。すなわち,偏差値にするのです。
順に作っていきましょう。
なお,このページの下からデータがダウンロードできます。
================================================
偏差値を求める
偏差値を求めるために,平均値と標準偏差を求めます。不偏分散ではなく,分散で計算します。
---平均と標準偏差-----------------------------------------------------------------
dlist=Readcsv("seiseki.csv");
rown=length(dlist);
coln=length(transpose(dlist));
data=dlist_(2..rown);
kaisu=coln-2;
average=[];
sd=[];
repeat(kaisu,t,
test=select(column(data,2+t),#!="");
ninzu=length(test);
average=append(average,sum(test)/ninzu);
sd=append(sd,sqrt(sum(test,#^2)/ninzu-average_t^2));
);
-----------------------------------------------------------------------------------
次に,各学生の科目ごとの偏差値を求めます。
順位のときは,data の後ろに追加しましたが,ここでは別リストを用意する方法でやってみましょう。
---偏差値の算出---------------------------------------------------------------
sslist=[];
repeat(rown-1,s,
ss=[];
repeat(kaisu,t,
ss=append(ss,(data_s_(t+2)-average_t)/sd_t*10+50);
);
sslist=append(sslist,ss);
);
-----------------------------------------------------------------------------------
data_s_(t+2) が s番目の学生の t 科目めの得点です。
レーダーチャートを作る
レーダーチャートを作るのはちょっと手間がかかります。まず,網を描画します。ここでは半径5の円周上に点をとって,Listplot() で結ぶことにします。網の密度もいろいろ考えられますが,20ごとにとり,5本ということにします。一番外側は実線,内側は点線で表示します。
このような「ちょっと手間のかかるもの」は,関数化するのがよいでしょう。あとから変更したり,手直しをするときにやりやすくなります。関数化したら,通常は Initialization スロットにおきますが,いまは Draw スロットにそのまま置いておきます。
科目数は5ですが,増減したときのために,得点リストを dt として,length(dt) で科目数を取得します。これをdnとします。そのあとの手順は
(a) レーダーチャートの中心を決め,変数に代入する。
(b) 原点中心,半径1の円周上の点の座標をリストにする。[0,1]を始点にして反時計回りにとる。
(c) (b)のリストを使って,半径2,3,4,5の点の座標をリストにする。
(d) (a)(b)のリストを使ってListplot() で網を描く。ただし,1〜4番目は点線,5番目は実線。
(e) 中心からの線を点線で引く
(f) 目盛の数字を入れる
となります。
dn=length(dt);
center=[18,2]; // レーダーチャートの中心
p0=apply(1..(dn+1),[cos(pi/2+2*pi*(#-1)/dn),sin(pi/2+2*pi*(#-1)/dn)]); // 半径1の円周上の点
pt=apply(1..5,s,apply(p0,t,center+s*t)); // 半径5まで
repeat(4,s,Listplot(text(s),pt_s,["do"])); // 網を描く
Listplot("5",pt_5);
forall(1..dn,Listplot(text(#+5),[center,pt_5_#],["do"]));
Letter([center+[0,1],"e","20",center+[0,2],"e","40",center+[0,3],"e","60",center+[0,4],"e","80"]);
スクリプトの意味を確認しましょう。
Listplot() で閉じた図形を描くには,先頭と同じ点が最後になければなりません。そこで,p0=apply(1..(dn+1)・・ としています。
原点中心の円周上の点を p0 として,中心を移動したものを pt としています。(4行目)
ここで,最後の s*t の意味を説明します。
まず,apply がネストしていますので,実行変数は # ではなく,明示的に s,t としています。
このとき,sには 1,2,3,4,5 の整数が順にはいります。
中の apply( はp0 にたいして働くので,t は半径の円周上の点です。すると s*t は 整数×リスト ということになります。リストに実数(整数)をかけた結果は,ベクトルや行列と同じで,すべての要素に実数がかけられます。
ということで,pt=apply(1..5,s,apply(p0,t,center+s*t)); で,半径5までの円周上の点のリストのリストができます。
つぎに, forall(1..dn,Listplot(text(#+5),[center,pt_5_#],["do"])); についてです。
forall(リスト,処理)は,リストのすべての要素に対して処理を行うものです。したがって,科目数の分だけ,中心から円周上の点に線分を引いています。今までに5本 Listplot() で線を描いたので,番号が text(#+5) となっています。
これで,前図のような編目が描かれます。
次にデータのラインを入れます。半径1のp0を利用します。
ppt=apply(1..dn,center+dt_#/20*p0_#);
ppt=append(ppt,ppt_1); // データの位置
Listplot("dt",ppt,["dr,2"]); // データのライン
1行目で科目数分の位置を求めますが,Listplot() で描くには閉じたものにしなくていはいけないので,先頭の要素を末尾に加えています。
最後に,外側に科目名を表示します。科目名はrowindex の第3要素以降です。
subj=flatten(apply(1..dn,[center+5.5*p0_#,"c",dlist_1_(#+2)]));
Letter(subj);
sunj はLetter で表示するためのリストです。Letter では,位置,方向,文字 をワンセットとして,表示する分だけ並べます。apply(・・・)によって,科目分の 位置,ewsn,文字 のリストのリスト(2次元)ができます。
これを flatten によって1次元のリストにします。
これでレーダーチャートは完成。全体を関数化します。引数は一人分の偏差値のリストです。
--レーダーチャートを描く関数 -----------------------------------------------------
radar(dt):=(
regional(dn,center,pt,ppt);
dn=length(dt);
center=[18,2]; // レーダーチャートの中心
p0=apply(1..(dn+1),[cos(pi/2+2*pi*(#-1)/dn),sin(pi/2+2*pi*(#-1)/dn)]); // 半径1の円周上の点 [0,1] から反時計回りで閉じる
pt=apply(1..5,s,apply(p0,t,center+s*t)); // 半径5まで
repeat(4,s,Listplot(text(s),pt_s,["do"])); // 網を描く
Listplot("5",pt_5);
forall(1..dn,Listplot(text(#+5),[center,pt_5_#],["do"]));
Letter([center+[0,1],"e","20",center+[0,2],"e","40",center+[0,3],"e","60",center+[0,4],"e","80"]);
ppt=apply(1..dn,center+dt_#/20*p0_#);
ppt=append(ppt,ppt_1); // データの位置
Listplot("dt",ppt,["dr,2"]); // データのライン
// 科目名を表示
subj=flatten(apply(1..dn,[center+5.5*p0_#,"c",dlist_1_(#+2)]));
Letter(subj);
);
-----------------------------------------------------------------------------------
成績個票を作る
成績表とレーダーチャートを並べた個票を作ります。
成績表については,「成績処理」の個票のところを参照してください。
順位が偏差値に変わり,偏差値を別リストとしましたので,そこを変更するだけです。
--個票出力-----------------------------------------------------------------------------------
Yoko=apply(1..(coln-1),20);
Tate=apply(1..4,10);
Tabledatalight("",Yoko,Tate,[],[0,"Rng=n"]);
Putrow(1,"c",["科目"] ++ dlist_1_(3..coln));
PutcoL(1,"c",["","得点","偏差値","平均"]);
mf(t):=(
Letter([[0,5],"e",data_t_1+" "+data_t_2]);
repeat(kaisu,s,
Putcell(s+1,2,"r",data_t_(s+2)+" ");
Putcell(s+1,3,"r",Sprintf(sslist_t_s,1)+" ");
Putcell(s+1,4,"r",Sprintf(average_s,1)+" ");
);
radar(sslist_t);
);
Setpara("seiseki","mf(t)","t=[1,"+(rown-1)+"]",["Div="+(rown-2)], ["Title=テスト結果"]);
--------------------------------------------------------------------------------------------
全体が大きくなるので,全体のサイズを Setunitlen("6mm"); で縮小します。40人分のレーダーチャート付き個票を作るのはかなり時間がかかるので焦らずに待ってください。
あらためてスクリプト全体を示します。
Addax(0);
Setunitlen("6mm");
// データ読み込み
dlist=Readcsv("seiseki.csv");
rown=length(dlist);
coln=length(transpose(dlist));
data=dlist_(2..rown);
kaisu=coln-2;
//平均,標準偏差計算
average=[];
sd=[];
repeat(kaisu,t,
test=select(column(data,2+t),#!="");
ninzu=length(test);
average=append(average,sum(test)/ninzu);
sd=append(sd,sqrt(sum(test,#^2)/ninzu-average_t^2));
);
// 偏差値計算
sslist=[];
repeat(rown-1,s,
ss=[];
repeat(kaisu,t,
ss=append(ss,(data_s_(t+2)-average_t)/sd_t*10+50);
);
sslist=append(sslist,ss);
);
// レーダーチャートを描く関数
radar(dt):=(
regional(dn,center,pt,ppt);
dn=length(dt);
center=[18,2]; // レーダーチャートの中心
p0=apply(1..(dn+1),[cos(pi/2+2*pi*(#-1)/dn),sin(pi/2+2*pi*(#-1)/dn)]); // 半径1の円周上の点 [0,1] から反時計回りで閉じる
pt=apply(1..5,s,apply(p0,t,center+s*t)); // 半径5まで
repeat(4,s,Listplot(text(s),pt_s,["do"])); // 網を描く
Listplot("5",pt_5);
forall(1..dn,Listplot(text(#+5),[center,pt_5_#],["do"]));
Letter([center+[0,1],"e","20",center+[0,2],"e","40",center+[0,3],"e","60",center+[0,4],"e","80"]);
ppt=apply(1..dn,center+dt_#/20*p0_#);
ppt=append(ppt,ppt_1); // データの位置
Listplot("dt",ppt,["dr,2"]); // データのライン
// 科目名を表示
subj=flatten(apply(1..dn,[center+5.5*p0_#,"c",dlist_1_(#+2)]));
Letter(subj);
);
// 作表
Yoko=apply(1..(coln-1),20);
Tate=apply(1..4,10);
Tabledatalight("",Yoko,Tate,[],[0,"Rng=n"]);
Putrow(1,"c",["科目"] ++ dlist_1_(3..coln));
PutcoL(1,"c",["","得点","偏差値","平均"]);
// アニメーション設定
mf(t):=(
Letter([[0,5],"e",data_t_1+" "+data_t_2]);
repeat(kaisu,s,
Putcell(s+1,2,"r",data_t_(s+2)+" ");
Putcell(s+1,3,"r",Sprintf(sslist_t_s,1)+" ");
Putcell(s+1,4,"r",Sprintf(average_s,1)+" ");
);
radar(sslist_t);
);
Setpara("seiseki","mf(t)","t=[1,"+(rown-1)+"]",["Div="+(rown-2)], ["Title=テスト結果"]);
Slider("A-C-B",[-2,10],[-2,0]);
mf(floor(|A,C|/|A,B|*(rown-1))+1);