Reputation: 45
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
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
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
Reputation: 3698
There may well be more refined ways of approaching this, but:
I haven't tried this, just run a couple of basic examples in my head, but it should cover it.
Upvotes: 0