Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// <summary>
- /// Model to work with a subset of a set and render charts and graphs correctly.
- /// </summary>
- public class PartialSetSlicer
- {
- /// <summary>
- /// Construct a <see cref="PartialSetSlicer" /> with <see cref="Threshold" /> set to the distribution of the last element.
- /// </summary>
- /// <param name="set">Data set.</param>
- /// <param name="size">Size for subset.</param>
- public PartialSetSlicer(
- IEnumerable<AudienceCountryModel> set,
- int size) : this(set, size, 0.05d) => Threshold = (decimal) Countries.Last().Distribution;
- public PartialSetSlicer(
- IEnumerable<AudienceCountryModel> set,
- int size,
- double threshold)
- {
- Countries = set
- .OrderByDescending(_ => _.Distribution)
- .ThenBy(_ => _.TwoLetterIsoCountryCode)
- .Take(size)
- .ToList();
- Distribution = Countries.Sum(_ => (decimal) _.Distribution);
- if (threshold < 0 && threshold > 1)
- {
- throw new ArgumentException("Threshold must be between 0 and 1", nameof(threshold));
- }
- Threshold = (decimal) threshold;
- }
- public IReadOnlyList<AudienceCountryModel> Countries { get; }
- /// <summary>
- /// Gets the threshold for countries to leave out.
- /// </summary>
- /// <value>
- /// Default is five percent (0.05d)
- /// </value>
- public decimal Threshold { get; }
- /// <summary>
- /// Gets the total distribution for those countries that didn't make it to the top five.
- /// </summary>
- public decimal NotAccountedForDistribution => 1m - Distribution;
- /// <summary>
- /// Gets the distribution for top five countries.
- /// </summary>
- public decimal Distribution { get; }
- public bool HasUnaccountedDistribution => NotAccountedForDistribution != 0;
- /// <summary>
- /// Create dummy rows to batch the not accounted for distribution under the "Other" label
- /// </summary>
- /// <remarks>
- /// Google Charts assume the input is relative, sums to 100%. To show e.g. top five, there is some work to be done to
- /// create a number of dummy rows that are below the threshold value and are unique. This will batch them as "Other"
- /// </remarks>
- /// <returns></returns>
- public IEnumerable<AudienceCountryModel> DummyRows()
- {
- if (!HasUnaccountedDistribution)
- {
- yield break;
- }
- // Calculate new value slightly less than threshold since they will be displayed if equal
- var value = Threshold * 0.99m;
- // Determine how much dummy rows we need to add
- var glitch = NotAccountedForDistribution - Threshold;
- if (glitch < 0)
- {
- // We simply add one row with distribution equal to not accounted for
- yield return new AudienceCountryModel(null, (double) NotAccountedForDistribution);
- }
- else
- {
- // More dummy rows needed. More precisely the divider for not accounted for and threshold
- var multiplier = (int) Math.Round(NotAccountedForDistribution / Threshold);
- // Create dummy rows with unique label
- var query = Enumerable.Range(0, multiplier)
- .Select(_ => new AudienceCountryModel(_.ToString(), (double) value));
- // Left overs
- var remainder = NotAccountedForDistribution;
- foreach (var audienceCountryModel in query)
- {
- remainder -= value;
- yield return audienceCountryModel;
- }
- if (remainder > 0m)
- {
- yield return new AudienceCountryModel(multiplier.ToString(), (double) remainder);
- }
- }
- }
- }
Add Comment
Please, Sign In to add comment