Подсчет триплетов в ДНК-последовательности - PullRequest
0 голосов
/ 03 июля 2018

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

Это мой код:

def cnt(seq):
    mydict = {}
    if len(seq) % 3 == 0:
        a = [x for x in seq]
        for i in range(len(seq)//3):
            b = ''.join(a[(0+3*i):(3+3*i)])
            for base1 in ['A', 'T', 'G', 'C']:
                for base2 in ['A', 'T', 'G', 'C']:
                    for base3 in ['A', 'T', 'G', 'C']:
                        triplet = base1 + base2 + base3
                        if b == triplet:
                            mydict[b] = 1
        for key in sorted(mydict):
            print("%s: %s" % (key, mydict[key]))
    else:
        print("Error")

Предоставляет ли Biopython функцию для решения этой проблемы?

EDIT:

Обратите внимание , что, например, в последовательности 'ATGAAG', 'TGA' или 'GAA' не"допустимые" триплеты, только "ATG" и "AAG" потому что в биологии и биоинформатике мы читаем «ATG» и «AAG», это та информация, которая нам нужна для перевода или что-то еще.

Вы можете представить это как последовательность слов, например "Hello world". Мы читаем «Привет» и «мир», а не «Привет», «Элло», «Ило ш», ...

Ответы [ 4 ]

0 голосов
/ 06 июля 2018

Вы можете использовать умные методы, как предложено в других ответах, но я построю решение, исходя из вашего кода, который почти работает: ваша проблема в том, что каждый раз, когда вы делаете mydict[b] = 1, вы сбрасываете счетчик * От 1002 * до 1.

Минимальное исправление

Вы можете решить эту проблему, протестировав наличие ключа, если нет, создать запись в dict, затем увеличить значение, но в python есть более удобные инструменты.

Минимальным изменением в вашем коде будет использование defaultdict(int) вместо dict. Всякий раз, когда встречается новый ключ, предполагается, что он имеет соответствующее значение по умолчанию для целого числа: 0. Таким образом, вы можете увеличивать значение вместо сброса:

from collections import defaultdict

def cnt(seq):
     # instanciate a defaultdict that creates ints when necessary
     mydict = defaultdict(int)
     if len(seq) % 3 == 0:
         a = [x for x in seq]
         for i in range(len(seq)//3):
             b = ''.join(a[(0+3*i):(3+3*i)])
             for base1 in ['A', 'T', 'G', 'C']:
                 for base2 in ['A', 'T', 'G', 'C']:
                     for base3 in ['A', 'T', 'G', 'C']:
                         triplet = base1 + base2 + base3
                         if b == triplet:
                             # increment the existing count (or the default 0 value)
                             mydict[b] += 1
         for key in sorted(mydict):
             print("%s: %s" % (key, mydict[key]))
     else:
         print("Error")

Работает как нужно:

cnt("ACTGGCACT")
ACT: 2
GGC: 1

Некоторые возможные улучшения

Теперь давайте попробуем немного улучшить ваш код.

Во-первых, как я писал в комментариях, давайте избежим ненужного преобразования вашей последовательности в список и используем лучшее имя переменной для текущего подсчитанного кодона:

from collections import defaultdict

def cnt(seq):
     mydict = defaultdict(int)
     if len(seq) % 3 == 0:
         a = [x for x in seq]
         for i in range(len(seq)//3):
             codon = seq[(0+3*i):(3+3*i)]
             for base1 in ['A', 'T', 'G', 'C']:
                 for base2 in ['A', 'T', 'G', 'C']:
                     for base3 in ['A', 'T', 'G', 'C']:
                         triplet = base1 + base2 + base3
                         if codon == triplet:
                             mydict[codon] += 1
         for key in sorted(mydict):
             print("%s: %s" % (key, mydict[key]))
     else:
         print("Error")

Теперь давайте упростим часть вложенного цикла, пробуя все возможные кодоны, заранее сгенерировав набор возможных кодонов:

from collections import defaultdict
from itertools import product

codons = {
    "".join((base1, base2, base3))
        for (base1, base2, base3) in product("ACGT", "ACGT", "ACGT")}

def cnt(seq):
     mydict = defaultdict(int)
     if len(seq) % 3 == 0:
         a = [x for x in seq]
         for i in range(len(seq)//3):
             codon = seq[(0+3*i):(3+3*i)]
             if codon in codons:
                 mydict[codon] += 1
         for key in sorted(mydict):
             print("%s: %s" % (key, mydict[key]))
     else:
         print("Error")

Теперь ваш код просто игнорирует триплеты, которые не являются действительными кодонами. Возможно, вместо этого вам следует выдать предупреждение:

from collections import defaultdict
from itertools import product

codons = {
    "".join((base1, base2, base3))
        for (base1, base2, base3) in product("ACGT", "ACGT", "ACGT")}

def cnt(seq):
     mydict = defaultdict(int)
     if len(seq) % 3 == 0:
         a = [x for x in seq]
         for i in range(len(seq)//3):
             codon = seq[(0+3*i):(3+3*i)]
             # We count even invalid triplets
             mydict[codon] += 1
         # We display counts only for valid triplets
         for codon in sorted(codons):
             print("%s: %s" % (codon, mydict[codon]))
         # We compute the set of invalid triplets:
         # the keys that are not codons.
         invalid = mydict.keys() - codons
         # An empty set has value False in a test.
         # We issue a warning if the set is not empty.
         if invalid:
             print("Warning! There are invalid triplets:")
             print(", ".join(sorted(invalid)))
     else:
         print("Error")

Более причудливое решение

Теперь более изящное решение, использующее cytoolz (вероятно, его необходимо установить, поскольку оно не является частью обычных дистрибутивов Python: pip3 install cytoolz, если вы используете pip):

from collections import Counter
from itertools import product, repeat
from cytoolz import groupby, keymap, partition 

# To make strings out of lists of strings
CAT = "".join

# The star "extracts" the elements from the result of repeat,
# so that product has 3 arguments, and not a single one
codons = {CAT(bases) for bases in product(*repeat("ACGT", 3))}

def cnt(seq):
    # keymap(CAT, ...) transforms the keys (that are tuples of letters)
    # into strings
    # if len(seq) is not a multiple of 3, pad="-" will append "-"
    # to complete the last triplet (which will be an invalid one)
    codon_counts = keymap(CAT, Counter(partition(3, seq, pad="-")))

    # separate encountered codons into valids and invalids
    codons_by_validity = groupby(codons.__contains__, codon_counts.keys())
    # get allows to provide a default value,
    # in case one of the categories is not present
    valids = codons_by_validity.get(True, [])
    invalids = codons_by_validity.get(False, [])

    # We display counts only for valid triplets
    for codon in sorted(valids):
        print("%s: %s" % (codon, codon_counts[codon]))

    # We issue a warning if there are invalid codons.
    if invalids:
        print("Warning! There are invalid triplets:")
        print(", ".join(sorted(invalids)))

Надеюсь, это поможет.

0 голосов
/ 04 июля 2018

Неясно, какой выход ожидается. Здесь мы используем одну из множества группирующих функций из more_itertools для построения соседних триплетов или «кодонов».

import more_itertools as mit


seq = "ATGATG"

codons = ["".join(w) for w in mit.grouper(3, seq)]
codons
# ['ATG', 'ATG']

Подсчитайте количество кодонов, позвонив по номеру len.

len(triplets)
# 2

Для более подробного анализа рассмотрите возможность разбиения задачи на более мелкие функции, которые (1) извлекают кодоны и (2) вычисляют вхождения.

Код

import collections as ct


def split_codons(seq):
    "Return codons from a sequence; raise for bad sequences."
    for w in mit.windowed(seq, n=3, step=3, fillvalue=""):
        part = "".join(w)
        if len(part) < 3:
            raise ValueError(f"Sequence not divisible by 3.  Got extra '{part}'.")
        yield part


def count_codons(codons):
    """Return dictionary of codon occurences."""
    dd = ct.defaultdict(int)
    for i, c in enumerate(codons, 1):
        dd[c] += 1
    return {k: (v, 100 * v/i) for k, v in dd.items()}

Демо

>>> seq     = "ATCGCAGAAATCCGCAGAATC"
>>> bad_seq = "ATCGCAGAAATCCGCAGAATCA"

>>> list(split_codons(seq))
['ATC', 'GCA', 'GAA', 'ATC', 'CGC', 'AGA', 'ATC']

>>> list(split_codons(bad_seq))
ValueError: Sequence not divisible by 3.  Got extra 'A'.

>>> count_codons(split_codons(seq))
{'ATC': (3, 42.857142857142854),
 'GCA': (1, 14.285714285714286),
 'GAA': (1, 14.285714285714286),
 'CGC': (1, 14.285714285714286),
 'AGA': (1, 14.285714285714286)}
0 голосов
/ 04 июля 2018

Мне потребовалось некоторое время, чтобы понять, что вы хотите не считать количество кодонов, а частоту каждого кодона. Ваш заголовок немного вводит в заблуждение в этом отношении. В любом случае, вы можете использовать collections.Counter для вашей задачи:

from collections import Counter

def cnt(seq):
    if len(seq) % 3 == 0:
        #split list into codons of three
        codons = [seq[i:i+3] for i in range(0, len(seq), 3)]
        #create Counter dictionary for it
        codon_freq = Counter(codons)
        #determine number of codons, should be len(seq) // 3
        n = sum(codon_freq.values())
        #print out all entries in an appealing form
        for key in sorted(codon_freq):
            print("{}: {} = {:5.2f}%".format(key, codon_freq[key], codon_freq[key] * 100 / n))
        #or just the dictionary
        #print(codon_freq)
    else:
        print("Error")

seq = "ATCGCAGAAATCCGCAGAATC"

cnt(seq)

Пример вывода:

AGA: 1 = 14.29%
ATC: 3 = 42.86%
CGC: 1 = 14.29%
GAA: 1 = 14.29%
GCA: 1 = 14.29%
0 голосов
/ 03 июля 2018

Вы можете сделать что-то вроде этого:

from itertools import product

seq = 'ATGATG'

all_triplets = [seq[i:i+3] for i in range(len(seq)) if i <= len(seq)-3]
# this gives ['ATG', 'TGA', 'GAT', 'ATG']

# add more valid_triplets here
valid_triplets = ['ATG']

len([(i, j) for i, j in product(valid_triplets, all_triplets) if i==j])

Выход:

2

...