简体   繁体   中英

How to normalize a list of floats when one value has to stay the same?

I have a list with these normalized values

list_a = [0.25, 0.25, 0.25, 0.25]

Now I want to change the value of one entry to another number, let's say 0.75 . This number is fixed and shouldn't change anymore.

list_a_changed = [0.25, 0.25, 0.75, 0.25]

To still make sure the sum of all the values in the list add up to 1 , the remaining values need to be 0.0833 . So my list will have to be:

list_a_normalized = [0.083, 0.083, 0.75, 0.083]

This is easy enough to figure out if all values share the same percentage in the initial list. I can just do 1 - 0.75 = 0.25 , and divide that 0.25 between remaining numbers since they all hold the same percentage of the total sum.

value_change = 0.75
remaining_value = 1 - value_change
divided_remaining_value = remaining_value / (len(list_a_changed) - 1)

list_a_normalized = [divided_remaining_value, divided_remaining_value, value_change, divided_remaining_value ]


But how would you do it if the original list was something like:

list_b = [0.25, 0.45, 0.20, 0.10]

And I change one value to 0.05

list_b_changed = [0.25, 0.45, 0.05, 0.10]

How would you calculate what the values of the other numbers will have to be so they each hold the appropriate portion of the remaining 0.95 ?

You may

  • compute the remaining
  • compute the total without the changed value, to get their relative proportion without the changed value
  • multiply their value to the remaining to get it into account, and divide by the relative total to get them proportionnate to the total
def normalize(values, index_not_change):
    remaining = 1 - values[index_not_change]
    total_except_remaining = sum(values) - values[index_not_change]
    return [(value * remaining / total_except_remaining if idx != index_not_change else value)
            for idx, value in enumerate(values)]

print(normalize([0.25, 0.25, 0.75, 0.25], 2)) # [0.0833333333, 0.0833333333, 0.75, 0.0833333333]
print(normalize([0.25, 0.45, 0.05, 0.10], 2)) # [0.296875, 0.534375, 0.05, 0.11875000000000001]

To understand the total_except_remaining purpose, without it it'd have been that

normalize([0.25, 0.25, 0.75, 0.25], 2) -> [0.0625, 0.0625, 0.75, 0.0625]

because you'd have compute a quarter of the remaining ( 0.25 ) but adding the fact that the relative sum was 0.75 and not 1, you update to their real proportion


You can put the modification in the same method too

def normalize(values, position, new_value):
    values[position] = new_value
    remaining = 1 - new_value
    total_except_remaining = sum(values) - new_value
    return [(value * remaining / total_except_remaining if idx != position else value)
            for idx, value in enumerate(values)]

print(normalize([0.25, 0.25, 0.25, 0.25], 2, 0.75))

Use change_normalized item and keep the list normalized:
The re_normalize keeps the list normalized by multiplying with the correct factor (which is the ratio between one and the sum without the changed item):


def change_normalized(lst, index, value):
    def touch(lst, index, value):
        lst[index] = value
    def re_normalize(lst, index, value):
        multiply_factor = (1 - value) / (sum(lst) - value)
        for j in range(len(lst)):
            if i == j:
                continue
            lst[j] *= multiply_factor
    touch(lst, i, value)
    re_normalize(lst, i, value)

i = 2
value = 0.05
list_b = [0.25, 0.45, 0.20, 0.10]

# Change item at index to value and keep list normalized
change_normalized(list_b, i, value)

# 1.0
print(sum(list_b))

This code can be shrinked to:

def change_normalized(lst, index, value):
    lst[index] = value
    multiply_factor = (1 - value) / (sum(lst) - value)
    lst[:] = [multiply_factor * x if i != j else x for j, x in enumerate(lst)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM