Skip to content

Instantly share code, notes, and snippets.

@Getaji
Last active July 13, 2022 04:13
Show Gist options
  • Save Getaji/942e7d92de04169cde95b56ae1e1f3a7 to your computer and use it in GitHub Desktop.
Save Getaji/942e7d92de04169cde95b56ae1e1f3a7 to your computer and use it in GitHub Desktop.
Mocha-Repositoryの楽曲ページでLevel Reviewの平均、中央値、範囲、分散、標準偏差を表示するスクリプト

スクリプトの最終更新: 2022/07/13 01:12 (v1.0.2)

説明

Mocha-Repositoryの楽曲ページでLevel Reviewの平均、中央値、範囲(最低Lvと最高Lv)、分散、標準偏差、変動係数を計算するスクリプトです。実行するとアラートポップアップで表示します。

image

  • Mocha-Repositoryにログインする必要があります。
  • 一部ブラウザで動かない可能性があります。
    • IEはサポートしません。Safariは不明です(確認環境がない)
    • スマホ版の一部ブラウザ(Chromeなど)では、ブックマークをアドレスバー経由で(ブクマ名を入れて)呼び出すと動作します。
  • 小数部は下4桁まで表示され、それ以下は四捨五入されます。
  • 通信を行わず表示中のページから計算するので、最新のデータではない可能性があります。
  • Webブラウザのフォントによって表示が崩れる可能性があります。
  • ページの構造が変化すると動作しなくなる可能性があります。
  • ページの譜面情報欄に情報を追加する形式のスクリプトを作る予定はありません。
    • 通信しないとはいえページを改変するスクリプトを公開したくないので……
  • 作者はMocha-Repositoryの管理人ではありません。このスクリプトについて管理人に問い合わせるのはおやめください。

ブックマークレット用コード

以下の1行をURLとしてWebブラウザのブックマークに追加すると、1クリックで利用できます。追加時に先頭の javascript: が抜けることがあるのでご注意ください。アドレスバーに直接貼り付けて実行することもできます。

javascript:(()=>{let c=a=>Number.isInteger(a)?a:Number(a.toFixed(4)),b=a=>doubleplay?a>=12?"\u2605"+c(a-11):"\u2606"+c(a):a>=13?"\u2605"+c(a-12):"\u2606"+c(a),a=scores.map(a=>a.level).filter(a=>a>0),e=a.length;if(!e){alert("\u8A08\u7B97\u3067\u304D\u307E\u305B\u3093\u3002\u4EE5\u4E0B\u306E\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059:\n\u30FB\u697D\u66F2\u30DA\u30FC\u30B8\u3067\u306F\u306A\u3044\n\u30FB\u30ED\u30B0\u30A4\u30F3\u3057\u3066\u3044\u306A\u3044\n\u30FB\u30EC\u30D3\u30E5\u30FC\u304C1\u4EF6\u3082\u306A\u3044\n\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u306E\u4E0D\u5177\u5408(\u304A\u554F\u3044\u5408\u308F\u305B\u304F\u3060\u3055\u3044)");return}let f=a.reduce((a,b)=>a+b,0)/a.length,g=(()=>{let b=[...a].sort((a,b)=>a-b),c=Math.floor(e/2);return e%2?b[c]:(b[c-1]+b[c])/2})(),h=Math.min(...a),i=Math.max(...a),j=`${b(h)}~${b(i)}`,d=a.reduce((a,b)=>a+Math.pow(b-f,2),0)/e,k=[document.querySelector("h1").textContent.trim(),a.length+"\u4EF6\u306E\u30EC\u30D3\u30E5\u30FC","\u5E73\u5747\u3000\u3000 : "+b(f),"\u4E2D\u592E\u5024\u3000 : "+b(g),"\u7BC4\u56F2\u3000\u3000 : "+j,"\u5206\u6563\u3000\u3000 : "+c(d),"\u6A19\u6E96\u504F\u5DEE : "+c(Math.sqrt(d)),"\u5909\u52D5\u4FC2\u6570 : "+c(Math.sqrt(d)/f),].join("\n");alert(k)})()

