Как нормализовать список с плавающей точкой, когда одно значение должно оставаться неизменным?

#python #list #floating-point #normalize

#python #Список #с плавающей запятой #нормализовать

Вопрос:

У меня есть список с этими нормализованными значениями

 list_a = [0.25, 0.25, 0.25, 0.25]
  

Теперь я хочу изменить значение одной записи на другое число, скажем 0.75 . Это число фиксировано и больше не должно меняться.

 list_a_changed = [0.25, 0.25, 0.75, 0.25]
  

Чтобы убедиться, что сумма всех значений в списке равна 1 , остальные значения должны быть 0.0833 . Итак, мой список должен быть:

 list_a_normalized = [0.083, 0.083, 0.75, 0.083]
  

Это достаточно просто, чтобы выяснить, имеют ли все значения одинаковый процент в исходном списке. Я могу просто сделать 1 - 0.75 = 0.25 и разделить это 0.25 между оставшимися числами, поскольку все они содержат одинаковый процент от общей суммы.

 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 ]


  

Но как бы вы это сделали, если исходный список был чем-то вроде:

 list_b = [0.25, 0.45, 0.20, 0.10]
  

И я меняю одно значение на 0.05

 list_b_changed = [0.25, 0.45, 0.05, 0.10]
  

Как бы вы рассчитали, какими должны быть значения других чисел, чтобы каждое из них содержало соответствующую часть оставшегося 0.95 ?

Комментарии:

1. Вы всегда можете заставить все остальные значения быть одинаковыми, поэтому первый алгоритм все еще действителен

Ответ №1:

Вы можете

  • вычислите remaining
  • вычислите общее количество без измененного значения, чтобы получить их относительную пропорцию без измененного значения
  • умножьте их значение на оставшееся, чтобы учесть его, и разделите на относительную сумму, чтобы получить их пропорциональными общей сумме
 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]
  

Чтобы понять total_except_remaining цель, без этого было бы так

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

поскольку вам пришлось бы вычислить четверть оставшегося ( 0.25 ), но добавив тот факт, что относительная сумма была 0.75 , а не 1, вы обновляете их реальную пропорцию


Вы также можете внести изменения в тот же метод

 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))
  

Ответ №2:

Используйте change_normalized item и поддерживайте нормализацию списка:
re_normalize сохраняет список нормализованным путем умножения на правильный коэффициент (который представляет собой отношение между единицей и суммой без измененного элемента):

 
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))
  

Этот код может быть сокращен до:

 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)