Advertisement
JadedDragoon

Property Grids

Jul 1st, 2018
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. function propGrids() {
  2.   const ssh = SpreadsheetApp.getActiveSpreadsheet();
  3.   const _   = LodashGS.load();
  4.  
  5.   const sizeTable = { range: ssh.getRangeByName('size_table') };
  6.   [ sizeTable.cols,
  7.     sizeTable.rows,
  8.     sizeTable.values
  9.   ] = getXRefs_(sizeTable.range.getValues(), _);
  10.   sizeTable.cols = deName(sizeTable.cols);
  11.   sizeTable.rows = deName(sizeTable.rows);
  12.  
  13.   const glowTable = { range: ssh.getRangeByName('glow_table') };
  14.   [ glowTable.cols,
  15.     glowTable.rows,
  16.     glowTable.values
  17.   ] = getXRefs_(glowTable.range.getValues(), _);
  18.   glowTable.cols = deName(glowTable.cols);
  19.   glowTable.rows = deName(glowTable.rows);
  20.  
  21.   sizeTable.xRows = glowTable.rows;
  22.   glowTable.xRows = sizeTable.rows;
  23.  
  24.   const recentTable = { range: ssh.getRangeByName('recent_data') };
  25.   [ recentTable.cols,
  26.     recentTable.values
  27.   ] = prepRecent(recentTable.range.getValues());
  28.  
  29.   const distMatrix = distribAdjust_(
  30.     recentTable,
  31.     sizeTable.rows,
  32.     glowTable.rows,
  33.     _
  34.   );
  35.  
  36.   const newValues = doFormulas_(sizeTable, glowTable, recentTable, distMatrix, _);
  37.  
  38.   sizeTable.range.offset(
  39.     1,
  40.     1,
  41.     _.size(sizeTable.rows) / 2,
  42.     _.size(sizeTable.cols) / 2
  43.   ).setValues(newValues.size);
  44.  
  45.   glowTable.range.offset(
  46.     1,
  47.     1,
  48.     _.size(glowTable.rows) / 2,
  49.     _.size(glowTable.cols) / 2
  50.   ).setValues(newValues.glow);
  51.  
  52.   function prepRecent(values) {
  53.     var superHead;
  54.     return [
  55.       getColumnObj_(_.map(values[1], function(val, index) {
  56.         if (values[0][index] != '') { superHead = values[0][index]; }
  57.         return (superHead + " - " + val).toLowerCase();
  58.       })),
  59.       _.slice(values,2)
  60.     ]
  61.   }
  62.  
  63.   function deName(object) {
  64.     const working = {};
  65.     _.forOwn(object, function(val, key) {
  66.       if (isFinite(val) && val != 0) { working[key]               = val-1; }
  67.       if (isFinite(key) && key != 0) { working[_.toString(key-1)] = val;   }
  68.     });
  69.     return working;
  70.   }
  71. }
  72.  
  73. function doFormulas_(sizeTable, glowTable, recentTable, distMatrix, _) {
  74.   const
  75.     dataTables = {
  76.       size: sizeTable,
  77.       glow: glowTable
  78.     },
  79.     xProps = {
  80.       size: 'glow',
  81.       glow: 'size'
  82.     },
  83.     order = [
  84.       'points'
  85.       ,'% chance'
  86.       ,'kept data points'
  87.       ,'w-avg. value per point'
  88.       ,'w-avg. value'
  89.       ,'median value'
  90.       ,'values trend'
  91.     ],
  92.     outp = {
  93.       size: undefined,
  94.       glow: undefined
  95.     },
  96.     mGetTrendData = _.memoize(getTrendData);
  97.  
  98.   _.forOwn(dataTables, function(propObj, prop) {
  99.     const rows  = propObj.rows;
  100.     const cols  = propObj.cols;
  101.     const xRows = propObj.xRows;
  102.    
  103.     var rowCount = 0, colCount = 0;
  104.     while (!_.isUndefined(rows[rowCount])) { rowCount++; }
  105.     while (!_.isUndefined(cols[colCount])) { colCount++; }
  106.     outp[prop] = Array(rowCount);
  107.     for (var i = 0; i < rowCount; i++) {
  108.       outp[prop][i] = Array(colCount);
  109.     }
  110.    
  111.     _.forEach(order, function(colKey) {
  112.       for (var i = 0; i < rowCount; i++) {
  113.         outp[prop][i][cols[colKey]] = doMath(
  114.           colKey,
  115.           rows[i],
  116.           prop,
  117.           xRows,
  118.           xProps,
  119.           rowCount,
  120.           rows,
  121.           outp
  122.         );
  123.       }
  124.     });
  125.   });
  126.  
  127.   return outp;
  128.  
  129.   function doMath(colKey, rowKey, prop, xRows, xProps, rowCount, rows, outp) {
  130.     switch(colKey) {
  131.       case 'points':
  132.         return points_(getSGLvl_(rowKey));
  133.        
  134.       case '% chance':
  135.         return getOddsSG_(getSGLvl_(rowKey));
  136.        
  137.       case 'kept data points':
  138.         return keptDataPoints_(distMatrix[prop][rowKey], _);
  139.        
  140.       case 'w-avg. value per point':
  141.       case 'w-avg. value':
  142.       case 'median value':
  143.         return valueCalc_(
  144.           prop,
  145.           xProps[prop],
  146.           rowKey,
  147.           xRows,
  148.           distMatrix[prop][rowKey],
  149.           recentTable,
  150.           points_(getSGLvl_(rowKey)),
  151.           _
  152.         )[colKey];
  153.        
  154.       case 'values trend':
  155.         //return null;
  156.         const getTrend = mGetTrendData(prop, outp, rowCount, 5, _);
  157.         return getTrend(_.unzip(outp[prop])[dataTables[prop].cols['points']][rows[rowKey]]);
  158.     }
  159.   }
  160.  
  161.   function getTrendData(prop, outp, rowCount, minSampleSize, _) {
  162.     const outpProp = _.unzip(outp[prop]);
  163.     return LeastSquares_(
  164.       _.filter(_.times(rowCount, function(i) {
  165.         return outpLookup(outpProp, 'points')[i];
  166.       }), function(value, i) {
  167.         if (outpLookup(outpProp, 'kept data points')[i] > minSampleSize) { return true; }
  168.       }),
  169.       _.filter(_.map(_.unzip([
  170.         outpLookup(outpProp, 'w-avg. value'),
  171.         outpLookup(outpProp, 'median value')
  172.       ]), function (pair) {
  173.         return _.mean(pair);
  174.       }), function(value, i) {
  175.         if (outpLookup(outpProp, 'kept data points')[i] > minSampleSize) { return true; }
  176.       })
  177.     )
  178.    
  179.     function outpLookup(outpProp, key) { return outpProp[dataTables[prop].cols[key]]; }
  180.   }
  181. };
  182.  
  183. var oddsSG_ = [
  184.   0.500488759,
  185.   0.250244379,
  186.   0.12512219,
  187.   0.062561095,
  188.   0.031280547,
  189.   0.015640274,
  190.   0.007820137,
  191.   0.003910068,
  192.   0.001955034,
  193.   0.000977517
  194. ];
  195.  
  196. function points_(level) {
  197.   return Math.pow(level, 2);
  198. }
  199.  
  200. function getOddsSG_(level) {
  201.   if (level < 1) { level = 1; }
  202.   return oddsSG_[level-1];
  203. }
  204.  
  205. function multiByOdds_(odds, _) {
  206.   _ = _ || LodashGS.load();
  207.  
  208.   return _.map(odds, function(value) {
  209.     return _.map(odds, function(value2) {
  210.       return (value2 / value);
  211.     })
  212.   });
  213. }
  214.  
  215. function getSGLvl_(name) {
  216.   return {
  217.     'none':        0,
  218.     'small':       1,
  219.     'dusky':       2,
  220.     'average':     2,
  221.     'lucent':      3,
  222.     'large':       3,
  223.     'bright':      4,
  224.     'massive':     4,
  225.     'brilliant':   5,
  226.     'enormous':    5,
  227.     'radiant':     6,
  228.     'gigantic':    6,
  229.     'dazzling':    7,
  230.     'staggering':  7,
  231.     'starlike':    8,
  232.     'monumental':  8,
  233.     'crown jewel': 9,
  234.     'colossal':    9,
  235.     'titanic':     10
  236.   }[name.toLowerCase()];
  237. }
  238.  
  239. function keptDataPoints_(dist, _) {
  240.   _ = _ || LodashGS.load();
  241.  
  242.   return _.sum(dist);
  243. }
  244.  
  245. var valueCalc_ = (function() {
  246.   const cache = {};
  247.  
  248.   function memoized(prop, xProp, name, xRows, dist, recent, points, _) {
  249.     _ = _ || LodashGS.load();
  250.    
  251.     var outp;
  252.     cache[prop] = cache[prop] || {};
  253.    
  254.     if (prop in cache && name in cache[prop]) {
  255.       // USE CACHE
  256.      
  257.       return output(cache[prop][name]);
  258.      
  259.     } else if (_.sum(dist) <= 0) {
  260.       // NEW PARAMS - ZERO USABLE LISTINGS
  261.      
  262.       return output({
  263.         'w-avg. value per point': null,
  264.         'w-avg. value': null,
  265.         'median value': null
  266.       });
  267.      
  268.     } else {
  269.       // NEW PARAMS - CALCULATE VALUES
  270.      
  271.       const xnames = [];
  272.       for (var i = 0; !_.isUndefined(xRows[i]); i++) { xnames.push(xRows[i]); }
  273.      
  274.       const verifyKeys = [
  275.         prop  + ' sd (5th pass) - kept data',
  276.         xProp + ' sd (5th pass) - kept data'
  277.       ]
  278.      
  279.       const lookupKeys = {};
  280.       lookupKeys['recent listings - ' + prop ] = name;
  281.      
  282.       const filteredMed = _.flatten(_.map(xnames, function(xname) {
  283.         lookupKeys['recent listings - ' + xProp] = xname;
  284.        
  285.         return _.slice(filterRecent_(
  286.           recent,
  287.           lookupKeys,
  288.           ['overall sd (4th pass) - kept data'],
  289.           _      
  290.         ), 0, dist[xRows[xname]]);
  291.       }));
  292.      
  293.       const filtered = _.flatten(_.map(xnames, function(xname) {
  294.         lookupKeys['recent listings - ' + xProp] = xname;
  295.        
  296.         return _.slice(filterRecent_(
  297.           {
  298.             values: filteredMed,
  299.             cols: recent.cols
  300.           },
  301.           lookupKeys,
  302.           verifyKeys,
  303.           _      
  304.         ), 0, dist[xRows[xname]]);
  305.       }));
  306.      
  307.       var weight = 0, sum = 0;
  308.       _.forEach(filtered, function(rowArray) {
  309.         const compiled = [
  310.           rowArray[recent.cols['total weight - ']],
  311.           rowArray[recent.cols[verifyKeys[0]    ]]
  312.         ]
  313.         weight += _.toNumber(compiled[0]);
  314.         sum    += _.toNumber(compiled[0] * compiled[1]);
  315.       });
  316.      
  317.       var medianArr = [];
  318.       _.forEach(filtered, function(rowArray) {
  319.         medianArr.push(rowArray[recent.cols['overall sd (4th pass) - kept data']]);
  320.       });
  321.       medianArr = _.sortBy(medianArr);
  322.  
  323.       return output({
  324.         'w-avg. value per point': (sum / weight),
  325.         'w-avg. value': (sum / weight) * points,
  326.         'median value': ((
  327.           medianArr[Math.floor((medianArr.length - 1) / 2)] +
  328.           medianArr[Math.ceil(( medianArr.length - 1) / 2)]
  329.         ) / 2) * points
  330.       });
  331.      
  332.     }
  333.  
  334.     function output(obj) {
  335.       cache[prop][name] = obj;
  336.       return obj;
  337.     }
  338.   }
  339.    
  340.   return memoized;
  341. })();
  342.  
  343. function filterRecent_(recent, lookupKeys, verifyKeys, _) {
  344.   return _.filter(recent.values, function(listing) {
  345.     for (var key in lookupKeys) { if (lookupKeys.hasOwnProperty(key)) {
  346.       if (listing[recent.cols[key]].toLowerCase() === lookupKeys[key]) { return true; }
  347.     }}    
  348.     for (var key in verifyKeys) { if (lookupKeys.hasOwnProperty(key)) {
  349.       if (_.isNumber(recent.cols[key])) { return true; }
  350.     }}
  351.   });
  352. }
  353.  
  354. function distribAdjust_(recent, sizeObj, glowObj, _) {
  355.   _ = _ || LodashGS.load();
  356.   const
  357.     multipliers  = multiByOdds_(oddsSG_, _),
  358.     recentValues = recent.values,
  359.     recentCols   = recent.cols,
  360.     sizes        = _.filter(_.values(sizeObj), function(value) { return _.isString(value); }),
  361.     glows        = _.filter(_.values(glowObj), function(value) { return _.isString(value); });
  362.  
  363.   return {
  364.    
  365.     size: labelBy(makeRepresent(mapCounts(
  366.       sizes,
  367.       glows,
  368.       'recent listings - size',
  369.       'recent listings - glow',
  370.       'size sd (5th pass) - kept data'
  371.     ), multipliers), sizes),
  372.    
  373.     glow: labelBy(makeRepresent(mapCounts(
  374.       glows,
  375.       sizes,
  376.       'recent listings - glow',
  377.       'recent listings - size',
  378.       'glow sd (5th pass) - kept data'
  379.     ), multipliers), glows)
  380.    
  381.   }
  382.    
  383.   function mapCounts(mains, secondaries, mainHead, secondHead, valueHead) {
  384.     return _.map(mains, function(main) {
  385.       return _.map(secondaries, function(secondary) {
  386.         return _.filter(recent.values, function(listing) {
  387.           if (
  388.             listing[recent.cols[  mainHead]].toLowerCase() === main      &&
  389.             listing[recent.cols[secondHead]].toLowerCase() === secondary &&
  390.             _.isNumber(listing[recent.cols[valueHead]])
  391.           ) { return true; }
  392.         }).length;
  393.       });
  394.     });
  395.   }
  396.  
  397.   function makeRepresent(countMapProperty, multipliers) {
  398.     return _.map(countMapProperty, function(propRow) {
  399.       var newCounts;
  400.      
  401.       // itterate over multiplier rows
  402.       main: for (var i = 0; i < 10; i++) {
  403.        
  404.         // reset newCounts to array of 10 zeros
  405.         newCounts = _.fill(Array(10), 0, 0, 10);
  406.        
  407.         // get row from multiplier matrix
  408.         var multiRow = multipliers[i];
  409.        
  410.         // itterate over property values and matching multipliers
  411.         for (var ii = 0; ii < 10; ii++) {
  412.           var propCount = propRow[ii];
  413.           if (propCount > 0) {
  414.             repCount = _.round(multiRow[ii] * propRow[i]);
  415.          
  416.             /*
  417.              * if the representative count would be more than the available count,
  418.              * restart from 'main'; else add repCount to new
  419.              */
  420.             if (repCount > propCount) { continue main; }
  421.             newCounts[ii] = repCount;
  422.           } else {
  423.             // we have found 0, all remaining counts are 0.
  424.             break;
  425.           }
  426.         }
  427.         break;
  428.       }      
  429.       return newCounts;
  430.     });
  431.   }
  432.  
  433.   function labelBy(countMatrix, labels) {
  434.     if (countMatrix.length !== labels.length) { throw new TypeError('mismatched inputs'); }
  435.     const outp = {};
  436.     _.forEach(countMatrix, function(row, i) {
  437.       outp[labels[i]] = row;
  438.     });
  439.     return outp;
  440.   }
  441. }
  442.  
  443. // https://github.com/jprichardson/least-squares
  444. function LeastSquares_(X, Y, computeError, ret) {
  445.   if (typeof computeError == 'object') {
  446.     ret = computeError
  447.     computeError = false
  448.   }
  449.  
  450.   if (typeof ret == 'undefined') ret = {}
  451.  
  452.   var sumX = 0
  453.   var sumY = 0
  454.   var sumXY = 0
  455.   var sumXSq = 0
  456.   var N = X.length
  457.  
  458.   for(var i = 0; i < N; ++i) {
  459.     sumX += X[i]
  460.     sumY += Y[i]
  461.     sumXY += X[i] * Y[i]
  462.     sumXSq += X[i] * X[i]
  463.   }
  464.  
  465.   ret.m = ((sumXY - sumX * sumY / N) ) / (sumXSq - sumX * sumX / N)
  466.   ret.b = sumY / N - ret.m * sumX / N
  467.  
  468.   if (computeError) {
  469.     var varSum = 0
  470.     for (var j = 0; j < N; ++j) {
  471.       varSum += (Y[j] - ret.b - ret.m*X[j]) * (Y[j] - ret.b - ret.m*X[j])
  472.     }
  473.  
  474.     var delta = N * sumXSq - sumX*sumX
  475.     var vari = 1.0 / (N - 2.0) * varSum
  476.  
  477.     ret.bErr = Math.sqrt(vari / delta * sumXSq)
  478.     ret.mErr = Math.sqrt(N / delta * vari)
  479.   }
  480.  
  481.   return function(x) {
  482.     return ret.m * x + ret.b
  483.   }
  484. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement