From 362a324b3329bdcc8f20f1d0865fce52ec9e8898 Mon Sep 17 00:00:00 2001 From: schneefux Date: Sat, 23 Jun 2018 14:08:12 +0200 Subject: Calculate r^2 and show 'uncertain' labels for max level stats --- src/HeroTalentTable.vue | 8 ++++---- src/ReportService.js | 14 ++++++++++++-- src/ReportTable.vue | 13 +++++++++---- 3 files changed, 25 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/HeroTalentTable.vue b/src/HeroTalentTable.vue index 6bd228a..dc2098c 100644 --- a/src/HeroTalentTable.vue +++ b/src/HeroTalentTable.vue @@ -22,15 +22,15 @@ {{ (100 * props.row.Winner).toFixed(2) }}% - uncertain +
uncertain
diff --git a/src/ReportService.js b/src/ReportService.js index 3a94671..3da118a 100644 --- a/src/ReportService.js +++ b/src/ReportService.js @@ -3,6 +3,7 @@ import * as metadata from '../data/2d73896c/metadata.json'; const POPULAR_THRESHOLD = 1.0; // percent const PICKS_THRESHOLD = 300; // picks +const VARIANCE_THRESHOLD = 0.25; // % minimum accuracy const report = require('../data/2d73896c/report.json') .filter((entry) => entry.Actor != undefined); // bad data from API downtime @@ -63,14 +64,23 @@ for(let mode of modes) { const slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x); const intercept = (sum_y - slope * sum_x) / n; + // sum (actual - average)^2 + const sstot = ys.map((y) => Math.pow(y - sum_y / n, 2)).reduce(sum, 0); + // sum (actual - estimated)^2 + const ssres = ys.map((y, index) => Math.pow((intercept + slope * xs[index]) - y, 2)).reduce(sum, 0); + // r^2 is also correlation coefficient ^ 2 + const rsquare = 1 - ssres / sstot; + return Object.assign({}, entry, { TalentWinrateBase: intercept, TalentWinrateScaling: slope, // to 1 = max level TalentWinrateMax: intercept + slope, TalentWinrateLevelScaling: slope / maps.getMaxLevel(entry), + TalentWinrateVariance: rsquare, TotalPicks: sum_weights || entry.Count, // NoTalent has no weights TotalWinner: sum_y / sum_weights || entry.Winner, SampleTooSmall: sum_weights < PICKS_THRESHOLD, + VarianceTooLarge: rsquare < VARIANCE_THRESHOLD, }); }); reports.set(mode, reportFilterModeRegressed); @@ -100,10 +110,10 @@ for(let mode of modes) { .filter((entry) => maps.getTalentRarity(entry.Talent) == 'Legendary' && 100 * playersPerMatch * entry.TotalPicks / totalPicks.get(mode)) .sort((entry1, entry2) => entry2.TotalWinner - entry1.TotalWinner)[0]); topLowLevel.set(mode, reports.get(mode) - .filter((entry) => !entry.SampleTooSmall) + .filter((entry) => !entry.SampleTooSmall && !entry.VarianceTooLarge) .sort((entry1, entry2) => entry2.TalentWinrateBase - entry1.TalentWinrateBase)[0]); topScaling.set(mode, reports.get(mode) - .filter((entry) => !entry.SampleTooSmall) + .filter((entry) => !entry.SampleTooSmall && !entry.VarianceTooLarge) .sort((entry1, entry2) => entry2.TalentWinrateLevelScaling - entry1.TalentWinrateLevelScaling)[0]); if (actors.length == 0) { diff --git a/src/ReportTable.vue b/src/ReportTable.vue index fa457e6..d44bdea 100644 --- a/src/ReportTable.vue +++ b/src/ReportTable.vue @@ -16,7 +16,7 @@
- Include entries without enough data + Include entries without enough data (statistics will be inaccurate)
@@ -70,18 +70,23 @@ {{ (100 * props.row.Winner).toFixed(2) }}% + + {{ props.row.TalentWinrateVariance.toFixed(2) }} + + - {{ (100 * props.row.Winner).toFixed(2) }}% + {{ (100 * props.row.Winner).toFixed(0) }}% -- cgit v1.3.1