表示を揃えないバージョンはこちらです。

javascript:(()=>{let c=a=>Number.isInteger(a)?a:Number(a.toFixed(4)),b=a=>doubleplay?a>=12?"\u2605"+c(a-11):"\u2606"+c(a):a>=13?"\u2605"+c(a-12):"\u2606"+c(a),a=scores.map(a=>a.level).filter(a=>a>0),e=a.length;if(!e){alert("\u8A08\u7B97\u3067\u304D\u307E\u305B\u3093\u3002\u4EE5\u4E0B\u306E\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059:\n\u30FB\u697D\u66F2\u30DA\u30FC\u30B8\u3067\u306F\u306A\u3044\n\u30FB\u30ED\u30B0\u30A4\u30F3\u3057\u3066\u3044\u306A\u3044\n\u30FB\u30EC\u30D3\u30E5\u30FC\u304C1\u4EF6\u3082\u306A\u3044\n\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u306E\u4E0D\u5177\u5408(\u304A\u554F\u3044\u5408\u308F\u305B\u304F\u3060\u3055\u3044)");return}let f=a.reduce((a,b)=>a+b,0)/a.length,g=(()=>{let b=[...a].sort((a,b)=>a-b),c=Math.floor(e/2);return e%2?b[c]:(b[c-1]+b[c])/2})(),h=Math.min(...a),i=Math.max(...a),j=`${b(h)}~${b(i)}`,d=a.reduce((a,b)=>a+Math.pow(b-f,2),0)/e,k=[document.querySelector("h1").textContent.trim(),a.length+"\u4EF6\u306E\u30EC\u30D3\u30E5\u30FC","\u5E73\u5747: "+b(f),"\u4E2D\u592E\u5024: "+b(g),"\u7BC4\u56F2: "+j,"\u5206\u6563: "+c(d),"\u6A19\u6E96\u504F\u5DEE: "+c(Math.sqrt(d)),"\u5909\u52D5\u4FC2\u6570: "+c(Math.sqrt(d)/f),].join("\n");alert(k)})()

備考1: Level Reviewの捉え方

このスクリプトの活用方法としては「大半が☆11.5をつけてて一部が☆10をつけている」といったケースで、より多数側に寄せた評価を見る用途などが考えられます。

ここで補足しておきたいのは、Level Review自体「多数の人が付けている評価が必ず正しい」というものではないということです。例えば上級者がその譜面の適正レベルを見誤ることなども起こり得ます。

なので、今後Level Reviewしようと思っている方は難易度表や他の方の評価を気にしすぎず、自身の感じた難易度を投票しましょう。先述のケースも「分散の値が大きいので個人差が大きいか適正レベルのプレイヤーにとって感じ方が違う可能性もある」などと考えることもできます。

備考2: 各項目の説明と補足

特別なことは書いてないので見なくても問題ありません。

平均

  • すべての値を足して割った値(算術平均)。
  • Mocha-Repositoryが標準で表示しているのは多分これ。
  • 平均にはいくつかの種類があり、他には幾何平均などがある。

中央値

  • 値を順番に並べたときの中央の値。
  • 値の数が偶数の場合は中央の2つの値を足して2で割る。
  • レベルレビューの性質を考えると、偶数の場合は両方とも記載するのがいいかもしれない。検討中。

範囲

  • 統計学の用語では最小値と最大値の差のことだが、このスクリプトでは最小値と最大値を一般的な範囲の表記にしている。

分散

  • 値の散らばり具合を表す値の一種。大きいほど値が散らばっている。すべてが同じ値なら0になる。
  • 平均値からの偏差の自乗の平均で求められる。
  • また、標準偏差の自乗で求めることもできる。
  • 標準偏差よりも数学的に扱いやすい。

標準偏差

  • 値の散らばり具合を表す値の一種。読み取り方は分散と大体同じ。
  • 分散の平方根で求めることができる。
  • 分散は計算過程で2乗していて元の値と単位が異なってしまうので、標準偏差を使うことで理解しやすくなったり平均との差などが計算しやすくなったりする。

