Мой вопрос: у меня есть 200 файлов в формате фата. Такие как:

 /User/Bin/bin.0.fa
 /User/Bin/bin.1.fa
 ...
 /User/Bin/bin.200.fa

Каждый файл .fa содержит идентификатор имени контига и нуклеотидные символы, разделенные следующим образом:

In /User/Bin/bin.0.fa

>c_000000000001
CGACATTTTCCAACTTATTTTTTCCTGTAGTAAAAATTATTTACATACAAAAAAGGAGCTGTTCACTAATTATTTAGTGC
>c_000000000002 
TACAACTCCTTTTTACTATTCTTCTGAATTTGATTTTTCATCCATTTGTTTTTGAGCTTCTTGAACTAATTTATCAAGACTATTATCTTCTACAACTTCATTTTCTTGTCTATCTAATTCATCTGTTAATGTTAATTGCTGATCTTTATCTTCTACATCT CTACCTGAAATTTTAGCTATAGCTACAATCTTTTCTTCATCAGAAGTTCTCATTAATCTAACTCCCATTGTAGCTCTAC
>c_000000000003  
AGTTACAGATACATCTGATACATTAATTCTTATAGCAACACCACTTGTATTTATAAGCATTAATTCATCTTCAGATTTACATACTGTTGCACCAACAACTTTACCAGTCTTTTCACTGATTTTGTATGTTATTAAACCAACTCCACCTCTATTTTGTCTC
...

In /User/Bin/bin.1.fa

>c_000000000004
GGATCATCGCTTGTACATCCCAAACCAAAAAAGAATACTGCACTTACAATCAGTTGGATTTGAAACGCGATTTTCATTTTTGGTATATGTTTAAGATTAGCACTTTGTTTCATTGCTTTTGGCTATGAACGATGTTTACGGGGGTGTA
>c_000000000005 
GAAAGAAGCGTATTGGTCGGTATAAATACCGCTCAACTAAACGAGCACAAAGCTACCGAAAATTTGGATGAATTGGCTTTTCTGGCCCAAACGGCTGGAGC
>c_000000000006
CGGCACTTATTTGCCCCAGCCCATTTTGGGGGTAGAAATACCCAAGAGCAAGGGAAAGGTTCGCCTTCTGGGTGTGCCTACCGTGGTTGACCGTATGTTGCAAC
... 

...
In /User/Bin/bin.200.fa

>c_000000020120   
CTCTGCAACTGGATCCCGAAAAGATCCGCAAAGAAAGCGAACCCAAAGAAAAAGTCGATCTGGAGAGCACCGTCGCCCGCAGTCTGGCCACCCT
>c_000000020121
CATCAATCATCTCAAATACTACCGCAACGCAGATTATTCCCAGTGCAATAACAAAACCGACTCCCGCCTCTTTTGTCTGGCCGTA
>c_000000050122 
GGTACGCCTCCGGCAGAACAAGGCGGCAACGAACCTCAGAACGAGGGAAAGCTAACCCAGGCCGGGTACGCCTCCGGCAGAACAAGGCGGCAACGAACCTCAGAACGAGGGAAAGCTAACCCAGGCCG
...  

Я хотел бы скопировать каждый идентификатор имени контига (без «>») из определенных файлов .fa в один текстовый файл с разделителями TAB, где идентификаторы имени контига названы как исходный файл n + 1. Нравится:

In /User/Bin/Summary.txt

c_000000000001 Bin_1
c_000000000002 Bin_1
c_000000000003 Bin_1
...
c_000000000004 Bin_2
c_000000000005 Bin_2
c_000000000006 Bin_2
...
...
c_000000020120 Bin_201
c_000000020121 Bin_201
c_000000020122 Bin_201
0
Roland 10 Янв 2020 в 12:27
Находятся ли >c_000.. и CTGATAC в одной строке или это идентификатор в отдельной строке?
 – 
FelixJN
10 Янв 2020 в 12:33
Как в примере: >c_0000... CTGATAC Символы нуклеотидов последовательности вынесены в отдельную строку.
 – 
Roland
10 Янв 2020 в 12:37

6 ответов

