добавить столбцы в файл с разделителями табуляции с помощью AWK - PullRequest
0 голосов
/ 16 мая 2018

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

File_1.txt

chr1    101845021   101845132   A   0
chr2    128205033   128205154   B   0
chr3    128205112   128205223   C   0
chr4    36259133    36259244    D   0
chr5    36259333    36259444    E   0
chr6    25497759    25497870    F   1
chr7    25497819    25497930    G   1
chr8    25497869    25497980    H   1

File_2.txt

chr1    101845021   101845132   A   6
chr2    128205033   128205154   B   7
chr3    128205112   128205223   C   7
chr4    36259133    36259244    D   7
chr5    36259333    36259444    E   10
chr6    25497759    25497870    F   11
chr7    25497819    25497930    G   11
chr8    25497869    25497980    H   12

File_3.txt

chr1    101845021   101845132   A   41
chr2    128205033   128205154   B   41
chr3    128205112   128205223   C   42
chr4    36259133    36259244    D   43
chr5    36259333    36259444    E   47
chr6    25497759    25497870    F   48
chr7    25497819    25497930    G   48
chr8    25497869    25497980    H   49

Ожидаемый выходной файл Final.txt

Part    Start   End Name    File1   File2   File3
chr1    101845021   101845132   A   0   6   41
chr2    128205033   128205154   B   0   7   41
chr3    128205112   128205223   C   0   7   42
chr4    36259133    36259244    D   0   7   43
chr5    36259333    36259444    E   0   10  47
chr6    25497759    25497870    F   1   11  48
chr7    25497819    25497930    G   1   11  48
chr8    25497869    25497980    H   1   12  49

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Предполагая, что оба 3 файла отсортированы, вы можете использовать команду join:

join -o "1.1,1.2,1.3,1.4,2.5,2.6,1.5" file3 <(join -o "1.1,1.2,1.3,1.4,1.5,2.5" file1 file2)

-o позволяет форматировать результат вывода с выбором определенных полей из обоих файлов.1.x и 2.x относится к данному файлу.Например, 1.1 относится к первому полю первого файла.

Поскольку join принимает только 2 файла, оператор bash <(...) используется для создания временного файла.


Другое решение, использующее paste и awk (при условии, что файлы отсортированы):

paste file* | awk '{print $1,$2,$3,$4,$5,$10,$15}'
0 голосов
/ 16 мая 2018

Файлы в том же порядке

Если можно с уверенностью предположить, что строки в каждом файле имеют одинаковый порядок, то вы можете выполнить работу довольно кратко с помощью:

awk '
FILENAME != oname { FN++; oname = FILENAME }
    { p[FNR] = $1; s[FNR] = $2; e[FNR] = $3; n[FNR] = $4; f[FN,FNR] = $5; N = FNR }
END {
    printf("%-8s %-12s %-12s %-4s %-5s %-5s %-5s\n",
           "Part", "Start", "End", "Name", "File1", "File2", "File3");
    for (i = 1; i <= N; i++)
    {
        printf("%-8s %-12d %-12d %-4s %-5d %-5d %-5d\n",
               p[i], s[i], e[i], n[i], f[1,i], f[2,i], f[3,i]);
    }
}' file_1.txt file_2.txt file_3.txt

Первая строка выделяется при запуске нового файла и увеличивает значение переменной FN (таким образом, строки из файла 1 могут быть помечены FN == 1 и т. Д.). Он записывает имя файла в oname, чтобы он мог заметить изменения.

Вторая строка работает с каждой строкой данных, сохраняя первые четыре поля в массивах p, s, e, n (индексируется по номеру записи в текущем файле) и записывает пятое столбец в f (индексируется FN и номером записи). Текущий номер записи в текущем файле записывается в N.

Блок END печатает заголовок, затем для каждой строки в массиве (индексированный от 1 до N) печатает различные поля.

Вывод (неудивительно):

Part     Start        End          Name File1 File2 File3
chr1     101845021    101845132    A    0     6     41   
chr2     128205033    128205154    B    0     7     41   
chr3     128205112    128205223    C    0     7     42   
chr4     36259133     36259244     D    0     7     43   
chr5     36259333     36259444     E    0     10    47   
chr6     25497759     25497870     F    1     11    48   
chr7     25497819     25497930     G    1     11    48   
chr8     25497869     25497980     H    1     12    49   

Файлы в разных порядках

Если вы не можете полагаться на записи в одном и том же порядке в каждом файле, вам придется работать усерднее. Предполагая, что записи в первом файле находятся в требуемом порядке, следующий скрипт организует печать записей в порядке:

awk '
FILENAME != oname { FN++; oname = FILENAME }
    { key = $1 SUBSEP $2 SUBSEP $3 SUBSEP $4
      if (FN == 1)
      {   p[key] = $1; s[key] = $2; e[key] = $3; n[key] = $4; f[FN,key] = $5; k[FNR] = key; N = FNR }
      else
      {   if (key in p)
            f[FN,key] = $5
          else
              printf "Unmatched key (%s) in %s\n", key, FILENAME
      }
    }
END {
    printf("%-8s %-12s %-12s %-4s %-5s %-5s %-5s\n",
           "Part", "Start", "End", "Name", "File1", "File2", "File3")
    for (i = 1; i <= N; i++)
    {
        key = k[i]
        printf("%-8s %-12d %-12d %-4s %-5d %-5d %-5d\n",
               p[key], s[key], e[key], n[key], f[1,key], f[2,key], f[3,key])
    }
}' "$@"

