using System.Collections.Generic;
namespace Degreed.Data.Service
public partial class EvaluationStepResult
public long ResultId { get; set; }
public long UserEvaluationId { get; set; }
public int StepId { get; set; }
public double Level { get; set; }
public string Comment { get; set; }
public System.DateTime DateCreated { get; set; }
public System.DateTime DateModified { get; set; }
public Nullable<double> Entropy { get; set; }
public string Name { get; set; }
public class EvaluationLevelComputer
private readonly IEnumerable<int> Levels;
private readonly Dictionary<int, double> UniformDistribution;
public EvaluationLevelComputer(int minLevel = 1, int maxLevel = 8)
Levels = Enumerable.Range(minLevel, maxLevel);
UniformDistribution = Levels.ToDictionary(l => l, l => (double)minLevel / maxLevel);
private Dictionary<int, Dictionary<int, double>> Expected(Dictionary<int, int> minimumLevelsByOption)
int numOptions = minimumLevelsByOption.Count;
var expectedDistribution = Levels.ToDictionary(l => l, l =>
return minimumLevelsByOption.Keys.ToDictionary(o => o, o => 0.4 / numOptions);
return expectedDistribution.ToDictionary(l => l.Key, l =>
var similarlyLeveledOptions = minimumLevelsByOption.Where(ml => ml.Value <= level);
if(!similarlyLeveledOptions.Any())
throw new ArgumentException(string.Format("The options must include a minimum level <= {0}.", level));
var closestOptionLevel = similarlyLeveledOptions.Max(ml => ml.Value);
var closestOptions = minimumLevelsByOption.Where(ml => minimumLevelsByOption[ml.Key] == closestOptionLevel).Select(ml => ml.Key);
return l.Value.ToDictionary(vk => vk.Key,
vk => closestOptions.Contains(vk.Key) ? vk.Value + (0.6 / closestOptions.Count()) : vk.Value);
private Dictionary<int, double> Joint(Dictionary<int, double> priorDistribution,
Dictionary<int, Dictionary<int, double>> expectedDistribution, int response)
return Levels.ToDictionary(l => l,
l => priorDistribution[l] * expectedDistribution[l][response]);
private Dictionary<int, double> Normalize(Dictionary<int, double> distribution)
double total = distribution.Values.Sum();
return Levels.ToDictionary(l => l, l => distribution[l] / total);
private double Expectation(Dictionary<int, double> distribution)
return distribution.Select(kv => kv.Key * kv.Value).Sum();
private double MedianLevel(Dictionary<int, double> distribution)
foreach (var level in Levels)
total += distribution[level];
private double Entropy(Dictionary<int, double> distribution)
foreach (int level in Levels)
var normalizedProbability = distribution[level];
entropy += -normalizedProbability * Math.Log(normalizedProbability, 2);
public (double level, double entropy) ExpectedLevel(Dictionary<int, Dictionary<int, int>> optionsByQuestion, Dictionary<int, int> responsesByQuestion)
var currentDistribution = UniformDistribution;
foreach (var questionOptions in optionsByQuestion)
if(responsesByQuestion.TryGetValue(questionOptions.Key, out int response))
var expectedDistribution = Expected(minimumLevelsByOption: questionOptions.Value);
currentDistribution = Joint(currentDistribution, expectedDistribution, response);
currentDistribution = Normalize(currentDistribution);
return (MedianLevel(currentDistribution), Entropy(currentDistribution));
public static void Main()
var optionsByQuestionsByStep = new Dictionary<int, Dictionary<int, Dictionary<int, int>>>
{ 5, new Dictionary<int, Dictionary<int, int>> {
{ 7, new Dictionary<int, int> { { 18, 1 }, { 19, 3 }, { 20, 5 }, { 21, 7 } } },
{ 8, new Dictionary<int, int> { { 22, 1 }, { 23, 1 }, { 24, 6 } } },
{ 10, new Dictionary<int, int> { { 25, 5 }, { 26, 1 } } },
{ 12, new Dictionary<int, int> { { 27, 1 }, { 28, 5 }, { 29, 7 } } },
{ 13, new Dictionary<int, int> { { 30, 1 }, { 31, 4 }, { 32, 6 }, { 33, 8 } } },
{ 15, new Dictionary<int, int> { { 34, 1 }, { 35, 4 }, { 36, 6 } } },
{ 17, new Dictionary<int, int> { { 37, 1 }, { 38, 4 }, { 39, 7 }, { 40, 8 } } } } },
{ 6, new Dictionary<int, Dictionary<int, int>> {
{ 18, new Dictionary<int, int> { { 41, 1 }, { 42, 3 }, { 43, 5 } } },
{ 19, new Dictionary<int, int> { { 44, 1 }, { 45, 4 }, { 46, 6 }, { 47, 7 } } },
{ 20, new Dictionary<int, int> { { 48, 1 }, { 49, 1 }, { 50, 2 } } },
{ 22, new Dictionary<int, int> { { 51, 1 }, { 53, 3 } } },
{ 23, new Dictionary<int, int> { { 55, 1 }, { 56, 3 }, { 57, 4 } } },
{ 24, new Dictionary<int, int> { { 58, 1 }, { 59, 3 }, { 60, 5 }, { 61, 7 } } },
{ 25, new Dictionary<int, int> { { 62, 1 }, { 63, 1 }, { 64, 7 } } } } },
{ 7, new Dictionary<int, Dictionary<int, int>> {
{ 28, new Dictionary<int, int> { {65, 6 }, { 66, 4 }, { 67, 1 } } },
{ 29, new Dictionary<int, int> { { 68, 7 }, { 69, 5 }, { 70, 1 }, { 71, 7 } } },
{ 30, new Dictionary<int, int> { { 72, 1 }, { 73, 4 }, { 74, 5 } } },
{ 31, new Dictionary<int, int> { { 75, 1 }, { 76, 1 }, { 77, 6 } } },
{ 34, new Dictionary<int, int> { { 78, 1 }, { 79, 1 }, { 80, 6 } } },
{ 35, new Dictionary<int, int> { { 81, 1 }, { 82, 1 }, { 83, 4 } } },
{ 36, new Dictionary<int, int> { { 84, 1 }, { 85, 1 }, { 86, 7 } } },
{ 38, new Dictionary<int, int> { { 87, 1 }, { 88, 2 }, { 89, 2 } } },
{ 39, new Dictionary<int, int> { { 90, 1 }, { 91, 4 } } },
{ 40, new Dictionary<int, int> { { 92, 1 }, { 93, 5 }, { 94, 7 }, { 95, 8 } } } } },
{ 8, new Dictionary<int, Dictionary<int, int>> {
{ 41, new Dictionary<int, int> { { 96, 1 }, { 97, 5 }, { 98, 7 } } },
{ 42, new Dictionary<int, int> { { 99, 1 }, { 100, 1 }, { 101, 4 } } },
{ 43, new Dictionary<int, int> { { 102, 1 }, { 103, 7 }, { 104, 7 } } },
{ 44, new Dictionary<int, int> { { 105, 3 }, { 106, 3 }, { 107, 1 } } },
{ 45, new Dictionary<int, int> { { 108, 1 }, { 109, 3 }, { 110, 4 } } },
{ 46, new Dictionary<int, int> { { 111, 1 }, { 112, 1 }, { 113, 6 } } },
{ 48, new Dictionary<int, int> { { 114, 1 }, { 115, 8 } } },
{ 51, new Dictionary<int, int> { { 116, 1 }, { 117, 8 } } } } },
{ 9, new Dictionary<int, Dictionary<int, int>> {
{ 54, new Dictionary<int, int> { { 118, 1 }, { 119, 2 }, { 120, 3 } } },
{ 55, new Dictionary<int, int> { { 121, 1 }, { 122, 3 }, { 123, 5 } } },
{ 56, new Dictionary<int, int> { { 124, 1 }, { 126, 1 } } },
{ 57, new Dictionary<int, int> { { 127, 1 }, { 128, 4 }, { 129, 6 } } },
{ 58, new Dictionary<int, int> { { 130, 1 }, { 131, 4 }, { 132, 6 } } },
{ 59, new Dictionary<int, int> { { 133, 1 }, { 134, 5 } } },
{ 61, new Dictionary<int, int> { { 135, 1 }, { 136, 6 } } },
{ 62, new Dictionary<int, int> { { 137, 1 }, { 138, 3 }, { 139, 5 } } },
{ 64, new Dictionary<int, int> { { 140, 1 }, { 141, 7 } } },
{ 66, new Dictionary<int, int> { { 142, 1 }, { 143, 2 }, { 144, 3 } } },
{ 67, new Dictionary<int, int> { { 145, 1 }, { 146, 8 } } } } },
{ 10, new Dictionary<int, Dictionary<int, int>> {
{ 69, new Dictionary<int, int> { { 147, 1 }, { 148, 1 }, { 149, 2 } } },
{ 70, new Dictionary<int, int> { { 150, 1 }, { 151, 4 }, { 152, 6 } } },
{ 71, new Dictionary<int, int> { { 153, 1 }, { 154, 4 }, { 155, 5 }, { 157, 6 } } },
{ 73, new Dictionary<int, int> { { 158, 1 }, { 159, 1 }, { 160, 7 } } },
{ 75, new Dictionary<int, int> { { 161, 1 }, { 162, 1 }, { 163, 5 } } },
{ 76, new Dictionary<int, int> { { 164, 1 }, { 165, 1 }, { 166, 6 } } },
{ 77, new Dictionary<int, int> { { 167, 1 }, { 168, 3 }, { 169, 6 } } },
{ 78, new Dictionary<int, int> { { 171, 1 }, { 172, 4 }, { 173, 6 }, { 174, 8 } } } } }
var responsesByQuestion = new Dictionary<int, int> {
var levelComputer = new EvaluationLevelComputer();
var results = new List<EvaluationStepResult>();
foreach (var stepQuestions in optionsByQuestionsByStep)
var result = levelComputer.ExpectedLevel(stepQuestions.Value, responsesByQuestion);
results.Add(new EvaluationStepResult
StepId = stepQuestions.Key,
Entropy = result.entropy,
Console.WriteLine("#######n Here are the results #############");
foreach (var esr in results)
Console.WriteLine($"Step ({esr.StepId}), Level ({esr.Level}), Entropy ({esr.Entropy})");