Search code examples
c#algorithmmathstatisticsfinance

C#: Split a Single Annual Figure into Monthly amounts based on an arbitrary Y=f(X) formula


I have an annual figure, let's say for purposes of this question that the figure is 100%.

What I would like to do is have C# split the 100% figure into a curved distribution of monthly percentages based on a specific formula Y = f(X), that still add to 100.

This is likely primarily a math question - how could I accomplish this without manually keying in figures that add to 100?


Solution

  • My comment as an answer. The way to do this is to get the seasonality curve expressed over time. Let's consider a simple case, where we the curve looks like a step-ish sine-ish curve. We'll use a unit of "percentage of maximum value" to describe the curve. Seasons will be 3 months long, so winter is Dec-Feb, spring is Mar-May, etc.

    The curve looks like:

    Dec-Feb Mar-May Jun-Aug Sep-Nov
    20% of Max 60% of Max 100% of Max 60% of Max

    If you add those all up (for 12 months), you will get 720%, or 7.2. So, the actual values you want to use are:

    Dec-Feb Mar-May Jun-Aug Sep-Nov
    2.78 (i.e., 20.0/7.2) 8.33 (60.0/7.2) 13.89 (100.0/7.2) 8.33 (60.0/7.2)

    Checking this to make sure it's valid:

    2.78 * 3 + 8.33 * 6 + 13.89 *3 = 99.99 (or, just about 100)


    Some working sample code for a C# console app using an arbitrary sinusoidal function that vaguely represents some early and late seasonality - MyFunction can be replaced with any function, including a stepwise one like above:

    using System;
    using System.Linq;
    namespace Test
    {
    class Program
    {
        static void Main(string[] args)
        {
    
            int months = 12;
    
            double[] scaleExpectations = new double[months];
    
            for (int i = 0; i < 12; i++)
            {
                scaleExpectations[i] = MyFunction(i);
            }
    
            double sum = scaleExpectations.Sum(); //this is the actual Integral of the series, since it is not a real curve
    
            double[] percents = new double[months];
    
            for (int i = 0; i < 12; i++)
            {
                percents[i] = Math.Round((scaleExpectations[i] * 100) / sum, 2);
    
                Console.WriteLine($"iteration {i} with value {percents[i]}");
    
                if (i == 11) // remove rounding error by altering last position
                {
                    var error = 100 - percents.Sum();
                    percents[i] = percents[i] + error;
                }
            }
    
            Console.WriteLine(percents.Sum());
    
        }
    
        public static double MyFunction(double input)
        {
            return .4 * Math.Sin(.5 * (input - 7.5)) + .9;
        }
    }
    }