public void Calculate_RandomSample_ShouldCalculateStandDeviation() { // I'd usually mock this but in the interest of time just do an integration test. var unitUnderTest = new StandardDeviation(new ArithmeticMean()); var numbers = new[] { 4d, 9d, 11d, 12d, 17d, 5d, 8d, 12d, 14d }; var result = unitUnderTest.Calculate(numbers); // irrational number round to nearest 2dp. Assert.AreEqual(3.94, Math.Round(result, 2)); }
private void CalculateMastery(bool refresh) { Progress.Reset(Snapshot.AchievementGameCount); Progress.IsEnabled = true; var unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); var masteryStats = new List <MasteryStats>(); var mostAwardedAchievements = new List <GameStatsViewModel.AchievementStats>(); var achievementIds = new List <int>(Snapshot.GamesWithAchievements); achievementIds.Sort(); foreach (var gameId in achievementIds) { Progress.Label = "Processing game " + gameId; Progress.Current++; if (!Progress.IsEnabled) { break; } string gameName = ""; int created = Int32.MaxValue; int consoleId = 0; using (var stream = File.OpenRead(Path.Combine(_settings.DumpDirectory, gameId + ".json"))) { var json = new JsonObject(stream); var patchData = json.GetField("PatchData").ObjectValue; var achievements = patchData.GetField("Achievements"); if (achievements.Type != JsonFieldType.ObjectArray) { continue; } if (!achievements.ObjectArrayValue.Any(a => a.GetField("Flags").IntegerValue != 5)) { continue; } foreach (var achievement in achievements.ObjectArrayValue) { var createdValue = achievement.GetField("Created").IntegerValue.GetValueOrDefault(); if (createdValue > 0 && createdValue < created) { created = createdValue; } } gameName = patchData.GetField("Title").StringValue; consoleId = patchData.GetField("ConsoleID").IntegerValue.GetValueOrDefault(); } var gameStats = new GameStatsViewModel() { GameId = gameId }; Debug.WriteLine(String.Format("{0} processing {1}", DateTime.Now, gameId)); gameStats.LoadGame(refresh); if (!gameStats.Achievements.Any()) { continue; } var twentyFifthPlayers = gameStats.NumberOfPlayers * 1 / 4; var fiftiethPlayers = gameStats.NumberOfPlayers / 2; var seventyFifthPlayers = gameStats.NumberOfPlayers * 3 / 4; var ninetiethPlayers = gameStats.NumberOfPlayers * 9 / 10; var twentyFifthPercentilePoints = 0; var fiftiethPercentilePoints = 0; var seventyFifthPercentilePoints = 0; var ninetiethPercentilePoints = 0; var ninetiethPercentileAchievements = 0; foreach (var achievement in gameStats.Achievements) { if (achievement.EarnedBy > twentyFifthPlayers) { twentyFifthPercentilePoints += achievement.Points; if (achievement.EarnedBy >= fiftiethPlayers) { fiftiethPercentilePoints += achievement.Points; if (achievement.EarnedBy >= seventyFifthPlayers) { seventyFifthPercentilePoints += achievement.Points; if (achievement.EarnedBy >= ninetiethPlayers) { ninetiethPercentilePoints += achievement.Points; ninetiethPercentileAchievements++; } } } } int i = 0; while (i < mostAwardedAchievements.Count) { if (achievement.EarnedBy > mostAwardedAchievements[i].EarnedBy) { break; } ++i; } if (i < 20) { if (mostAwardedAchievements.Count == 20) { mostAwardedAchievements.RemoveAt(19); } achievement.Description = gameName; mostAwardedAchievements.Insert(i, achievement); } } var standardDeviation = 0.0; var mean = 0.0; if (gameStats.HardcoreMasteredUserCount > 0) { var times = new List <double>(); foreach (var user in gameStats.TopUsers) { if (user.PointsEarned == gameStats.TotalPoints && user.IsEstimateReliable) { times.Add(user.GameTime.TotalMinutes); } } if (times.Count > 0) { mean = times.Average(); if (gameStats.HardcoreMasteredUserCountEstimated) { standardDeviation = StandardDeviation.CalculateFromSample(times); } else { standardDeviation = StandardDeviation.Calculate(times); } } } var minutesPerPoint = new List <float>(); foreach (var user in gameStats.TopUsers) { if (user.PointsEarned > 0) { minutesPerPoint.Add((float)user.GameTime.TotalMinutes / user.PointsEarned); } } minutesPerPoint.Sort(); var stats = new MasteryStats { GameId = gameStats.GameId, GameName = gameStats.DialogTitle.Substring(12).Trim(), ConsoleId = consoleId, Created = unixEpoch + TimeSpan.FromSeconds(created), Points = gameStats.TotalPoints, NumPlayers = gameStats.NumberOfPlayers, HardcoreMasteredUserCount = gameStats.HardcoreMasteredUserCount, MeanTimeToMaster = mean, StdDevTimeToMaster = standardDeviation, MinutesPerPointToMaster = mean / gameStats.TotalPoints, MinutesPerPoint = minutesPerPoint.Count > 0 ? minutesPerPoint[minutesPerPoint.Count / 2] : 0, TwentyFifthPercentilePoints = twentyFifthPercentilePoints, FiftiethPercentilePoints = fiftiethPercentilePoints, SeventyFifthPercentilePoints = seventyFifthPercentilePoints, NintiethPercentilePoints = ninetiethPercentilePoints, NintiethPercentileAchievements = ninetiethPercentileAchievements, }; if (gameStats.NumberOfPlayers == 0) { stats.PlayersPerDay = 0.0; } else { var age = (DateTime.Now - stats.Created).TotalDays; stats.PlayersPerDay = gameStats.NumberOfPlayers / age; } masteryStats.Add(stats); } masteryStats.Sort((l, r) => { if (l.MinutesPerPoint < r.MinutesPerPoint) { return(-1); } else if (l.MinutesPerPoint > r.MinutesPerPoint) { return(1); } return(0); }); _backgroundWorkerService.InvokeOnUiThread(() => { Results.Clear(); foreach (var stats in masteryStats) { Results.Add(stats); } }); _mostAwardedAchievements = mostAwardedAchievements; Progress.Label = String.Empty; }