Лучший ответ

Учитывая ваш опубликованный пример ввода/вывода и ответ, который вы приняли, все, что вам действительно нужно, это использовать GNU awk для ARGIND:

awk -F'>' -v OFS='\t' 'NF>1{print $2, "Bin_"ARGIND}' /Usr/Bin/bin*.fa > /User/Bin/Summary.txt

Или с любым awk:

awk -F'>' -v OFS='\t' 'FNR==1{++c} NF>1{print $2, "Bin_"c}' /Usr/Bin/bin*.fa > /User/Bin/Summary.txt
0
Ed Morton 10 Янв 2020 в 19:48
Это очень элегантный подход. Тем не менее, он основан на непрерывной нумерации имен входных файлов без пробелов, что на практике может быть не так (даже в примере, где OP заявил: «У меня есть 200 файлов», но затем дает bin.0.fa ... bin.200.fa в качестве имен файлов; хотя, конечно, это могла быть опечатка). Опять же, это только моя интерпретация утверждения ОП «названный как исходный файл n+1», что он хотел, чтобы идентификатор в выходном файле был привязан к входному имени файла, и ваш ответ может быть таким же правильным.
 – 
AdminBee
13 Янв 2020 в 11:06
1
Да, в самом деле! Эти команды также работают. Спасибо!
 – 
Roland
13 Янв 2020 в 11:14

Это должно быть возможно с GNU Awk:

awk 'match($0,/^>([^[:space:]]+)/,v) {match(FILENAME,/^.*bin\.([[:digit:]]+)\.fa$/,fid); printf("%s\tBin_%s\n", v[1], fid[1]+1);}' /User/Bin/bin.*.fa > summary.txt

Это будет:

  • сопоставьте все строки, начинающиеся с >, и извлеките шаблон за символом > (до первого пробела) в переменную массива awk v с помощью match()} функция в реализации GNU awk
  • сделайте то же самое, чтобы извлечь номер имени файла из текущего обрабатываемого имени файла (хранящегося во внутренней переменной FILENAME) в переменную массива fid
  • напечатать идентификатор contig (первый элемент v) и идентификатор файла (Bin_, за которым следует увеличенное значение fid[1])

Вывод вызова awk затем перенаправляется в файл summary.txt

Изменить

Как предложил Эд Мортон, эффективность можно повысить, извлекая идентификатор только при «изменении входного файла», что можно обнаружить с помощью FNR (номер строки в файле), равного 1 (при этом избегая возможных проблем). при наличии пробелов в нумерации имен файлов).

Кроме того, использования match() можно избежать, обрезав строку, содержащую «идентификатор контига», от начального > и любого завершающего пробела; это также делает код более переносимым между реализациями awk.

awk 'FNR==1{n=split(FILENAME,fid,"[/.]"); out=fid[n-1]+1} index($0,">")==1{gsub(/(^>|[ \t]*$)/,""); printf("%s\tBin_%s\n", $0, out);}' /User/Bin/bin.*.fa
0
AdminBee 13 Янв 2020 в 11:37
Спасибо! Сработало отлично. :)
 – 
Roland
10 Янв 2020 в 13:06
FWIW тот же подход, написанный более переносимо (он будет работать во всех awks) и эффективнее (он запускает match() только для FILENAME один раз для входного файла, а не один раз для входной строки), будет (непроверено) awk 'FNR==1{n=split(FILENAME,fid,"[/.]"); out=fid[n-1]+1} sub(/^>/,"") {printf "%s\tBin_%s\n", $0, out}' /User/Bin/bin.*.fa > summary.txt
 – 
Ed Morton
10 Янв 2020 в 19:37
1
Это хорошая идея сделать его более эффективным, и я включу его в ответ. Однако обратите внимание, что в примерах входных файлов OP, похоже, есть конечные пробелы и (возможно) горизонтальные табуляции в строках «contig ID», и, учитывая, что они, вероятно, напрямую скопированы и вставлены из исходных файлов, я подумал, что это больше «надежный» использовать (правда, дорогой) вызов match() для извлечения поля «contig ID».
 – 
