views:

838

answers:

4

(code examples are python)
Lets assume we have a list of percentages that add up to 100:

mylist = [2.0, 7.0, 12.0, 35.0, 21.0, 23.0]

Some values of mylist may be changed, others must stay fixed.
Lets assume the first 3 (2.0, 7.0, 12.0) must stay fixed and the last three (35.0, 21.0, 23.0) may be changed.

fix = mylist[:3]
vari = mylist[3:]

The goal is to add a new item to mylist, while sum(mylist) stays 100.0 and vari
items keep their relations to each other. For that we need to substract a CERTAIN
PERCENTAGE from each vari item. Example: lets assume we want to add 4.0 to mylist.
Using an ugly aproximation loop I found out that i need to substract ca. 5.0634%
of each vari item (CERTAIN PERCENTAGE = 5.0634):

adjusted =[]
for number in vari:
    adjusted.append(number-(number*(5.0634/100.0)))
adjusted.extend(fix)
adjusted.append(4.0)

adjusted now contains my desired result.

My question is how to calculate CERTAIN PERCENTAGE ;.)

+3  A: 

you're being silly.

let's say you want to add 4.0 to the list. You don't need to subtract a certain amount from each one. What you need to do is multiply each item.

100 - 4 = 96. therefore, multiply each item by 0.96

you want to add 20.0 as an item. so then you multiply each item by 0.8, which is (100-20)*0.01

update: Hrmn I didn't read carefuly enough.

think of it like this. (fixed)+(vari)= 100; (fixed)+(vari * x) + newitem = 100;

so basically like what we did before except with just the vari portion. if vari totals to 50, and the new item you're adding is 3.0, then multiply each item in vari by (47/50)

Breton
100 - 4 = 94? 96? ;)
Jonathan Lonowski
Also he can't subtract. The technique is fine though.
Cyril Gupta
d'oh. Late night. shouldn't do math when I'm tired.
Breton
yes i am silly ;.) ... but you should admit that substracting a certain amount works also ...
Titusz
it works, yes, but then you have to post a question on stack overflow to figure out what to do next.
Breton
Brenton: Look, it worked! The way you said!
Lakshman Prasad
+8  A: 

How's this?

def adjustAppend( v, n ):
    weight= -n/sum(v)
    return [ i+i*weight for i in v ] + [n]

Given a list of numbers v, append a new number, n. Weight the existing number to keep the sum the same.

 sum(v) == sum( v + [n] )

Each element of v, i, must be reduced by some function of i, r(i) such that

sum(r(i)) == -n

or

sum( map( r, v ) ) == -n

Therefore, the weighting function is -(n*i)/sum(v)

S.Lott
Constantly adjusting the float values are going to cause an accumulation of rounding errors. Keep track of which are absolute and which are relative values (as units, like px vs. % in CSS) and when calculating the final values, adjust them in the results, not the storage.
ironfroggy
@ironfroggy: You're right. Imagine that the use case involves an arbitrary sequence of appends and removes. Each number could then have a rather complex adjustment depending on the history of changes. That would be too complex for the resulting gain in precision.
S.Lott
I implemented this, and it works nice. To solve the rounding problem we could define: In favor of the sum(v) being exactly 100.0 it would be ok for the relations between the items to be aproximatly the same ;.)
Titusz
Actually, you'd do better to use a rational number package instead of float for this. http://docs.python.org/dev/library/fractions.html
S.Lott
i was playing around with Decimal but fractions seems to be the right way to go... thanks for the tip...
Titusz
+1  A: 
NEW_NUMBER = 4.0

mylist = [2.0, 7.0, 12.0, 35.0, 21.0, 23.0]
fix = mylist[:3]
vari = mylist[3:]

weight = (100 - sum(fix) - NEW_NUMBER)/sum(vari)

adjusted = []
adjusted.extend( (weight*v for v in vari) )
adjusted.extend(fix)
adjusted.append(NEW_NUMBER)

print sum(adjusted)                # 100.0

Edit: Triptych is right, if you are actually interested in the certain percentage, the following code goes for it:

certain_percentage = 100 * NEW_NUMBER / sum(vari)
print certain_percentage        # 5.06329113924

I think that your constant 5.0634 should actually be 5.0633.

Federico Ramponi
+2  A: 
new_item = 4.0
CERTAIN_PERCENTAGE = 100 * (float(new_item) / sum(vari))
Triptych
nice and short answer ;.) thanks...
Titusz
Thanks. It's exactly what you asked for, but don't miss the other good answers in here.
Triptych