Это тесно связано с предыдущим сценарием; FN обработка идентична. Переменная SUBSEP используется для разделения индексов в многоиндексном массиве. Переменная key содержит то же значение, которое генерироваться путем индексации массива z[$1,$2,$3,$4].

При работе с первым файлом (FN == 1) создаются значения в массивах p, s, e, n, индексированные по key. Пятый столбец записывается в f аналогично. Порядок, в котором ключи появляются в файле, записывается в массиве k, индексируемом номером записи (файла).

Если вы работаете со вторым или третьим файлом, проверьте, известен ли ключ, и сообщите, если он не известен. Предполагая, что это известно, добавьте пятый столбец в f снова.

Печать аналогична, за исключением того, что она последовательно собирает ключи из k, а затем печатает соответствующие значения.

С учетом этих файлов:

  • file_4.txt

    chr8    25497869    25497980    H   1
    chr7    25497819    25497930    G   1
    chr6    25497759    25497870    F   1
    chr5    36259333    36259444    E   0
    chr4    36259133    36259244    D   0
    chr3    128205112   128205223   C   0
    chr2    128205033   128205154   B   0
    chr1    101845021   101845132   A   0
    
  • file_5.txt

    chr2    128205033   128205154   B   7
    chr8    25497869    25497980    H   12
    chr3    128205112   128205223   C   7
    chr1    101845021   101845132   A   6
    chr6    25497759    25497870    F   11
    chr4    36259133    36259244    D   7
    chr7    25497819    25497930    G   11
    chr5    36259333    36259444    E   10
    
  • file_6.txt

    chr5    36259333    36259444    E   47
    chr4    36259133    36259244    D   43
    chr6    25497759    25497870    F   48
    chr8    25497869    25497980    H   49
    chr2    128205033   128205154   B   41
    chr3    128205112   128205223   C   42
    chr7    25497819    25497930    G   48
    chr1    101845021   101845132   A   41
    

Скрипт выдает результат:

Part     Start        End          Name File1 File2 File3
chr8     25497869     25497980     H    1     12    49   
chr7     25497819     25497930     G    1     11    48   
chr6     25497759     25497870     F    1     11    48   
chr5     36259333     36259444     E    0     10    47   
chr4     36259133     36259244     D    0     7     43   
chr3     128205112    128205223    C    0     7     42   
chr2     128205033    128205154    B    0     7     41   
chr1     101845021    101845132    A    0     6     41   

Есть много обстоятельств, что эти сценарии не очень тщательно приспосабливаются. Например, если файлы имеют разную длину; если есть повторяющиеся ключи; если в одном или двух файлах найдены ключи, а в других - нет; если данные пятого столбца не являются числовыми; если второй и третий столбцы не являются числовыми; если есть только два файла или более трех файлов в списке. Проблема «не числовой» на самом деле легко решается; просто используйте %s вместо %d. Но сценарии хрупкие. Они работают в показанных экосистемах, но не очень в целом. Необходимые исправления не являются невероятно сложными; тем не менее, их надо кодировать.

Может быть больше или меньше 3 файлов

Расширение предыдущего сценария для обработки произвольного числа файлов и вывода разделенных табуляцией данных вместо форматированных (читаемых) данных не очень сложно.

awk '
FILENAME != oname { FN++; file[FN] = oname = FILENAME }
    { key = $1 SUBSEP $2 SUBSEP $3 SUBSEP $4
      if (FN == 1)
      {   p[key] = $1; s[key] = $2; e[key] = $3; n[key] = $4; f[FN,key] = $5; k[FNR] = key; N = FNR }
      else
      {   if (key in p)
              f[FN,key] = $5
          else
          {
              printf "Unmatched key (%s) in %s\n", key, FILENAME
              exit 1
          }

      }
    }
END {
    printf("%s\t%s\t%s\t%s", "Part", "Start", "End", "Name")
    for (i = 1; i <= FN; i++) printf("\t%s", file[i]);
    print ""
    for (i = 1; i <= N; i++)
    {
        key = k[i]
        printf("%s\t%s\t%s\t%s", p[key], s[key], e[key], n[key])
        for (j = 1; j <= FN; j++)
            printf("\t%s", f[j,key])
        print ""
    }
}' "$@"

Ключевым моментом является то, что printf не выводит символ новой строки, если вы не попросите его сделать это, но print выводит символ новой строки. Код ведет запись фактических имен файлов для использования при распечатке столбцов. Он перебирает массив данных файла, предполагая, что в каждом файле одинаковое количество строк.

Учитывая 6 файлов в качестве входных данных - три исходных файла, копию первого файла в обратном порядке и переставленные копии второго и третьего файлов, выходные данные имеют 6 столбцов дополнительных данных, с идентифицированными столбцами:

Part    Start   End     Name    file_1.txt      file_2.txt      file_3.txt      file_4.txt      file_5.txt      file_6.txt
chr1    101845021       101845132       A       0       6       41      0       6       41
chr2    128205033       128205154       B       0       7       41      0       7       41
chr3    128205112       128205223       C       0       7       42      0       7       42
chr4    36259133        36259244        D       0       7       43      0       7       43
chr5    36259333        36259444        E       0       10      47      0       10      47
chr6    25497759        25497870        F       1       11      48      1       11      48
chr7    25497819        25497930        G       1       11      48      1       11      48
chr8    25497869        25497980        H       1       12      49      1       12      49
...