Как работает эта строка awk, которая подсчитывает количество нуклеотидов в файле fasta?

#unix #awk #bioinformatics

Вопрос:

В настоящее время я учусь использовать awk и нашел нужную мне команду awk, но не до конца понимаю, что происходит. Эта строка кода берет файл генома, называемый fasta, и возвращает всю длину каждой последовательности в нем. Для тех, кто не знаком с файлами fasta, это текстовые файлы, которые могут содержать несколько генетических последовательностей, называемых смежными. Это следует общей структуре:

 >Nameofsequence
Sequencedata like: ATGCATCG
GCACGACTCGCTATATTATA
>Nameofsequence2
Sequencedata
 

Строка находится здесь:

 cat file.fa | awk '$0 ~ ">" {if (NR > 1) {print c;} c=0;printf substr($0,2,100) "t"; } $0 !~ ">" {c =length($0);} END { print c; }'
 

Я понимаю, что кот открывает файл fasta, проверяет, соответствует ли его строка имени последовательности, и в какой-то момент подсчитывает количество символов в разделе данных. Но я не понимаю, как он разбивает раздел данных на подстроки и как он сбрасывает количество с каждой новой последовательностью.


ПРАВКА Эда Мортона: вот приведенный выше сценарий awk, отформатированный разборчиво gawk -o- :

 $0 ~ ">" {
    if (NR > 1) {
        print c
    }
    c = 0
    printf substr($0, 2, 100) "t"
}

$0 !~ ">" {
    c  = length($0)
}

END {
    print c
}
 

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

1. Если вы зададите какие-либо вопросы в будущем, пожалуйста, не публикуйте страшный «онлайн», чтобы мы попытались прочитать, опубликуйте сценарий, который будет отформатирован с новыми строками и отступами, которые подчеркивают поток управления вашей программой. Если это awk-скрипт, и вы не можете найти для него хороший макет, просто запустите его с помощью gawk -o- 'script' , и он будет распечатан для вас. Я просто сделал это для вас по этому вопросу.

Ответ №1:

Сначала отформатируйте команду:

 awk '
  $0 ~ ">" {
    if (NR > 1) {print c;}
    c=0;
    printf substr($0,2,100) "t";
  }
  $0 !~ ">" {
    c =length($0);
  }
  END { print c; }
  ' file.fa
 

Код будет использоваться c для подсчета символов.Этот счетчик начинается со значения 0 и будет сбрасываться до 0 каждый раз, когда строка с > анализируется.
Длина входной строки добавляется c , если входная строка не содержит a > .
Значение c должно быть напечатано после последовательности, поэтому, когда оно находит новое > (не в первой строке) или когда анализируется полный файл (блок с END ).
Как вы, возможно, уже сейчас понимаете:
breaking down the data section in substrings заключается в сопоставлении входной строки с a > , и
resetting the counts with each new sequence делается с помощью c=0 в блоке с. $0 ~ ">"

Посмотрите на комментарий Эда: printf утверждение используется неправильно. Я не знаю, как часто %s встречается в файле fasta, но это не важно: используйте %s для входных строк.

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

1. Никогда printf input не используйте для каких-либо входных значений, так как это приведет к сбою, если входные данные содержат символы форматирования printf, например %s . Так что вместо printf substr($0,2,100) "t" использования printf "%st", substr($0,2,100)

Ответ №2:

@Walterа уже ответил на ваш вопрос, объяснив, что делает скрипт, но в случае, если вам интересно вот улучшенная версия, включая несколько небольших исправлений для использования printf input и печать пустой строки, если входной файл пуст и улучшений по сравнению с избыточными измерениями одного и того же состояния дважды и тестирование > и удаление его отдельно, а не все сразу:

 BEGIN { OFS="t" }
sub(/^>/,"") {
    if (lgth) { print name, lgth }
    name = $0
    lgth = 0
    next
}
{ lgth  = length($0) }
END {
    if (lgth) { print name, lgth }
}
 

В качестве альтернативы вы могли бы сделать:

 BEGIN { OFS="t" }
sub(/^>/,"") {
    if (seq != "") { print name, length(seq) }
    name = $0
    seq = ""
    next
}
{ seq = seq $0 }
END {
    if (seq != "") { print name, length(seq) }
}
 

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

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

1. Вы можете избежать next с BEGIN { OFS="t" } split ($0,a,">")==2 { if (lgth) { print name, lgth } name = a[2] lgth = 0 } {lgth = length(a[1]) } END { if (lgth) { print name, lgth } }

2. Мне это кажется менее ясным и менее эффективным, чем использование next .

3. Правда, дальше все более понятно. Другое решение заключается в awk 'BEGIN { RS=">"; FS="n"; OFS="" } { name=$1; $1=""; lgth=length($0); if (lgth) { printf("%st%sn", name, lgth) } }' file.fa