久々の更新となってしまいました。
近頃、現場運営表作りに関するご相談を多く頂きます。
この辺の話題ですね。
運営表作りの中で、長年の経験を必要とされるのがレンタル器材のサイズ決め。
足のサイズからフィンのサイズを決めたり、体重からBCのサイズを決めるぐらいであれば、以下の様に関数でも実現可能です。
変化する量(足のサイズ、体重)が1次元の場合ですね。
しかし、ウエットスーツの場合、こうは行きません。
同じ170cmでも、60kg無い人もいれば、80kgある人もいます。
変化する量が2つになるだけで、話は一気にややこしくなりますね。
ということで、以前僕は、こんなことを考えました。
あるデータをいくつかの集団に分類する。
その式はブラックボックスながら、これは今流行のAIで活用される、ディープラーニングがお得意の分野です。
その世界では分類器と呼ばれます。
例えば、この分野で例示されるのはなぜか昔から『アヤメ』なのですが、複数のアヤメの品種を分類する、という例がよく示されます。
これは、アヤメの花弁のサイズやがくのサイズなどをデータ化し、それを分類しています。
これを応用しまくったのが、顔の検出システムだったりします。
話を戻しましょう。
実は、以前少しディープラーニングの勉強をした際、手元にある約700人分の身長×体重データを用いて、ウエットのサイズ判定器を作ろうと考えました。
結果…
ディープラーニング自体に挫折しました…
それからしばらくの間、レンタルウエットスーツのサイズ判定問題からは遠ざかっていたのですが、今回ご相談を頂きましたので、再考してみました。
仕組み
今回はディープラーニングなんて大それた物は持ちだしません。
ふと、今年の冬に作った物を思い出しました。
この採寸補助ツールでは
身長×体重×性別と、性別ごとの採寸データの標準値における身長×体重を比較し、誤差の少ない体形の数値を基準値として表示する
という機能を構築しました。
この標準値サイズがキッチリと日本人男性の身長×体重をカバーしていれば、これを応用できるのでは?
ということで手元のデータを比較検証。
手元の約700件の身長×体重データのうち、男性で67%、女性では93%ものデータが標準値に収まっていました。
男性に一抹の不安が残りますが、このまま突き進んでみましょう。
今回の仕組みはこんな感じです。
- 身長×体重×性別から基準値を割り出す
- 割り出した基準値の胸囲、腹囲などを抽出(推定胸囲、腹囲)
- 推定胸囲、推定腹囲などをレンタルウエットの胸囲、腹囲などと比較
- 誤差が最も少ないものを正解とする
1、2は構築済み、しかもjavascriptで構築しているので、Googleスプレッドシートで複雑な操作をするためのGoogleAppsScriptとの相性も抜群のはず。
ということで、ザクザクっと手を動かすこと2時間?3時間?
ひとまずプロトタイプが完成しました。
今回は、テストとしてmobby’sさんのレンタルウエットのデータを用いて判別してみました。
結果
mobby’sさんのレンタルウエットの展開は以下の通りです。
男性 | ||||||
サイズ名 | 身長 | 体重 | 上胸囲 | 胸囲 | 腹囲 | 尻囲 |
M | 165 | 65 | 94 | 90.5 | 80 | 93 |
ML | 170 | 70 | 94 | 92.5 | 82 | 94 |
L | 175 | 75 | 98 | 94.5 | 84 | 93 |
XL | 180 | 80 | 100 | 96.5 | 86 | 98 |
女性 | ||||||
サイズ名 | 身長 | 体重 | 上胸囲 | 胸囲 | 腹囲 | 尻囲 |
XS | 145 | 45 | 80 | 81 | 63 | 88 |
S | 150 | 50 | 82 | 83 | 66 | 90 |
MS | 155 | 55 | 84 | 85 | 68 | 93 |
M | 160 | 57 | 85 | 86 | 68 | 93 |
L | 165 | 62 | 87 | 88 | 70 | 95 |
自分の感覚値としては以下の様な感じです。
- 男性用
- M
~170cm - ML
170cm~175cm
175cm~178cmで細い人 - L
175cm前後でガタイの良い人
178cm~ - XL
180cm前後かつガタイの良い人
- M
- 女性用
- XS
通常は使わない - S
~155cm - MS
155cm~165cm。万能 - M
160cm~でややふっくらした人
165cm~ - L
通常は使わない
- XS
結果は以下の通り。
うーん、思ったより…
というか、グラフの縦軸の取り方が微妙ですね…
そして、計算結果としては、身長の影響が色濃く出てしまっている様に感じます。
原因としては
身長、体重、上胸囲、胸囲、腹囲、尻囲を比較しているのですが、よくよく考えると、身長以外は比較的体重に依存しやすい数値なんですよね。
説明変数の多重共線性ってやつですね。
ただ、2つのサイズの境界線なんかは比較的良く表現で来ていたので、プロトタイプとしてはまずまずかなと思います。
体重だけ単位が違うので体重だけ削ってみる。
それぞれの数値に重みづけをする。
こんな修正を今後、加えてみようと思います。
グラフも見やすくした上で、試行錯誤の結果をご報告できればと思います。
ちなみに、違うメーカーのレンタルウエットであっても、基準サイズさえ分かればサクッと対応可能です。
※実はGULLバージョンも作ってあったり…
実装(参考)
参考&自分の覚書として、実際のコードを載せておきます。
function wet_size(sex,tall,weight){
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(sex == '男性'){
var sheet = ss.getSheetByName('MEN');
}else{
var sheet = ss.getSheetByName('WOMEN');
}
var lastcol = sheet.getLastColumn();
var tall_data = sheet.getRange(1, 1 , 1 ,lastcol).getValues();
var weight_data = sheet.getRange(2, 1 , 1 , lastcol).getValues();
var dif = new Array();
for(var i = 0;i<lastcol;i++){
dif.push(Math.abs(tall_data[0][i] - tall)+Math.abs(weight_data[0][i] - weight));
}
var difIndex = dif.indexOf(Math.min.apply(null,dif));
var data = sheet.getRange(9,difIndex+1,5,1).getValues();
var chest = data[0][0];
var bust = data[1][0];
var waist = data[2][0];
var hip = data[4][0];
if(sex == '男性'){
var wetSheet = ss.getSheetByName('WET_M');
var sizecnt = 4;
}else{
var wetSheet = ss.getSheetByName('WET_F');
var sizecnt = 5;
}
var wetdata = wetSheet.getRange(1,2,sizecnt,6).getValues();
var wettall = new Array();
for(var c = 0;c<sizecnt;c++){
wettall.push(wetdata[c][0]);
}
var wetweight = new Array();
for(var c = 0;c<sizecnt;c++){
wetweight.push(wetdata[c][1]);
}
var wetchest = new Array();
for(var c = 0;c<sizecnt;c++){
wetchest.push(wetdata[c][2]);
}
var wetbust = new Array();
for(var c = 0;c<sizecnt;c++){
wetbust.push(wetdata[c][3]);
}
var wetwaist = new Array();
for(var c = 0;c<sizecnt;c++){
wetwaist.push(wetdata[c][4]);
}
var wethip = new Array();
for(var c = 0;c<sizecnt;c++){
wethip.push(wetdata[c][5]);
}
var wetdif = new Array();
for(var j = 0;j<sizecnt;j++){
wetdif.push(Math.abs(wettall[j]-tall)+Math.abs(wetchest[j]-chest)+Math.abs(wetbust[j]-bust)+Math.abs(wetwaist[j]-waist)+Math.abs(wethip[j]-hip)+Math.abs(wetweight[j]-weight));
}
var wetdifIndex = wetdif.indexOf(Math.min.apply(null,wetdif));
var size = wetSheet.getRange(wetdifIndex + 1 , 1).getValue();
return size;
}