입력 파일이 2개 있습니다.
파일 1(공백으로 구분)
ID POS a0 a1
SNP1 123 A C
SNP2 456 T C
SNP3 789 G A
파일 2(공백으로 구분)
0 1 0 1 0 0 0 1
1 1 0 0 1 0 0 1
0 1 1 1 0 0 0 1
원하는 출력
A C A C A A A C
C C T T C T T C
G A A A G G G A
파일 2의 각 줄은 파일 1의 한 줄을 나타냅니다. 비결은 a0과 a1의 해당 문자를 각각 0과 1로 바꾸는 것입니다. 이것은 단지 작은 예일 뿐이며 실제 파일은 600,000줄이 넘을 정도로 거대합니다.
저는 awk 또는 perl 솔루션을 찾고 있습니다.
답변1
읽을 수 없는 awk 진술로
$ awk 'NR>1{a[0]=$3;a[1]=$4;getline<f;for(i=1;i<=NF;i++)$i=a[$i];print}' f=file2 file1
A C A C A A A C
C C T T C T T C
G A A A G G G A
더 읽기 쉬운:
awk '
# skip the header in file1
NR == 1 {next}
{
# read the values from the file1 line
a[0] = $3
a[1] = $4
# replace the current record with the corresponding line from the map file
getline < map_file
# and now substitute the 0/1 with the values
for (i=1; i<=NF; i++)
$i = a[$i]
print
}
' map_file=file2 file1
답변2
이 작업을 정확하게 수행할 수 있지만 awk
변형으로 여기에 awk
+ paste
솔루션이 있습니다. bash
프로세스 대체를 지원하는 다른 쉘이 필요합니다 .
paste <(tail -n +2 file1) file2 |
awk '{a["0"]=$3; a["1"]=$4; for (i=5; i<=NF; ++i) printf "%s%s", a[$i], i==NF?"\n": " "}'
tail -n +2
헤더 행을 건너 뛰어야 합니다 file1
.
답변3
#!/usr/bin/env perl
# TODO docs on usage here, or write perldocs below, etc.
use strict;
use warnings;
die "Usage: $0 headerfile datafile\n" if @ARGV != 2;
my ($headerfile, $datafile) = @ARGV;
open(my $hfh, '<', $headerfile) or die "could not open '$headerfile': $!\n";
open(my $dfh, '<', $datafile) or die "could not open '$datafile': $!\n";
readline $hfh; # skip the header line
my $lineno = 1;
while (!eof($hfh) and !eof($dfh)) {
my $convert_to = join '', (split ' ', scalar readline $hfh)[-2,-1];
die sprintf "no conversion at $headerfile:%d\n", $lineno+1
if !defined $convert_to;
$_ = readline $dfh;
die "no data to convert at $datafile:$lineno\n" if !defined;
eval "tr/01/$convert_to/, 1" or die $@;
print;
$lineno++;
}