/// <summary> /// Gets the top two rated options. /// </summary> /// <param name="rankedVotes">The group votes.</param> /// <param name="option1">The top rated option. Null if there aren't any options available.</param> /// <param name="option2">The second rated option. Null if there is only one option available.</param> private static void GetTopTwoRatedOptions(IEnumerable<RankGroupedVoters> rankedVotes, out string option1, out string option2, ref string debug) { var scoredVotes = from vote in rankedVotes select new { Vote = vote.VoteContent, Rank = RankScoring.LowerWilsonScore(vote.Ranks) }; var orderedVotes = scoredVotes.OrderByDescending(a => a.Rank); var topTwo = orderedVotes.Take(2); var v1 = topTwo.FirstOrDefault(); var v2 = topTwo.Skip(1).FirstOrDefault(); option1 = v1?.Vote; option2 = v2?.Vote; StringBuilder sb = new StringBuilder(); // Output: [Option1, Option2] [Score1, Score2] [PrefCount1, PrefCount2] sb.Append("RIR: ["); if (v1 != null) sb.Append(v1.Vote); sb.Append(", "); if (v2 != null) sb.Append(v2.Vote); sb.Append("] ["); if (v1 != null) sb.Append($"{v1.Rank:f5}"); sb.Append(", "); if (v2 != null) sb.Append($"{v2.Rank:f5}"); sb.Append("] "); debug = sb.ToString(); }
/// <summary> /// Gets the least preferred choice. /// This is normally determined by selecting the option with the lowest Borda count. /// This is inverted because we don't want to convert ranks to Borda values (it gains us nothing). /// It then needs to be averaged across the number of instances of each vote, to /// account for unranked options. This allows apples-to-apples comparisons against options /// that are ranked in all votes. /// We then need to scale it relative to the number of instances of that option appearing, to deal /// with truncated rankings (where there are more options than rankings allowed). /// An option ranked infrequently can be scaled up relative to its rate of occurance for /// a high likelihood of elimination. /// </summary> /// <param name="localRankings">The vote rankings.</param> /// <returns>Returns the vote string for the least preferred vote.</returns> private static string GetLeastPreferredChoice(List <VoterRankings> localRankings) { var groupVotes = GroupRankVotes.GroupByVoteAndRank(localRankings); var rankedVotes = from vote in groupVotes select new { Vote = vote.VoteContent, Rank = RankScoring.LowerWilsonScore(vote.Ranks) }; var worstVote = rankedVotes.MinObject(a => a.Rank); Debug.Write($"({worstVote.Rank:f5}) {worstVote.Vote}"); return(worstVote.Vote); }
/// <summary> /// Implementation to generate the ranking list for the provided set /// of votes for a specific task. /// </summary> /// <param name="task">The task that the votes are grouped under.</param> /// <returns>Returns a ranking list of winning votes.</returns> protected override RankResults RankTask(GroupedVotesByTask task) { // Can calculating the score easily by having all the rankings for // each vote grouped together. var groupVotes = GroupRankVotes.GroupByVoteAndRank(task); var rankedVotes = from vote in groupVotes select new { Vote = vote.VoteContent, Rank = RankScoring.LowerWilsonScore(vote.Ranks) }; var orderedVotes = rankedVotes.OrderByDescending(a => a.Rank); RankResults results = new RankResults(); results.AddRange(orderedVotes.Select(a => new RankResult(a.Vote, $"Wilson: [{a.Rank:f5}]"))); return(results); }