Guest User

Untitled

a guest
Jun 21st, 2018
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.06 KB | None | 0 0
  1. /// <summary>
  2. /// Model to work with a subset of a set and render charts and graphs correctly.
  3. /// </summary>
  4. public class PartialSetSlicer
  5. {
  6. /// <summary>
  7. /// Construct a <see cref="PartialSetSlicer" /> with <see cref="Threshold" /> set to the distribution of the last element.
  8. /// </summary>
  9. /// <param name="set">Data set.</param>
  10. /// <param name="size">Size for subset.</param>
  11. public PartialSetSlicer(
  12. IEnumerable<AudienceCountryModel> set,
  13. int size) : this(set, size, 0.05d) => Threshold = (decimal) Countries.Last().Distribution;
  14.  
  15. public PartialSetSlicer(
  16. IEnumerable<AudienceCountryModel> set,
  17. int size,
  18. double threshold)
  19. {
  20. Countries = set
  21. .OrderByDescending(_ => _.Distribution)
  22. .ThenBy(_ => _.TwoLetterIsoCountryCode)
  23. .Take(size)
  24. .ToList();
  25. Distribution = Countries.Sum(_ => (decimal) _.Distribution);
  26. if (threshold < 0 && threshold > 1)
  27. {
  28. throw new ArgumentException("Threshold must be between 0 and 1", nameof(threshold));
  29. }
  30.  
  31. Threshold = (decimal) threshold;
  32. }
  33.  
  34. public IReadOnlyList<AudienceCountryModel> Countries { get; }
  35.  
  36. /// <summary>
  37. /// Gets the threshold for countries to leave out.
  38. /// </summary>
  39. /// <value>
  40. /// Default is five percent (0.05d)
  41. /// </value>
  42. public decimal Threshold { get; }
  43.  
  44. /// <summary>
  45. /// Gets the total distribution for those countries that didn't make it to the top five.
  46. /// </summary>
  47. public decimal NotAccountedForDistribution => 1m - Distribution;
  48.  
  49. /// <summary>
  50. /// Gets the distribution for top five countries.
  51. /// </summary>
  52. public decimal Distribution { get; }
  53.  
  54. public bool HasUnaccountedDistribution => NotAccountedForDistribution != 0;
  55.  
  56. /// <summary>
  57. /// Create dummy rows to batch the not accounted for distribution under the "Other" label
  58. /// </summary>
  59. /// <remarks>
  60. /// Google Charts assume the input is relative, sums to 100%. To show e.g. top five, there is some work to be done to
  61. /// create a number of dummy rows that are below the threshold value and are unique. This will batch them as "Other"
  62. /// </remarks>
  63. /// <returns></returns>
  64. public IEnumerable<AudienceCountryModel> DummyRows()
  65. {
  66. if (!HasUnaccountedDistribution)
  67. {
  68. yield break;
  69. }
  70.  
  71. // Calculate new value slightly less than threshold since they will be displayed if equal
  72. var value = Threshold * 0.99m;
  73. // Determine how much dummy rows we need to add
  74. var glitch = NotAccountedForDistribution - Threshold;
  75. if (glitch < 0)
  76. {
  77. // We simply add one row with distribution equal to not accounted for
  78. yield return new AudienceCountryModel(null, (double) NotAccountedForDistribution);
  79. }
  80. else
  81. {
  82. // More dummy rows needed. More precisely the divider for not accounted for and threshold
  83. var multiplier = (int) Math.Round(NotAccountedForDistribution / Threshold);
  84. // Create dummy rows with unique label
  85. var query = Enumerable.Range(0, multiplier)
  86. .Select(_ => new AudienceCountryModel(_.ToString(), (double) value));
  87. // Left overs
  88. var remainder = NotAccountedForDistribution;
  89. foreach (var audienceCountryModel in query)
  90. {
  91. remainder -= value;
  92. yield return audienceCountryModel;
  93. }
  94.  
  95. if (remainder > 0m)
  96. {
  97. yield return new AudienceCountryModel(multiplier.ToString(), (double) remainder);
  98. }
  99. }
  100. }
  101. }
Add Comment
Please, Sign In to add comment