Сгенерируйте N "случайных" строк длиной K, используя таблицу вероятностей
Как создать N "случайные" строки длины K, используя таблицу вероятностей? K будет некоторое четное число.
prob_table = {'aa': 0.2, 'ab': 0.3, 'ac': 0.5}
Предположим, что вероятность 'acacab' выше, чем вероятность 'aaaaaa'.
Это подзадача более крупной задачи, которую я использую для генерации синтетических последовательностей на основе таблицы вероятностей. Я не знаю, как использовать таблицу вероятностей для генерации "случайных" строк?
Что у меня есть до сих пор:
def seq_prob(fprob_table,K= 6, N= 10):
#fprob_table is the probability dictionary that you input
#K is the length of the sequence
#N is the amount of sequences
seq_list = []
#possibly using itertools or random to generate the semi-"random" strings based on the probabilities
return seq_list
4 ответа:
Есть несколько хороших подходов к принятию взвешенных случайных решений, описанных в конце документации для встроенного модуля
random:Общая задача состоит в том, чтобы сделать случайный.choice () с взвешенными вероятностями.
Если веса являются малыми целыми отношениями, простой метод заключается в построении выборочной совокупности с повторами:
>>> weighted_choices = [('Red', 3), ('Blue', 2), ('Yellow', 1), ('Green', 4)] >>> population = [val for val, cnt in weighted_choices for i in range(cnt)] >>> random.choice(population) 'Green'Более общий подход состоит в том, чтобы расположить веса в кумулятивном распределении с модуле itertools.accumulate (), а затем найдите случайное значение с помощью bisect.bisect ():
Чтобы адаптировать этот последний подход к вашей конкретной проблеме, я бы сделал:>>> choices, weights = zip(*weighted_choices) >>> cumdist = list(itertools.accumulate(weights)) >>> x = random.random() * cumdist[-1] >>> choices[bisect.bisect(cumdist, x)] 'Blue'Это предполагает, что ключевые строки в вашей таблице вероятностей имеют одинаковую длину, если они имеют несколько разных длин, этот код иногда (возможно, большую часть времени!) дайте ответы, которые длиннее символовimport random import itertools import bisect def seq_prob(fprob_table, K=6, N=10): choices, weights = fprob_table.items() cumdist = list(itertools.accumulate(weights)) results = [] for _ in range(N): s = "" while len(s) < K: x = random.random() * cumdist[-1] s += choices[bisect.bisect(cumdist, x)] results.append(s) return resultsK. Я полагаю, что он также предполагает, чтоKявляется точным кратным длины ключа, хотя на самом деле это будет работать, если это не так (это просто даст строки результатов, которые все длиннее символовK, так как нет никакого способа получитьKточно).
Можно использовать
random.random:Это, без сомнения, может быть улучшено, ноfrom random import random def seq_prob(fprob_table, K=6, N=10): #fprob_table is the probability dictionary that you input #K is the length of the sequence #N is the amount of sequences seq_list = [] s = "" while len(seq_list) < N: for k, v in fprob_table.items(): if len(s) == K: seq_list.append(s) s = "" break rn = random() if rn <= v: s += k return seq_listrandom.randomполезно при работе с вероятностью.
Я уверен, что есть более чистый/лучший способ, но вот один простой способ сделать это.
Здесь мы заполняем
pick_listзначениями 100 отдельных пар символов, число значений которых определяется вероятностью. В этом случае существуют 20'aa', 30'ab'и 50'ac'записей внутриpick_list. Затемrandom.choice(pick_list)равномерно вытягивает случайную запись из списка.import random prob_table = {'aa': 0.2, 'ab': 0.3, 'ac': 0.5} def seq_prob(fprob_table, K=6, N=10): #fprob_table is the probability dictionary that you input # fill list with number of items based on the probabilities pick_list = [] for key, prob in fprob_table.items(): pick_list.extend([key] * int((prob * 100))) #K is the length of the sequence #N is the amount of sequences seq_list = [] for i in range(N): sub_seq = "".join(random.choice(pick_list) for _ in range(int(K/2))) seq_list.append(sub_seq) return seq_listС результатами:
seq_prob(prob_table) ['ababac', 'aaacab', 'aaaaac', 'acacac', 'abacac', 'acaaac', 'abaaab', 'abaaab', 'aaabaa', 'aaabaa']
Если ваши таблицы или последовательности большие, использование numpy может быть полезно, Так как это, вероятно, будет значительно быстрее. Кроме того, numpy построен для такого рода задач, и подход прост для понимания и всего 3 или 4 строки.
Идея состояла бы в том, чтобы преобразовать вероятности в кумулятивные вероятности, т. е. отображение(.2, .5, .3)в(.2, .7, 1.), и тогда случайные числа, генерируемые вдоль плоского распределения от0до1, будут попадать в ячейки кумулятивной суммы с частотой соответствует Весам. Включает вsearchsortedможет использоваться, чтобы быстро найти бин случайных значений лежат в пределах. То есть,import numpy as np prob_table = {'aa': 0.2, 'ab': 0.3, 'ac': 0.5} N = 10 k = 3 # number of strings (not number of characters) rvals = np.random.random((N, k)) # generate a bunch of random values string_indices = np.searchsorted(np.cumsum(prob_table.values()), rvals) # weighted indices x = np.array(prob_table.keys())[string_indices] # get the strings associated with the indices y = ["".join(x[i,:]) for i in range(x.shape[0])] # convert this to a list of strings # y = ['acabab', 'acacab', 'acabac', 'aaacaa', 'acabac', 'acacab', 'acabaa', 'aaabab', 'abacac', 'aaabab']Здесь я использовал
kкак количество строк, которое вам нужно, а неKкак количество символов, так как постановка задачи неоднозначна относительно строк/символов.