変動係数

  • 値の散らばり具合を表す係数。
  • 標準偏差を平均値で割ることで求められる。
  • 平均値に対するデータとばらつきの関係を相対的に評価することができる。

計算ミス、動作不良、スクリプト自体の問題等ありましたら Twitter でお問い合わせください。

以下はソースコードです。改変自由です。

(()=>{let c=a=>Number.isInteger(a)?a:Number(a.toFixed(4)),b=a=>doubleplay?a>=12?"\u2605"+c(a-11):"\u2606"+c(a):a>=13?"\u2605"+c(a-12):"\u2606"+c(a),a=scores.map(a=>a.level).filter(a=>a>0),e=a.length;if(!e){alert("\u8A08\u7B97\u3067\u304D\u307E\u305B\u3093\u3002\u4EE5\u4E0B\u306E\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059:\n\u30FB\u697D\u66F2\u30DA\u30FC\u30B8\u3067\u306F\u306A\u3044\n\u30FB\u30ED\u30B0\u30A4\u30F3\u3057\u3066\u3044\u306A\u3044\n\u30FB\u30EC\u30D3\u30E5\u30FC\u304C1\u4EF6\u3082\u306A\u3044\n\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u306E\u4E0D\u5177\u5408(\u304A\u554F\u3044\u5408\u308F\u305B\u304F\u3060\u3055\u3044)");return}let f=a.reduce((a,b)=>a+b,0)/a.length,g=(()=>{let b=[...a].sort((a,b)=>a-b),c=Math.floor(e/2);return e%2?b[c]:(b[c-1]+b[c])/2})(),h=Math.min(...a),i=Math.max(...a),j=`${b(h)}~${b(i)}`,d=a.reduce((a,b)=>a+Math.pow(b-f,2),0)/e,k=[document.querySelector("h1").textContent.trim(),a.length+"\u4EF6\u306E\u30EC\u30D3\u30E5\u30FC","\u5E73\u5747\u3000\u3000 : "+b(f),"\u4E2D\u592E\u5024\u3000 : "+b(g),"\u7BC4\u56F2\u3000\u3000 : "+j,"\u5206\u6563\u3000\u3000 : "+c(d),"\u6A19\u6E96\u504F\u5DEE : "+c(Math.sqrt(d)),"\u5909\u52D5\u4FC2\u6570 : "+c(Math.sqrt(d)/f),].join("\n");alert(k)})()
(()=>{let c=a=>Number.isInteger(a)?a:Number(a.toFixed(4)),b=a=>doubleplay?a>=12?"\u2605"+c(a-11):"\u2606"+c(a):a>=13?"\u2605"+c(a-12):"\u2606"+c(a),a=scores.map(a=>a.level).filter(a=>a>0),e=a.length;if(!e){alert("\u8A08\u7B97\u3067\u304D\u307E\u305B\u3093\u3002\u4EE5\u4E0B\u306E\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059:\n\u30FB\u697D\u66F2\u30DA\u30FC\u30B8\u3067\u306F\u306A\u3044\n\u30FB\u30ED\u30B0\u30A4\u30F3\u3057\u3066\u3044\u306A\u3044\n\u30FB\u30EC\u30D3\u30E5\u30FC\u304C1\u4EF6\u3082\u306A\u3044\n\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u306E\u4E0D\u5177\u5408(\u304A\u554F\u3044\u5408\u308F\u305B\u304F\u3060\u3055\u3044)");return}let f=a.reduce((a,b)=>a+b,0)/a.length,g=(()=>{let b=[...a].sort((a,b)=>a-b),c=Math.floor(e/2);return e%2?b[c]:(b[c-1]+b[c])/2})(),h=Math.min(...a),i=Math.max(...a),j=`${b(h)}~${b(i)}`,d=a.reduce((a,b)=>a+Math.pow(b-f,2),0)/e,k=[document.querySelector("h1").textContent.trim(),a.length+"\u4EF6\u306E\u30EC\u30D3\u30E5\u30FC","\u5E73\u5747: "+b(f),"\u4E2D\u592E\u5024: "+b(g),"\u7BC4\u56F2: "+j,"\u5206\u6563: "+c(d),"\u6A19\u6E96\u504F\u5DEE: "+c(Math.sqrt(d)),"\u5909\u52D5\u4FC2\u6570: "+c(Math.sqrt(d)/f),].join("\n");alert(k)})()
(() => {
// 小数部の下5桁以降を四捨五入
const fixNumber = (n) => Number.isInteger(n) ? n : Number((n).toFixed(4));
// レベルの数値を通常/発狂レベル表記の文字列に変換する
const formatLevel = (v) => {
if (doubleplay) {
return v >= 12 ? "★" + fixNumber(v - 11) : "☆" + fixNumber(v);
}
return v >= 13 ? "★" + fixNumber(v - 12) : "☆" + fixNumber(v);
}
// すべてのレビューのレベル値
const reviews = scores.map((score) => score.level).filter((lv) => lv > 0);
// 件数
const num = reviews.length;
if (!num) {
alert("計算できません。以下の可能性があります:\n・楽曲ページではない\n・ログインしていない\n・レビューが1件もない\n・スクリプトの不具合(お問い合わせください)");
return;
}
// 平均
const average = reviews.reduce((a, c) => a + c, 0) / reviews.length;
// 中央値
const median = (() => {
const src = [...reviews].sort((a, b) => a - b);
const half = Math.floor(num / 2);
return num % 2 ? src[half] : (src[half - 1] + src[half]) / 2.0;
})();
// 範囲
const min = Math.min(...reviews);
const max = Math.max(...reviews);
const range = `${formatLevel(min)}~${formatLevel(max)}`;
// 分散
const variance = reviews.reduce((a, c) => a + Math.pow(c - average, 2), 0) / num;
const s = [
document.querySelector("h1").textContent.trim(),
reviews.length + "件のレビュー",
"平均   : " + formatLevel(average),
"中央値  : " + formatLevel(median),
"範囲   : " + range,
"分散   : " + fixNumber(variance),
"標準偏差 : " + fixNumber(Math.sqrt(variance)),
"変動係数 : " + fixNumber(Math.sqrt(variance) / average),
].join("\n");
alert(s);
})()
(() => {
// 小数部の下5桁以降を四捨五入
const fixNumber = (n) => Number.isInteger(n) ? n : Number((n).toFixed(4));
// レベルの数値を通常/発狂レベル表記の文字列に変換する
const formatLevel = (v) => {
if (doubleplay) {
return v >= 12 ? "★" + fixNumber(v - 11) : "☆" + fixNumber(v);
}
return v >= 13 ? "★" + fixNumber(v - 12) : "☆" + fixNumber(v);
}
// すべてのレビューのレベル値
const reviews = scores.map((score) => score.level).filter((lv) => lv > 0);
// 件数
const num = reviews.length;
if (!num) {
alert("計算できません。以下の可能性があります:\n・楽曲ページではない\n・ログインしていない\n・レビューが1件もない\n・スクリプトの不具合(お問い合わせください)");
return;
}
// 平均
const average = reviews.reduce((a, c) => a + c, 0) / reviews.length;
// 中央値
const median = (() => {
const src = [...reviews].sort((a, b) => a - b);
const half = Math.floor(num / 2);
return num % 2 ? src[half] : (src[half - 1] + src[half]) / 2.0;
})();
// 範囲
const min = Math.min(...reviews);
const max = Math.max(...reviews);
const range = `${formatLevel(min)}~${formatLevel(max)}`;
// 分散
const variance = reviews.reduce((a, c) => a + Math.pow(c - average, 2), 0) / num;
const s = [
document.querySelector("h1").textContent.trim(),
reviews.length + "件のレビュー",
"平均: " + formatLevel(average),
"中央値: " + formatLevel(median),
"範囲: " + range,
"分散: " + fixNumber(variance),
"標準偏差: " + fixNumber(Math.sqrt(variance)),
"変動係数: " + fixNumber(Math.sqrt(variance) / average),
].join("\n");
alert(s);
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment