【问题标题】:Use result of forloop to create new list in python使用for循环的结果在python中创建新列表
【发布时间】:2023-12-23 01:27:02
【问题描述】:

我创建了一个 mutate_v1 函数,可以在 DNA 序列中生成随机突变。

def mutate_v1(sequence, mutation_rate):
    dna_list = list(sequence)
    for i in range(len(sequence)):
        r = random.random()
        if r < mutation_rate:
            mutation_site = random.randint(0, len(dna_list) - 1)
            dna_list[mutation_site] = random.choice(list('ATCG'))
        return ''.join(dna_list)

如果我将我的函数应用于 G0 的所有元素,我会得到新一代 (G1) 突变体(突变序列列表)。

G0 = ['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']

G1 = [mutate_v1(s,0.01) for s in G0]

#G1
['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']

如何将我的功能重复到 G20(20 代)?

我可以像下面这样手动完成

G1   = [mutate_v1(s,0.01) for s in G0]
G2   = [mutate_v1(s,0.01) for s in G1]
G3   = [mutate_v1(s,0.01) for s in G2]
G4   = [mutate_v1(s,0.01) for s in G3]
G5   = [mutate_v1(s,0.01) for s in G4]
G6   = [mutate_v1(s,0.01) for s in G5]
G7   = [mutate_v1(s,0.01) for s in G6]

但我确信 for 循环会更好。 我已经测试了几个codes,但没有结果。

有人可以帮忙吗?

【问题讨论】:

  • 你想存储我假设的每一代?
  • 使用map()函数调用mutate_v1 n次。
  • @DaniMesejo 是的。我想从上一代(Gn)中获取新列表(Gn+1)。

标签: python for-loop nested bioinformatics dna-sequence


【解决方案1】:

使用range迭代最多代数,并将每一代存储在一个列表中,每一代都是变异上一个的结果:

G0 = ['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']

generations = [G0]
for _ in range(20):
    previous_generation = generations[-1]
    generations.append([mutate_v1(s, 0.01) for s in previous_generation])

# then you can access by index to a generation
print(generations[1])  # access generation 1
print(generations[20]) # access generation 20

输出

['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']
['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAT']

【讨论】:

  • 这里是 OP 了解生成器的好机会。
  • @ddejohn 我看不出这怎么能很容易地转化为生成器,因为每一代都依赖于前一代。一种解决方案可能是使用累积,但这就是我想到的全部
  • 您介意我添加一个涉及生成器的答案吗?您的解决方案很棒,但我认为这对 OP 来说是一个很好的学习机会。
  • @ddejohn 没问题,随意。
  • @DaniMesejo 回应你最初的怀疑,生成器实际上是一个教科书用例,用于依赖于它们最近状态的序列(生成器的“hello world”通过增加以前的值)!有关实现细节,请参阅我的答案(tl;dr,yield g,然后将 g 重新分配给它的突变)。
【解决方案2】:

Dani 的回答是一个不错的简单解决方案,但我想演示另一种方法,使用 Python 中稍微更高级的编程技术,生成器函数:

def mutation_generator(g0):
    g = g0.copy()
    while True:
        yield g
        g = [mutate_v1(seq, 0.01) for seq in g]

现在,mutation_generator 是一个无限序列生成器,这意味着理论上您可以无限地继续发展您的序列。如果要抢20代:

g0 = ['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']
generation = mutation_generator(g0)
twenty_generations = [next(generation) for _ in range(20)]

这个生成器的好处是我们可以在任何时候从它停止的地方重新启动它。假设您已经对前 20 代进行了一些分析,现在您想看看接下来的 10 代会发生什么:

next_hundred = [next(generation) for _ in range(100)]

现在,我们可以初始化一个 new 生成器,使用来自 twenty_generations 的最后一代作为新生成器的初始生成,但这不是必需的,因为我们的 generation 生成器只是在 20 代时停止,随时准备继续变异,只要您致电 next(generation)

这开辟了很多可能性,包括发送新的突变率参数,或者如果您愿意,甚至可以发送全新的突变函数。真的,任何你想要的。

这里的另一个好处是您可以在相同的初始序列上运行多个生成器,并观察它们是如何发散的。请注意,使用在函数中使用 for 循环的更传统方法完全可以做到这一点,但使用生成器的好处是您不必一次生成整个序列;只有当你告诉它(通过next())它才会发生变异。例如:

g0 = ['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']
universe_1 = mutation_generator(g0)
universe_2 = mutation_generator(g0)
universe_3 = mutation_generator(g0)

# The first generation is always the same as g0, but this can be modified if you desire
next(universe_1)
next(universe_2)
next(universe_3)

# Compare the first mutation without having to calculate twenty generations in each 'universe' before getting back results
first_mutation_u1 = next(universe_1)
first_mutation_u2 = next(universe_2)
first_mutation_u3 = next(universe_3)

同样,您还可以修改生成器函数mutation_generator 以接受其他参数,例如自定义突变函数,甚至可以随时更改突变率等。

最后,顺便说一句,使用生成器可以很容易地跳过数千代,而无需在内存中存储多个序列:

g0 = ['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']
generation = mutation_generator(g0)
for _ in range(10000):
    next(generation)

print(g0)  # first gen
print(next(generation))  # ten thousand generations later

输出:

['CTGAA', 'CTGAA', 'CTGAA', 'CTGAA', 'CTGAA']
['TTGGA', 'CTTCG', 'TGTGA', 'TAACA', 'CATCG']

使用for 基于循环的方法,您将不得不创建并存储所有 10000 代(浪费大量内存),或者修改 Dani 答案中的代码以使其更像生成器(但没有好处!)。

如果您想了解更多信息,Real Python 在生成器上有一个good article。当然,也可以查看the docs

【讨论】:

  • 我会检查的,谢谢:)
  • 这是一个很好的答案!
最近更新 更多