#bash #unix #awk
#bash #unix #awk
Вопрос:
Я очень признателен, если кто-нибудь может мне помочь объединить несколько файлов (до 8) с двумя общими столбцами ($ 1 $ 2). Я хочу получить все значения в 3 доллара и заменить пробел на 0. Вот примеры из 4 файлов
Файл1:
chr1 111001 234
chr2 22099 108
Файл2:
chr1 111001 42
chr1 430229 267
File3:
chr1 111001 92
chr5 663800 311
File4:
chr1 111001 129
chr2 22099 442
Желаемый результат
chr1 111001 234 42 92 129
chr1 430229 0 267 0 0
chr2 22099 108 0 0 442
chr5 663800 0 0 311 0
Я пытался
awk '{ a[$1 OFS $2 FS] = a[$1 OFS $2 FS] ( a[$1 OFS $2 FS] == "" ? "" : OFS) $3 }END{ for (i in a){print i,"0",a[i]} }' OFS="t" file1.txt file2.txt file3.txt file4.txt | sort -k1
вывод
chr1 111001 0 234 42 92 129
chr1 430229 0 267
chr2 22099 0 108 442
chr5 663800 0 311
Заранее большое спасибо
Комментарии:
1. Пожалуйста, добавьте свои усилия в виде кода в свой вопрос, так как это настоятельно рекомендуется для SO.
2. Жаль
join
, что работает только с 2 файлами одновременно.3. Может
chr1 111001
появляться более одного раза в одном файле?4. Нет, только один раз для каждого файла @anubhava
5. @Shawn, этот код может объединять несколько файлов
awk '{ a[$1 OFS $2 FS] = a[$1 OFS $2 FS] ( a[$1 OFS $2 FS] == "" ? "" : OFS) $3 }END{ for (i in a){print i,a[i]} }'
Ответ №1:
Еще один вариант, не могли бы вы попробовать следующее, написанное и протестированное с показанными образцами.
awk '
{
if(!a[FILENAME] ){
file[ count]=FILENAME
}
b[$1 OFS $2 OFS FILENAME]=$NF
c[$1 OFS $2]
if(!d[$1 OFS $2] ){
e[ count1]=$1 OFS $2
}
}
END{
for(i=1;i<=length(c);i ){
printf("%s ",e[i])
for(j=1;j<=count;j ){
printf("%s %s",(b[e[i] OFS file[j]]!=""?b[e[i] OFS file[j]]:0),j==count?ORS:OFS)
}
}
}
' file{1..4} | sort -k1
Вывод будет следующим.
chr1 111001 234 42 92 129
chr1 430229 0 267 0 0
chr2 22099 108 0 0 442
chr5 663800 0 0 311 0
Объяснение: добавление подробного объяснения выше.
awk ' ##Starting awk program from here.
{
if(!a[FILENAME] ){ ##Checking condition if FILENAME is present in a then do following.
file[ count]=FILENAME ##Creating file with index of count and value is current file name.
}
b[$1 OFS $2 OFS FILENAME]=$NF ##Creating array b with index of 1st 2nd and filename and which has value as last field.
c[$1 OFS $2] ##Creating array c with index of 1st and 2nd field and keep increasing its value with 1.
if(!d[$1 OFS $2] ){ ##Checking condition if 1st and 2nd field are NOT present in d then do following.
e[ count1]=$1 OFS $2 ##Creating e with index of count1 with increasing value of 1 and which has first and second fields here.
}
}
END{ ##Starting END block of this awk program from here.
for(i=1;i<=length(c);i ){ ##Starting for loop which runs from i=1 to till length of c here.
printf("%s ",e[i]) ##Printing value of array e with index i here.
for(j=1;j<=count;j ){ ##Starting for loop till value of count here.
printf("%s %s",(b[e[i] OFS file[j]]!=""?b[e[i] OFS file[j]]:0),j==count?ORS:OFS) ##Printing value of b with index of e[i] OFS file[j] if it present then print else print 0, print new line if j==count or print space.
}
}
}
' file{1..4} | sort -k1 ##Mentioning Input_files 1 to 4 here and sorting output with 1st field here.
РЕДАКТИРОВАТЬ: согласно замечаниям ВЕЛИКОГО ГУРУ регулярных выражений @ anubhava sir, добавляющего решение с ARGC
помощью и ARGV
с помощью GNU awk
.
awk '
{
b[$1 OFS $2 OFS FILENAME]=$NF
c[$1 OFS $2]
if(!d[$1 OFS $2] ){
e[ count1]=$1 OFS $2
}
}
END{
count=(ARGC-1)
for(i=1;i<=length(c);i ){
printf("%s ",e[i])
for(j=1;j<=(ARGC-1);j ){
printf("%s %s",(b[e[i] OFS ARGV[j]]!=""?b[e[i] OFS ARGV[j]]:0),j==count?ORS:OFS)
}
}
}
' file{1..4} | sort -k1
Ответ №2:
Вы можете использовать это gnu-awk
:
awk 'BEGIN {
for (k=1; k<ARGC; k)
s = s " " 0
}
{
key=$1 OFS $2
if (!(key in map))
map[key] = s
map[key] = gensub("^( ([0-9] ){" ARGIND-1 "})[0-9] ", "\1" $3, "1", map[key])
}
END {
PROCINFO["sorted_in"]="@ind_str_asc"
for (k in map)
print k map[k]
}' file{1..4} | column -t
chr1 111001 234 42 92 129
chr1 430229 0 267 0 0
chr2 22099 108 0 0 442
chr5 663800 0 0 311 0
Объяснение:
- Мы создаем строку со всеми нулями, по одному для каждого файла в аргументах
- С помощью
gensub
мы создаем регулярное выражение, используяARGIND
(индекс текущего аргумента) - Это регулярное выражение заменяет
0
в текущейARGIND
позиции на$3
END
блок просто распечатывает содержимое ассоциативного массива, хранящееся вmap
column -t
используется для табличного отображения данных
Вот эквивалентная команда, чтобы заставить ее работать в POSIX awk
(не gnu):
awk 'BEGIN {
for (k=1; k<ARGC; k)
s = s " " 0
}
FNR == 1 {
ARGIND
}
{
key=$1 OFS $2
if (!(key in map))
map[key] = s
split(map[key], a)
a[ARGIND] = $3
v = ""
for (k=1; k<ARGC; k)
v = v " " a[k]
map[key]=v
}
END {
for (k in map)
print k map[k]
}' file{1..4}
Комментарии:
1. Я действительно ценю, что это работает отлично, хотя я не очень знаком с аргументом ARGIND, и для меня это хорошее начало для работы над этим
2. Незнание чего-либо — это возможность узнать что-то новое, не так ли? кстати
ARGIND
, это просто переменная во 2-м POSIX-коде, там мы могли бы использовать `var` вместоARGIND
as.
Ответ №3:
Эти файлы выглядят так, как будто они получены из файлов vcf. Если это так, не изобретайте велосипед. Используйте любой из специализированных инструментов биоинформатики для работы с этими файлами. Например: bedtools
, bcftools
, Picard MergeVcfs
, и т. Д
Найдите больше, выполнив поиск merge bed files
или merge vcf files
. Большинство этих инструментов / пакетов биоинформатики можно установить с помощью conda
bioconda
канала from.
После того, как файлы bed / vcf объединены / объединены / пересекаются / и т. Д., Используйте общие утилиты * NIX и языки сценариев для извлечения и обработки файлов, если они не находятся ни в одном из распространенных форматов биоинформатики.
Комментарии:
1. Я действительно ценю ваше предложение. Но мне действительно нужен такой сценарий, потому что у меня есть другие файлы, с
bedtools
которыми я также не могу работать