AdminBee
13 Янв 2020 в 11:31
Не уверен, что OP имеет или заботится об этом, но если они это сделают, вы можете просто использовать $1 вместо $0 при печати идентификатора конфигурации, поскольку они не содержат пробелов в своем значении.
 – 
Ed Morton
13 Янв 2020 в 16:57
#!/usr/bin/env python

import os

files = os.listdir('/User/Bin')
for file in files:
    fi = open(file, 'r')
    n = file.split('.')[1]
    for line in fi:
        line = line.strip()
        if line.startswith('>'):
            bins = 'Bin_' + n
            print("%s\t%s" % (line[1:], bins))
    fi.close()

Пока вы работаете в Linux, у вас может быть установлен python. Это может помочь.

0
NixMan 10 Янв 2020 в 12:49
Да, это другое решение проблемы. Спасибо!
 – 
Roland
10 Янв 2020 в 13:08

Команда, которая может выполнить это, выглядит следующим образом

grep "^>" /User/Bin/*.fa | awk -F ":>|\\." '{sub(".*/", "", $1);printf "%s\t%s_%d\n",$4,$1,$2+1}'

Позвольте мне объяснить различные шаги

grep "^>" /User/Bin/*.fa

Приведенная выше команда ищет только строки с требуемым шаблоном и печатает имена файлов вместе со строкой. Мы могли бы использовать только awk для вывода совпадающих строк, но с помощью grep мы упрощаем выполнение требуемых операций с именем файла.

awk -F ":>|\\." 

Это делает символы «:>» или «.» в качестве разделителя полей, тем самым разделяя вывод grep на 4 части - 1. имя файла до первого "." 2. Имя индекса 3. Расширение 4. Фактический шаблон найден

{sub(".*/", "", $1)

Приведенный выше шаг удаляет базовое имя из имени файла в первой части.

printf "%s\t%s.%d\n",$4,$1,$2+1}

Выполняет окончательную печать, где вторая часть увеличивается на 1 , как вам это требуется, и вставляется вкладка для разделения вкладок.

0
amisax 10 Янв 2020 в 13:06
Хороший рабочий процесс. Спасибо за объяснения.
 – 
Roland
10 Янв 2020 в 13:10

Попробуйте также

awk 'FNR == 1 {split (FILENAME, T, "."); FN = T[1] "_" ++T[2]}; /^>/ {print substr ($1, 2), FN}' *.fa
c_000000000001 bin_1
c_000000000002 bin_1
c_000000000003 bin_1
c_000000000004 bin_2
c_000000000005 bin_2
c_000000000006 bin_2
c_000000020120 bin_201
c_000000020121 bin_201
c_000000050122 bin_201
0
RudiC 10 Янв 2020 в 18:17

Альтернативные комбинации awk grep

grep -Eo "c_[0-9]+" *.fa | awk -F'[.:]' '{print $4,"Bin_"$2+1}' | column -t

Или

grep -Eo "c_[0-9]+" *.fa | awk -F'[.:]' 'BEGIN{OFS="\t"}{print $4,"Bin_"$2+1}'

Или если это не всегда "bin..."

grep -Eo "c_[0-9]+" *.fa | awk -F'[.:]' 'BEGIN{OFS="\t"}{print $4,toupper(substr($1,1,1))substr($1,2)"_"$2+1}'

Обратите внимание, что это использование grep не удастся, если есть только 1 входной файл, и в этом случае вам нужно использовать параметр -H

0
bu5hman 10 Янв 2020 в 19:44
Спасибо за улов
 – 
bu5hman
10 Янв 2020 в 16:50
Вам никогда не понадобится grep, если вы используете awk.
 – 
Ed Morton
10 Янв 2020 в 19:19
На самом деле это правда, что вы этого не делаете, но мне было лень набирать все решения @AdminBee, и ОП не повредит увидеть альтернативные идеи и использование инструментов. Возможно, но гольфи тоже.
 – 
bu5hman
10 Янв 2020 в 19:36
Достаточно справедливо для -E, но шаблон OP требует числа после c_, поэтому это либо [0-9]+, либо [0-9][0-9]*
 – 
bu5hman
10 Янв 2020 в 19:44