Добавление реалистичного шума в распределение гаусса при сохранении количества выборок выше / ниже порогового значения приблизительно постоянным

#python #numpy #statistics #normal-distribution

#python #numpy #Статистика #нормальное распределение

Вопрос:

У меня есть нормальное распределение и пороговая функция, которая определяет, является ли значение true.

Я хотел бы добавить шум к истинным значениям, так что чем дальше выше / ниже порогового значения, тем меньше вероятность того, что значение будет инвертировано. Таким образом, в крайних случаях, возможно, существует вероятность переворота в 1%, а прямо у порога — вероятность 50%.

Я также хотел бы сохранить количество выборок выше и ниже порогового значения примерно постоянным до и после добавления шума.

Приведенный ниже код, я думаю, выполняет первую половину, но не уверен в подходе ко второй половине. (возможно, пробное вычитание дельты из rnum, пока сумма значений до и после операции не окажется в пределах некоторой погрешности)

 import numpy as np

mean = .5
std_dev = .2
num_points = 10000

arr =  np.sort( np.random.normal(loc=mean, scale= std_dev, size=(num_points)) )

threshold = .8

trues = arr >= threshold

temp = np.where(trues, 1-arr, arr)
scaling = max(temp)
temp *= .5/scaling

rnum = np.random.random(size=(num_points))

flip = rnum <= temp

trues = np.logical_xor(trues, flip)
 

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

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

Ответ №1:

Если я правильно понял, вы хотите иметь выходной вектор со следующими свойствами:

  • логический вектор
  • то же количество элементов, что и во входном векторе
  • вероятность того, что каждый элемент будет истинным, зависит от его значения w.r.t. threshold
  • количество истин такое же, как если бы мы использовали простой порог

Итак, вам нужна функция вероятности, которая сообщает вероятности каждого входного значения, чтобы дать значение True для выходного значения. При обычном пороговом значении вероятность составляет 1 выше порогового значения и 0 ниже порогового значения. Однако вам нужно что-то более мягкое.

Без последнего требования (количество истин) для выходного вектора алгоритм был бы очень простым. Выходные данные функции вероятности будут сравниваться со случайными значениями между 0 и 1, и это будет результатом. В зависимости от распределения входного сигнала и функции вероятности это может привести к удовлетворительным результатам.

Просто пример этого:

 # threshold at 0.8, below 0.7 always false, above 0.9 always True, linear in between
def prob_f(x):
    return np.clip((x - 0.8) / .2   .5, 0., 1.)


def noisy_threshold(sig):
    p = prob_f(sig)
    return p > random.random(sig.shape)
 

Но если требуется лучшее соответствие количеству истин, нам нужно что-то сделать после этого. Нам нужна функция, которой задается желаемое количество истин и вероятностей. Конечно, это изменяет некоторые свойства результирующего распределения, поэтому «чистого» способа сделать это нет.

Одной из возможностей было бы немного скорректировать наш порог вероятности. Например:

 def_ noisy_threshold(sig, threshold):
    # number of Trues with simple thresholding
    n_trues = np.asum(sig > threshold)

    # difference between random noise and our probability
    rdiff = prob_f(sig) - random.random(sig.shape)

    # sort the differences
    sortdiff = sorted(rdiff)

    # a new threshold is used so that the number of Trues is correct:
    return rdiff >= sortdiff[-n_trues]
 

Это вернет точные n_trues значения в случае, если нам не так сильно не повезло, как получение одних и тех же случайных различий.