AlwaysSunny
AlwaysSunny

Reputation: 45

How to normalize a float collection such that the sum of all elements is X

Please forgive me if this question is improperly phrased or the solution is trivial. I cannot seem to find an existing answer searching with the terms with which I'm familiar.

I have an array of non-negative floats, and I want to normalize them - that much I can do. My issue is, I'd like the sum of all elements in the collection to be a specific amount.

I can envision ugly ways to accomplish this, but I just know there's a "right" way to make this happen. The purpose is creating a compound bar graph whose total width must be fixed. Each data point in the collection is assigned a color which should get N% of the total bargraph width. I am limited to this approach by the graph display method.

C# examples preferred, if code is necessary.

// normalize data        
var ratio = 100.0 / widths.Max();
var normalizedList = widths.Select(o => o * ratio).ToList();
// magic happens here such that the sum of all elements is N
// and the relative scale of each sibling element is preserved

My tremendous and earnest thanks for any assistance,


additional information: The graph is a compound (segmented) bar graph. Each element of the float collection corresponds to a segment. http://oi62.tinypic.com/2vskt8m.jpg

The necessity of the sum-of-all-elements-is-N rule relates to the graph drawing method, over which I have limited authority.

To achieve my desired width (of the graph visuals being driven by this float collection), the sum-of-all-elements must be a specific number, hence the normalizing. However, both the number of elements and their values change, and I'm unclear on how to compensate for this any other way. Scaling the graph visuals in any other fashion to avoid this kerfuffle is not desirable for many extraneous reasons.

Upvotes: 4

Views: 2419

Answers (3)

user3473830
user3473830

Reputation: 7285

I believe Mathematical equation would be

s= s0 + s1 + s2 + ...

a= size / s

a * s = a* (s0 + s1 + s2 + ...)

a * s = a*s0 + a*s1 + a*s2 + ...

so this should solve the problem

        var total = widths.Sum();
        var size=100;
        var ratio = size/total; //a in the equation
        var normalizedList = widths.Select(o => o * ratio).ToList();

        var normalSum = normalizedList.Sum(); //should be equal to size

Upvotes: 3

petelids
petelids

Reputation: 12815

I think you just have your ratio slightly wrong. What you need is your desired total divided by the sum of all elements. For example

int desiredTotal = 300; 

float[] widths = new float[] { 35f, 63f, 12f };

float ratio = desiredTotal / widths.Sum();
var normalizedList = widths.Select(o => o * ratio).ToList();

foreach (var item in normalizedList)
{
    Console.WriteLine(item);
}

Console.WriteLine(normalizedList.Sum());

/* Which prints:
95.45454
171.8182
32.72727
300
*/

Upvotes: 4

Octopoid
Octopoid

Reputation: 3698

There may well be more refined ways of approaching this, but:

  • Normalise entire set based around 1
  • Calculate total sum of normalised set as Sum.
  • Multiply each item by (DesiredSum / Sum)

I haven't tried this, just run a couple of basic examples in my head, but it should cover it.

Upvotes: 0

Related Questions