Объединение нескольких файлов с двумя общими столбцами и замена пустого на 0

#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 которыми я также не могу работать