Bash способ сравнения указанных c столбцов из двух разных файлов на основе списка индексов - PullRequest
2 голосов
/ 09 марта 2020

У меня есть два разделенных табуляцией файла из 1708 строк и разного количества столбцов. Моя цель состоит в том, чтобы сравнить значение, сохраненное для всех строк, но только некоторые специфические c столбцы . У меня есть два списка, содержащие номер столбца, который я хочу сравнить; Вот пример:

  • Файл A ➝ col_ind_A = [12,20,24,55]
  • Файл B ➝ col_ind_B = [14,28,35,79]

Здесь столбец 12 файла A следует сравнивать со столбцом 14 файла B, 20 файла A с 28 файлом B и так далее. Если файл A имеет значение 0, а файл B - нет, я хочу изменить файл C (копия файла A) в этой позиции, а затем сохранить значение файла B (которое не равно 0):

# FileA                     #FileB                     #FileC
col11 col12 col13           col13 col14 col15          col11 col12 col13
  A     C     G               A      C     G            A      C     G
  G     0     T               G      T     T            G      T     T 

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

Если это поможет, я покажу код R, который делает именно это (он слишком медленный):

for(i in 1:1708){ #rows
  for(j in 1:31946){ #cols 
    if( fileA[i, col_ind_A[j]] == '0'  && fileA[i, col_ind_A[j]] != fileB[ i, col_ind_B[j]]){ 
      fileC[i, col_ind_A[j]] <- fileB[i, col_ind_B[j]] # write value from fileB in file C 
    }
  }
}

Любая помощь будет великолепной. Спасибо !!

Ответы [ 3 ]

2 голосов
/ 09 марта 2020

A perl скрипт, который делает это:

#!/usr/bin/env perl
use strict;
use warnings;
use autodie;
use feature qw/say/;
use List::Util qw/pairs/;

# Adjust as needed.
my @columns = (12 => 14, 20 => 28, 24 => 35, 55 => 79);

my ($filea_name, $fileb_name) = @ARGV;

@columns = pairs map { $_ - 1 } @columns;
open my $filea, '<', $filea_name;
open my $fileb, '<', $fileb_name;
$, = " "; # Or "\t" or whatever to delimit output columns
while (my $linea = <$filea>) {
    my $lineb = <$fileb> or die "Files have different line counts\n";
    chomp $linea;
    chomp $lineb;
    my @acols = split ' ', $linea;
    my @bcols = split ' ', $lineb;
    for my $p (@columns) {
        if ($acols[$$p[0]] eq "0" && $bcols[$$p[1]] ne "0") {
            $acols[$$p[0]] = $bcols[$$p[1]];
        }
    }
    say @acols;
}

(принимает в качестве аргументов командной строки FileA и FileB)

0 голосов
/ 09 марта 2020

Поскольку вы запросили решение awk, вот оно простое:

awk -v col_ind_A='12 20 24 55' -v col_ind_B='14 28 35 79' '
BEGIN { OFS="\t"
        split(col_ind_A, ciA)
        split(col_ind_B, ciB)
        while (getline <"FileB" > 0 && split($0, B) && getline <"FileA" > 0)
        {
            for (i in ciA) if ($ciA[i] == 0) $ciA[i] = B[ciB[i]]
            print >"FileC"
        }
      }'

Но это не будет быстрее, чем код R. Шагом оптимизации для кода R, вероятно, будет устранение внутреннего l oop:

for (i in 1:nrow(FileA))
{
    j = which(FileA[i, col_ind_A] == 0)
    FileC[i, col_ind_A[j]] = FileB[i, col_ind_B[j]]
}
0 голосов
/ 09 марта 2020

Сначала соедините файлы построчно, затем просто проверьте условие, которое вы хотите проверить.

# recreate input
cat >file1 <<EOF
col11 col12 col13
A C G
G 0 T
EOF
cat >file2 <<EOF
col13 col14 col15
A C G
G T T
EOF

paste file1 file2 |
awk '{ if ($2 == 0 && $2 != $6) $2=$6; print $1, $2 ,$3}'

вывод :

col11 col12 col13
A C G
G T T

Я думаю, из for(i in 1:1708){ #rows возможно, вы хотите перебрать все столбцы, если в обоих файлах одинаковое количество столбцов:

paste file1 file2 |
awk '{ 
   for (i=1;i<=NF/2;++i) if ($i == 0 && $i != $(i*2)) $i = $(i*2);
   for (i=1;i<=NF/2;++i) printf "%s%s", $i, i==NF/2?ORS:OFS; 
}'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...